Initial release
Change-Id: If51199c8e3e9655001b2ba0f4ef7d762029d5764
This commit is contained in:
parent
224075f69b
commit
21aa57a51b
@ -1,4 +1,4 @@
|
||||
FROM docker.io/library/node:20.11.0-bookworm
|
||||
FROM docker.io/library/node:21.4.0-bookworm
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
RUN corepack enable pnpm && pnpm install && npx vue-tsc
|
||||
|
@ -2,6 +2,9 @@
|
||||
"name": "enigma-nebulous",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"engines": {
|
||||
"node": "v21.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"serve": "vite",
|
||||
"build": "vue-tsc && vite build",
|
||||
|
@ -3,6 +3,7 @@
|
||||
<div class="py-5 md:py-0">
|
||||
<MobileMenu v-if="!isMenuHidden" />
|
||||
<Header v-if="!isMenuHidden" layout="side-menu" />
|
||||
|
||||
<div class="flex overflow-hidden">
|
||||
<SideMenu v-if="!isMenuHidden" />
|
||||
<!-- BEGIN: Content -->
|
||||
@ -30,6 +31,7 @@ import SideMenu from "@/components/SideMenu"
|
||||
import MobileMenu from "@/components/MobileMenu"
|
||||
import Modal from "@/components/Modal"
|
||||
import Snackbar from "@/components/Snackbar"
|
||||
import Footer from "@/base-components/Headless/Menu/Footer.vue";
|
||||
|
||||
const route: RouteLocationNormalizedLoaded = useRoute()
|
||||
|
||||
@ -44,4 +46,6 @@ watch(
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
|
||||
</script>
|
||||
|
@ -19,7 +19,7 @@ import ICodeEditor = editor.ICodeEditor
|
||||
|
||||
interface IEditorProps {
|
||||
modelValue: string
|
||||
language: "yaml" | "math"
|
||||
language: "yaml" | "math" | "json" | "casbin"
|
||||
theme?: string
|
||||
fontSize?: number
|
||||
destroyDelay?: number
|
||||
|
@ -8,6 +8,7 @@ export const keywords = [
|
||||
"POW",
|
||||
"LOG",
|
||||
"LOG10",
|
||||
"MEAN",
|
||||
"LN2",
|
||||
"LN10",
|
||||
"LOG10E",
|
||||
|
@ -17,7 +17,11 @@
|
||||
<div class="hidden md:flex w-full max-w-4xl mr-8">
|
||||
<slot name="title" />
|
||||
</div>
|
||||
<Button variant="primary" class="ml-auto" @click="onSaveClick">Save</Button>
|
||||
<div class="ml-auto" v-if="!saveEnabled">
|
||||
</div>
|
||||
<Button variant="primary" class="ml-auto" @click="onSaveClick"
|
||||
v-if="saveEnabled"
|
||||
>Save</Button>
|
||||
<Button
|
||||
v-if="currentStage.next"
|
||||
variant="primary"
|
||||
@ -104,7 +108,9 @@
|
||||
<!-- BEGIN: CANCEL BUTTONS LINE -->
|
||||
<div class="pb-2 mt-auto">
|
||||
<div class="flex justify-between items-end mt-5">
|
||||
<Button variant="outline-danger" class="ml-auto" @click="onExitClickHandler">Cancel</Button>
|
||||
<Button variant="outline-danger" class="ml-auto" @click="onExitClickHandler">
|
||||
{{saveEnabled ? 'Cancel' : 'Close'}}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END: CANCEL BUTTONS LINE -->
|
||||
@ -126,6 +132,7 @@ const router = useRouter()
|
||||
|
||||
interface MultiStepsProviderProps {
|
||||
stages: Record<string, Stage>
|
||||
saveEnabled: boolean
|
||||
entrypointComponent: string
|
||||
returnRouteName: string
|
||||
responseErrorMessages: Array<string>
|
||||
@ -165,6 +172,7 @@ watch(currentStageName, () => {
|
||||
|
||||
const clientErrorMessages = ref<Array<string>>([])
|
||||
|
||||
const saveEnabled = computed(() => props.saveEnabled)
|
||||
const currentStage = computed<Stage>(() => props.stages[currentStageName.value])
|
||||
const visibleStagesHeads = computed(() => Object.values(props.stages).filter(({ isInvisible }) => !isInvisible))
|
||||
const lastStageNumber = computed(() => Math.max(...Object.values(props.stages).map(({ stage }) => stage)))
|
||||
|
@ -66,6 +66,7 @@
|
||||
}"
|
||||
>
|
||||
<option value="maximize">Maximize Utility</option>
|
||||
<option value="minimize">Minimize Utility</option>
|
||||
<option value="constant">Constant</option>
|
||||
</FormSelect>
|
||||
<Lucide icon="Trash2" class="w-10 text-danger" @click="removeFunction(index)" />
|
||||
|
@ -8,12 +8,14 @@
|
||||
<MetricsTemplateDesktop
|
||||
:metrics="localMetrics"
|
||||
:componentList="props.componentList"
|
||||
:templateNames="props.templateNames"
|
||||
@levelChangeHandler="onLevelChangeHandler"
|
||||
class="hidden md:table"
|
||||
/>
|
||||
<MetricsTemplateMobile
|
||||
:metrics="localMetrics"
|
||||
:componentList="props.componentList"
|
||||
:templateName="props.templateNames"
|
||||
@levelChangeHandler="onLevelChangeHandler"
|
||||
class="md:hidden"
|
||||
/>
|
||||
@ -21,37 +23,37 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue"
|
||||
import {computed, ref} from "vue"
|
||||
import Lucide from "@/base-components/Lucide/Lucide.vue"
|
||||
import { IMetricComposite, IMetricRaw } from "@/interfaces/metrics.interface.ts"
|
||||
import MetricsTemplateDesktop from "@/components/Application/Metrics/MetricsTemplateDesktop.vue"
|
||||
import MetricsTemplateMobile from "@/components/Application/Metrics/MetricsTemplateMobile.vue"
|
||||
import {ITemplate} from "@/interfaces/template.interface.ts";
|
||||
import _ from "lodash";
|
||||
|
||||
interface MetricsProps {
|
||||
componentList: Array<{ label: string; value: string }>
|
||||
metrics: Array<IMetricRaw | IMetricComposite>
|
||||
templateNames: Array<string>
|
||||
}
|
||||
const props = defineProps<MetricsProps>()
|
||||
|
||||
const localMetrics = computed(() => props.metrics)
|
||||
|
||||
|
||||
const addMetric = () => {
|
||||
localMetrics.value.push({
|
||||
type: "raw",
|
||||
name: "",
|
||||
sensor: "",
|
||||
config: [],
|
||||
level:"global",
|
||||
components:[],
|
||||
isWindowOutputRaw: true,
|
||||
isWindowInputRaw: true,
|
||||
outputRaw: {
|
||||
type: "all",
|
||||
interval: 0,
|
||||
unit: "ms"
|
||||
},
|
||||
inputRaw: {
|
||||
type: "all",
|
||||
interval: 0,
|
||||
unit: "ms"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { computed, reactive, ref } from "vue"
|
||||
import { computed } from 'vue';
|
||||
import { IMetricComposite, IMetricRaw } from "@/interfaces/metrics.interface.ts"
|
||||
import _ from "lodash"
|
||||
import { required } from "@vuelidate/validators"
|
||||
@ -8,30 +8,25 @@ export function useMetrics(metrics: Array<IMetricComposite | IMetricRaw>) {
|
||||
const localMetrics = computed(() => metrics)
|
||||
const previouslyEditedMetricsData: Record<number, IMetricComposite | IMetricRaw> = {}
|
||||
|
||||
const metricsRawRules = {
|
||||
|
||||
const metricsRawRules = computed(() => ({
|
||||
name: { required }
|
||||
}
|
||||
}));
|
||||
|
||||
const metricsCompositeRules = {
|
||||
const metricsCompositeRules = computed(() => ({
|
||||
name: { required },
|
||||
formula: { required }
|
||||
}
|
||||
}));
|
||||
|
||||
const metricRules = reactive({
|
||||
name: { required },
|
||||
formula: { required }
|
||||
})
|
||||
|
||||
// Could not make it work better. Only "reactive" can keep validation rules dynamic
|
||||
const getValidationRules = (type: string) => {
|
||||
if (type === "composite") {
|
||||
Object.assign(metricRules, { formula: { required } })
|
||||
} else {
|
||||
// @ts-ignore
|
||||
delete metricRules.formula
|
||||
const getValidationRules = (type:string) => {
|
||||
if (type === 'composite') {
|
||||
return metricsCompositeRules.value;
|
||||
} else if (type === 'raw') {
|
||||
return metricsRawRules.value;
|
||||
}
|
||||
return metricRules
|
||||
}
|
||||
return { name: { required } };
|
||||
};
|
||||
|
||||
|
||||
const metricTypeChangeHandler = (index: number, event: HTMLElementEvent<HTMLSelectElement>) => {
|
||||
const { target } = event
|
||||
@ -45,11 +40,13 @@ export function useMetrics(metrics: Array<IMetricComposite | IMetricRaw>) {
|
||||
type: "composite",
|
||||
name: "",
|
||||
formula: "",
|
||||
template:"",
|
||||
level:"global",
|
||||
components:[],
|
||||
isWindowInput: true,
|
||||
isWindowOutput: true,
|
||||
level: "global",
|
||||
input: {
|
||||
type: "all",
|
||||
type: "batch",
|
||||
interval: 0,
|
||||
unit: "ms"
|
||||
},
|
||||
@ -63,17 +60,13 @@ export function useMetrics(metrics: Array<IMetricComposite | IMetricRaw>) {
|
||||
localMetrics.value[index] = {
|
||||
type: "raw",
|
||||
isWindowOutputRaw: true,
|
||||
isWindowInputRaw: true,
|
||||
level:"global",
|
||||
components:[],
|
||||
outputRaw: {
|
||||
type: "all",
|
||||
interval: 0,
|
||||
unit: "ms"
|
||||
},
|
||||
inputRaw: {
|
||||
type: "all",
|
||||
interval: 0,
|
||||
unit: "ms"
|
||||
},
|
||||
name: "",
|
||||
sensor: "",
|
||||
config: []
|
||||
|
@ -95,6 +95,19 @@
|
||||
<option value="components">Components</option>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="flex flex-col flex-grow">
|
||||
<Label>Template</Label>
|
||||
<Select
|
||||
v-model="metric.template"
|
||||
:class="{
|
||||
'input--invalid': v.template?.$error || hasBackendError(`metrics[${index}].template`)
|
||||
}"
|
||||
>
|
||||
<option v-for="(template, templateOptionIndex) in templateNames" :key="templateOptionIndex">
|
||||
{{ template }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div v-if="metric.level === 'components' && metric.components" class="flex flex-col">
|
||||
<Label>Components</Label>
|
||||
@ -118,6 +131,7 @@
|
||||
<template v-else> <option>No keys available</option> </template>
|
||||
</TomSelect>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
@ -139,7 +153,7 @@
|
||||
id="windowInput"
|
||||
v-model="metric.isWindowInput"
|
||||
/>
|
||||
<FormCheck.Label class="font-bold" for="windowInput">Window Input</FormCheck.Label>
|
||||
<FormCheck.Label class="font-bold" for="windowInput">Input Intervals</FormCheck.Label>
|
||||
</FormCheck>
|
||||
<div v-if="metric.isWindowInput && metric.input" class="flex space-x-3">
|
||||
<div class="flex flex-col flex-grow">
|
||||
@ -149,7 +163,7 @@
|
||||
v-model="metric.input.type"
|
||||
:class="{ 'input--invalid': hasBackendError(`metrics[${index}].input.type`) }"
|
||||
>
|
||||
<option v-for="(option, behaviorIndex) in BEHAVIOR_OPTIONS" :key="behaviorIndex">
|
||||
<option v-for="(option, behaviorIndex) in BEHAVIOR_OPTIONS_INPUT" :key="behaviorIndex">
|
||||
{{ option }}
|
||||
</option>
|
||||
</Select>
|
||||
@ -184,7 +198,7 @@
|
||||
id="windowOutput"
|
||||
v-model="metric.isWindowOutput"
|
||||
/>
|
||||
<FormCheck.Label class="font-bold" for="windowOutput">Window Output</FormCheck.Label>
|
||||
<FormCheck.Label class="font-bold" for="windowOutput">Output Intervals</FormCheck.Label>
|
||||
</FormCheck>
|
||||
<div v-if="metric.isWindowOutput && metric.output" class="flex flex-col">
|
||||
<div class="flex space-x-3">
|
||||
@ -195,7 +209,7 @@
|
||||
v-model="metric.output.type"
|
||||
:class="{ 'input--invalid': hasBackendError(`metrics[${index}].output.type`) }"
|
||||
>
|
||||
<option v-for="(option, behaviorIndex) in BEHAVIOR_OPTIONS" :key="behaviorIndex">
|
||||
<option v-for="(option, behaviorIndex) in BEHAVIOR_OPTIONS_OUTPUT" :key="behaviorIndex">
|
||||
{{ option }}
|
||||
</option>
|
||||
</Select>
|
||||
@ -276,48 +290,41 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<FormCheck class="mb-2">
|
||||
<FormCheck.Input
|
||||
class="border"
|
||||
type="checkbox"
|
||||
id="windowInputRaw"
|
||||
v-model="metric.isWindowInputRaw"
|
||||
/>
|
||||
<FormCheck.Label class="font-bold" for="windowInputRaw">Window Input</FormCheck.Label>
|
||||
</FormCheck>
|
||||
<div v-if="metric.isWindowInputRaw && metric.inputRaw" class="flex space-x-3">
|
||||
<div class="flex flex-col flex-grow">
|
||||
<Label>Type</Label>
|
||||
<Select
|
||||
class="w-auto capitalize"
|
||||
v-model="metric.inputRaw.type"
|
||||
:class="{ 'input--invalid': hasBackendError(`metrics[${index}].input.type`) }"
|
||||
>
|
||||
<option v-for="(option, behaviorIndex) in BEHAVIOR_OPTIONS" :key="behaviorIndex">
|
||||
{{ option }}
|
||||
<div class="flex flex-col">
|
||||
<Label>Level</Label>
|
||||
<Select
|
||||
v-model="metric.level"
|
||||
@change="emit('levelChangeHandler', index, $event as HTMLElementEvent<HTMLSelectElement>)"
|
||||
:class="{
|
||||
'input--invalid': v.level?.$error || hasBackendError(`metrics[${index}].level`)
|
||||
}"
|
||||
>
|
||||
<option value="global">Global</option>
|
||||
<option value="components">Components</option>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div v-if="metric.level === 'components' && metric.components" class="flex flex-col">
|
||||
<Label>Components</Label>
|
||||
<TomSelect
|
||||
v-model="metric.components"
|
||||
class="w-full"
|
||||
multiple
|
||||
:class="{
|
||||
'input--invalid': v.components?.$error || hasBackendError(`metrics[${index}].components`)
|
||||
}"
|
||||
>
|
||||
<template v-if="componentList.length">
|
||||
<option
|
||||
v-for="(option, componentOptionIndex) in componentList"
|
||||
:key="componentOptionIndex"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="flex flex-col flex-grow">
|
||||
<Label>Interval</Label>
|
||||
<Input
|
||||
type="number"
|
||||
v-model="metric.inputRaw.interval"
|
||||
:class="{ 'input--invalid': hasBackendError(`metrics[${index}].input.interval`) }"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col flex-grow">
|
||||
<Label>Unit</Label>
|
||||
<Select
|
||||
class="w-auto capitalize"
|
||||
v-model="metric.inputRaw.unit"
|
||||
:class="{ 'input--invalid': hasBackendError(`metrics[${index}].input.unit`) }"
|
||||
>
|
||||
<option v-for="(option, timeUnitIndex) in UNIT_TIME_OPTIONS" :key="timeUnitIndex">
|
||||
{{ option }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else> <option>No keys available</option> </template>
|
||||
</TomSelect>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -329,7 +336,7 @@
|
||||
id="windowOutputRaw"
|
||||
v-model="metric.isWindowOutputRaw"
|
||||
/>
|
||||
<FormCheck.Label class="font-bold" for="windowOutputRaw">Window Output</FormCheck.Label>
|
||||
<FormCheck.Label class="font-bold" for="windowOutputRaw">Output Intervals</FormCheck.Label>
|
||||
</FormCheck>
|
||||
<div v-if="metric.isWindowOutputRaw && metric.outputRaw" class="flex flex-col">
|
||||
<div class="flex space-x-3">
|
||||
@ -388,7 +395,7 @@ import { Disclosure } from "@/base-components/Headless"
|
||||
import { ValidateEach } from "@vuelidate/components"
|
||||
import { FormCheck } from "@/base-components/Form"
|
||||
import { IMetricComposite, IMetricRaw } from "@/interfaces/metrics.interface.ts"
|
||||
import { UNIT_TIME_OPTIONS, BEHAVIOR_OPTIONS } from "@/constants"
|
||||
import { UNIT_TIME_OPTIONS, BEHAVIOR_OPTIONS, BEHAVIOR_OPTIONS_INPUT ,BEHAVIOR_OPTIONS_OUTPUT} from "@/constants"
|
||||
import Select from "@/base-components/Form/FormSelect.vue"
|
||||
import Label from "@/base-components/Form/FormLabel.vue"
|
||||
import Input from "@/base-components/Form/FormInput.vue"
|
||||
@ -405,9 +412,11 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
const props = defineProps<{
|
||||
metrics: Array<IMetricComposite | IMetricRaw>
|
||||
componentList: Array<{ label: string; value: string }>
|
||||
componentList: Array<{ label: string; value: string }>,
|
||||
templateNames: Array<string>
|
||||
}>()
|
||||
|
||||
|
||||
const {
|
||||
localMetrics,
|
||||
getValidationRules,
|
||||
|
@ -254,51 +254,7 @@
|
||||
<Lucide icon="Trash2" class="ml-3 text-danger" @click="removeSetting(metric, settingIndex)" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<FormCheck class="mb-2">
|
||||
<FormCheck.Input
|
||||
class="border"
|
||||
type="checkbox"
|
||||
id="windowInputRaw"
|
||||
v-model="metric.isWindowInputRaw"
|
||||
/>
|
||||
<FormCheck.Label class="font-bold" for="windowInputRaw">Window Input</FormCheck.Label>
|
||||
</FormCheck>
|
||||
<div v-if="metric.isWindowInputRaw && metric.inputRaw" class="flex space-x-3">
|
||||
<div class="flex flex-col flex-grow">
|
||||
<Label>Type</Label>
|
||||
<Select
|
||||
class="w-auto capitalize"
|
||||
v-model="metric.inputRaw.type"
|
||||
:class="{ 'input--invalid': hasBackendError(`metrics[${index}].input.type`) }"
|
||||
>
|
||||
<option v-for="(option, behaviorIndex) in BEHAVIOR_OPTIONS" :key="behaviorIndex">
|
||||
{{ option }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="flex flex-col flex-grow">
|
||||
<Label>Interval</Label>
|
||||
<Input
|
||||
type="number"
|
||||
v-model="metric.inputRaw.interval"
|
||||
:class="{ 'input--invalid': hasBackendError(`metrics[${index}].input.interval`) }"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col flex-grow">
|
||||
<Label>Unit</Label>
|
||||
<Select
|
||||
class="w-auto capitalize"
|
||||
v-model="metric.inputRaw.unit"
|
||||
:class="{ 'input--invalid': hasBackendError(`metrics[${index}].input.unit`) }"
|
||||
>
|
||||
<option v-for="(option, timeUnitIndex) in UNIT_TIME_OPTIONS" :key="timeUnitIndex">
|
||||
{{ option }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex flex-col">
|
||||
<FormCheck class="mb-2">
|
||||
|
@ -3,7 +3,8 @@
|
||||
<Templates :templates="templates" />
|
||||
<Parameters :parameters="parameters" :templateNames="templateNames" />
|
||||
|
||||
<Metrics :metrics="metrics" :componentList="props.payload.componentList" />
|
||||
<Metrics :metrics="metrics" :componentList="props.payload.componentList"
|
||||
:templateNames="templateNames"/>
|
||||
|
||||
<div class="flex flex-col space-y-5">
|
||||
<p class="text-2xl">SLO</p>
|
||||
@ -55,6 +56,7 @@ const props = withDefaults(defineProps<MetricsProps>(), {
|
||||
{
|
||||
type: "composite",
|
||||
name: "",
|
||||
template: "",
|
||||
formula: "",
|
||||
isWindowInput: true,
|
||||
isWindowOutput: true,
|
||||
|
@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<div class="flex flex-col box p-5 flex-grow space-y-5">
|
||||
<div class="flex items-center space-x-4">
|
||||
<p class="text-2xl">Resources</p>
|
||||
<Lucide icon="PlusCircle" @click="redirectToResources" />
|
||||
</div>
|
||||
|
||||
<div class="flex-grow overflow-y-auto h-0">
|
||||
<div
|
||||
v-for="(resource, index) in resources"
|
||||
@ -58,6 +53,7 @@ const props = withDefaults(defineProps<ResourcesProps>(), {
|
||||
})
|
||||
|
||||
const resources = computed<Array<IAppResource>>(() =>
|
||||
|
||||
resourceStore.resources.results.map((resource) => {
|
||||
// prettier-ignore
|
||||
const isEnabled = props.payload.appResources
|
||||
@ -65,7 +61,7 @@ const resources = computed<Array<IAppResource>>(() =>
|
||||
return {
|
||||
uuid: resource.uuid,
|
||||
title: resource.title,
|
||||
platform: resource.platform,
|
||||
platform: resource.platform.title,
|
||||
enabled: isEnabled
|
||||
}
|
||||
})
|
||||
|
@ -7,6 +7,7 @@
|
||||
returnRouteName="applications"
|
||||
:responseErrorMessages="responseErrorMessages"
|
||||
:v$="v$"
|
||||
:save-enabled="applicationData.status =='draft'"
|
||||
@saveClick="saveClickHandler"
|
||||
>
|
||||
<template #title>
|
||||
@ -72,6 +73,7 @@ const props = withDefaults(defineProps<ApplicationProps>(), {
|
||||
isWindowInput: true,
|
||||
isWindowOutput: true,
|
||||
level: "global",
|
||||
template: "",
|
||||
components: [],
|
||||
input: {
|
||||
type: "all",
|
||||
|
40
gui/src/components/DebugInfo/DebugInfo.vue
Normal file
40
gui/src/components/DebugInfo/DebugInfo.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
|
||||
<Menu>
|
||||
<Menu.Button
|
||||
class="flex items-center justify-center w-8 h-8 overflow-hidden rounded-full shadow-lg image-fit zoom-in intro-x uppercase"
|
||||
>
|
||||
<Lucide icon="Info" class="w-5 h-5 text-slate-300 intro-x cursor-pointer"/>
|
||||
</Menu.Button>
|
||||
|
||||
<Menu.Items
|
||||
class="w-56 mt-px relative bg-primary/80 before:block before:absolute before:bg-black before:inset-0 before:rounded-md before:z-[-1] text-white"
|
||||
>
|
||||
<Menu.Header class="font-normal">
|
||||
<div class="font-medium">App Information</div>
|
||||
</Menu.Header>
|
||||
<Menu.Divider class="bg-white/[0.08]" />
|
||||
<Menu.Item class="hover:bg-white/5">
|
||||
<div class="dark:text-slate-300">
|
||||
{{ backend }}<br/>
|
||||
{{ environment }}<br/>
|
||||
{{ build_id }}<br/>
|
||||
{{ context }}<br/>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</Menu>
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {Menu} from "@/base-components/Headless";
|
||||
import Lucide from "@/base-components/Lucide";
|
||||
|
||||
const backend = import.meta.env.VITE_API_URL
|
||||
const environment = import.meta.env.NODE_VERSION
|
||||
const build_id = import.meta.env.BUILD_ID
|
||||
const context = import.meta.env.CONTEXT
|
||||
|
||||
|
||||
|
||||
</script>
|
@ -34,100 +34,8 @@
|
||||
<Breadcrumb.Link to="/" :active="true"> Dashboard </Breadcrumb.Link>
|
||||
</Breadcrumb>
|
||||
<!-- END: Breadcrumb -->
|
||||
<!-- BEGIN: Search -->
|
||||
<div class="relative mr-3 intro-x sm:mr-6">
|
||||
<div class="relative hidden sm:block">
|
||||
<FormInput
|
||||
type="text"
|
||||
class="border-transparent w-56 shadow-none rounded-full bg-slate-200 pr-8 transition-[width] duration-300 ease-in-out focus:border-transparent focus:w-72 dark:bg-darkmode-400"
|
||||
placeholder="Search..."
|
||||
@focus="showSearchDropdown"
|
||||
@blur="hideSearchDropdown"
|
||||
/>
|
||||
<Lucide
|
||||
icon="Search"
|
||||
class="absolute inset-y-0 right-0 w-5 h-5 my-auto mr-3 text-slate-600 dark:text-slate-500"
|
||||
/>
|
||||
</div>
|
||||
<a class="relative text-white/70 sm:hidden" href="">
|
||||
<Lucide icon="Search" class="w-5 h-5 dark:text-slate-500" />
|
||||
</a>
|
||||
<TransitionRoot
|
||||
as="template"
|
||||
:show="searchDropdown"
|
||||
enter="transition-all ease-linear duration-150"
|
||||
enterFrom="mt-5 invisible opacity-0 translate-y-1"
|
||||
enterTo="mt-[3px] visible opacity-100 translate-y-0"
|
||||
entered="mt-[3px]"
|
||||
leave="transition-all ease-linear duration-150"
|
||||
leaveFrom="mt-[3px] visible opacity-100 translate-y-0"
|
||||
leaveTo="mt-5 invisible opacity-0 translate-y-1"
|
||||
>
|
||||
<div class="absolute right-0 z-10 mt-[3px]">
|
||||
<div class="w-[450px] p-5 box">
|
||||
<div class="mb-2 font-medium">Pages</div>
|
||||
<div class="mb-5">
|
||||
<a href="" class="flex items-center">
|
||||
<div
|
||||
class="flex items-center justify-center w-8 h-8 rounded-full bg-success/20 dark:bg-success/10 text-success"
|
||||
>
|
||||
<Lucide icon="Inbox" class="w-4 h-4" />
|
||||
</div>
|
||||
<div class="ml-3">Mail Settings</div>
|
||||
</a>
|
||||
<a href="" class="flex items-center mt-2">
|
||||
<div class="flex items-center justify-center w-8 h-8 rounded-full bg-pending/10 text-pending">
|
||||
<Lucide icon="Users" class="w-4 h-4" />
|
||||
</div>
|
||||
<div class="ml-3">Users & Permissions</div>
|
||||
</a>
|
||||
<a href="" class="flex items-center mt-2">
|
||||
<div
|
||||
class="flex items-center justify-center w-8 h-8 rounded-full bg-primary/10 dark:bg-primary/20 text-primary/80"
|
||||
>
|
||||
<Lucide icon="CreditCard" class="w-4 h-4" />
|
||||
</div>
|
||||
<div class="ml-3">Transactions Report</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="mb-2 font-medium">Users</div>
|
||||
<div class="mb-5">
|
||||
<a
|
||||
v-for="(faker, fakerKey) in _.take(fakerData, 4)"
|
||||
:key="fakerKey"
|
||||
href=""
|
||||
class="flex items-center mt-2"
|
||||
>
|
||||
<div class="w-8 h-8 image-fit">
|
||||
<img alt="Midone Tailwind HTML Admin Template" class="rounded-full" :src="faker.photos[0]" />
|
||||
</div>
|
||||
<div class="ml-3">{{ faker.users[0].name }}</div>
|
||||
<div class="w-48 ml-auto text-xs text-right truncate text-slate-500">
|
||||
{{ faker.users[0].email }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="mb-2 font-medium">Products</div>
|
||||
<a
|
||||
v-for="(faker, fakerKey) in _.take(fakerData, 4)"
|
||||
:key="fakerKey"
|
||||
href=""
|
||||
class="flex items-center mt-2"
|
||||
>
|
||||
<div class="w-8 h-8 image-fit">
|
||||
<img alt="Midone Tailwind HTML Admin Template" class="rounded-full" :src="faker.images[0]" />
|
||||
</div>
|
||||
<div class="ml-3">{{ faker.products[0].name }}</div>
|
||||
<div class="w-48 ml-auto text-xs text-right truncate text-slate-500">
|
||||
{{ faker.products[0].category }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionRoot>
|
||||
</div>
|
||||
<!-- END: Search -->
|
||||
<!-- BEGIN: DARK -->
|
||||
<DebugInfo class="mr-4" />
|
||||
<DarkModeSwitcher class="mr-4" />
|
||||
<!-- END: DARK -->
|
||||
<!-- BEGIN: Account Menu -->
|
||||
@ -173,6 +81,7 @@ import Breadcrumb from "@/base-components/Breadcrumb"
|
||||
import Logo from "@/base-components/Logo"
|
||||
import { FormInput } from "@/base-components/Form"
|
||||
import { Menu } from "@/base-components/Headless"
|
||||
import DebugInfo from "@/components/DebugInfo/DebugInfo.vue";
|
||||
|
||||
const router = useRouter()
|
||||
const props = defineProps<{
|
||||
|
@ -1,50 +1,8 @@
|
||||
<template>
|
||||
<div class="flex flex-col text-start">
|
||||
<slot name="title"></slot>
|
||||
<Dialog.Description class="flex-col space-y-4 p-8">
|
||||
<div class="flex flex-col">
|
||||
<Label>Name</Label>
|
||||
<Input
|
||||
type="email"
|
||||
v-model="resourceData.title"
|
||||
:class="{
|
||||
'input--invalid': v$.title.$error
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<Label>Type</Label>
|
||||
<Select
|
||||
v-model="resourceData.platform"
|
||||
:class="{
|
||||
'input--invalid': v$.platform.$error
|
||||
}"
|
||||
>
|
||||
<option v-for="(platform, index) in platformsOptions" :key="index" :value="platform.uuid">
|
||||
{{ platform.title }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<Label>App ID</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.appId"
|
||||
:class="{
|
||||
'input--invalid': v$.appId.$error
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<Label>App Secret</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.appSecret"
|
||||
:class="{
|
||||
'input--invalid': v$.appSecret.$error
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<Dialog.Description>
|
||||
<ResourceForm :resource-data="resourceData" :rules="rules"/>
|
||||
</Dialog.Description>
|
||||
<Dialog.Footer>
|
||||
<Button type="button" variant="outline-secondary" @click="closeModal(false)" class="w-20 mr-4"> Cancel </Button>
|
||||
@ -54,29 +12,41 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from "vue"
|
||||
import { useUIStore } from "@/store/modules/ui.ts"
|
||||
import { useResourceStore } from "@/store/modules/resources.ts"
|
||||
import { extractExternalResults } from "@/utils/helper.ts"
|
||||
import { useVuelidate } from "@vuelidate/core"
|
||||
import { required } from "@vuelidate/validators"
|
||||
import { Dialog } from "@/base-components/Headless"
|
||||
import {reactive} from "vue"
|
||||
import {useUIStore} from "@/store/modules/ui.ts"
|
||||
import {useResourceStore} from "@/store/modules/resources.ts"
|
||||
import {extractExternalResults} from "@/utils/helper.ts"
|
||||
import {useVuelidate} from "@vuelidate/core"
|
||||
import {required} from "@vuelidate/validators"
|
||||
import {Dialog} from "@/base-components/Headless"
|
||||
import Button from "@/base-components/Button"
|
||||
import Label from "@/base-components/Form/FormLabel.vue"
|
||||
import Input from "@/base-components/Form/FormInput.vue"
|
||||
import Select from "@/base-components/Form/FormSelect.vue"
|
||||
import { SNACKBAR_MESSAGE_TYPES } from "@/constants"
|
||||
import { IResourcePayload } from "@/types/resource.ts"
|
||||
import { IPlatform } from "@/interfaces/platform.interface.ts"
|
||||
import {SNACKBAR_MESSAGE_TYPES} from "@/constants"
|
||||
import {IResourcePayload} from "@/types/resource.ts"
|
||||
import ResourceForm from "@/components/Modal/ResourceForm.vue";
|
||||
import {IPlatform} from "@/interfaces/platform.interface.ts";
|
||||
|
||||
const resourceStore = useResourceStore()
|
||||
const uiStore = useUIStore()
|
||||
|
||||
const resourceData = reactive<IResourcePayload>({
|
||||
title: "",
|
||||
platform: "",
|
||||
appId: "",
|
||||
appSecret: ""
|
||||
platform: {"uuid":'','title':''} as IPlatform,
|
||||
_platform: [{"uuid":'','title':''} as IPlatform], // TODO Remove this
|
||||
securityGroup:"",
|
||||
subnet:"",
|
||||
endpoint:"",
|
||||
identityVersion:"",
|
||||
defaultNetwork:"",
|
||||
credentials: {
|
||||
user:"",
|
||||
secret:"",
|
||||
domain:"",
|
||||
},
|
||||
sshCredentials: {
|
||||
username:"",
|
||||
privateKey:"",
|
||||
keyPairName:"",
|
||||
}
|
||||
})
|
||||
|
||||
// HACK: https://github.com/vuelidate/vuelidate/issues/1147
|
||||
@ -84,9 +54,7 @@ const externalServerValidation = () => true
|
||||
|
||||
const rules = {
|
||||
title: { required, externalServerValidation },
|
||||
platform: { required, externalServerValidation },
|
||||
appId: { required, externalServerValidation },
|
||||
appSecret: { required, externalServerValidation }
|
||||
platform: { required, externalServerValidation }
|
||||
}
|
||||
|
||||
const $externalResults = reactive({})
|
||||
@ -96,8 +64,13 @@ const closeModal = (skipConfirmation: boolean = false) => {
|
||||
uiStore.setModalWindowState(null, skipConfirmation)
|
||||
}
|
||||
|
||||
|
||||
const createResource = async () => {
|
||||
if (!(await v$.value.$validate())) return
|
||||
if (!(await v$.value.$validate())){
|
||||
console.log("Failed validation")
|
||||
return
|
||||
}
|
||||
|
||||
resourceStore
|
||||
.createResource(resourceData)
|
||||
.then((createdResource) => {
|
||||
@ -113,11 +86,4 @@ const createResource = async () => {
|
||||
})
|
||||
}
|
||||
|
||||
const platformsOptions = ref<Array<IPlatform>>([])
|
||||
|
||||
const getPlatforms = async () => {
|
||||
platformsOptions.value = await resourceStore.getPlatforms()
|
||||
}
|
||||
|
||||
getPlatforms()
|
||||
</script>
|
||||
|
@ -1,51 +1,7 @@
|
||||
<template>
|
||||
<div class="flex flex-col text-start">
|
||||
<slot name="title"></slot>
|
||||
<Dialog.Description class="flex-col space-y-4 p-8">
|
||||
<div class="flex flex-col">
|
||||
<Label>Name</Label>
|
||||
<Input
|
||||
type="email"
|
||||
v-model="resourceData.title"
|
||||
:class="{
|
||||
'input--invalid': v$.title?.$error
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<Label>Type</Label>
|
||||
<Select
|
||||
v-model="resourceData.platform"
|
||||
:class="{
|
||||
'input--invalid': v$.platform?.$error
|
||||
}"
|
||||
>
|
||||
<option v-for="(platform, index) in platformsOptions" :key="index" :value="platform.uuid">
|
||||
{{ platform.title }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<Label>App ID</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.appId"
|
||||
:class="{
|
||||
'input--invalid': v$.appId?.$error
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<Label>App Secret</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.appSecret"
|
||||
:class="{
|
||||
'input--invalid': v$.appSecret?.$error
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</Dialog.Description>
|
||||
<ResourceForm :resource-data="resourceData" :rules="rules"/>
|
||||
<Dialog.Footer>
|
||||
<Button type="button" variant="outline-secondary" @click="closeModal(false)" class="w-20 mr-4"> Cancel </Button>
|
||||
<Button variant="primary" type="button" class="w-20" @click="editResource"> Save </Button>
|
||||
@ -68,6 +24,7 @@ import Select from "@/base-components/Form/FormSelect.vue"
|
||||
import { SNACKBAR_MESSAGE_TYPES } from "@/constants"
|
||||
import { IResourcePayload } from "@/types/resource.ts"
|
||||
import { IPlatform } from "@/interfaces/platform.interface.ts"
|
||||
import ResourceForm from "@/components/Modal/ResourceForm.vue";
|
||||
|
||||
interface ResourceEditingProps {
|
||||
payload: IResourcePayload & { uuid: string }
|
||||
@ -78,21 +35,14 @@ const props = defineProps<ResourceEditingProps>()
|
||||
const resourceStore = useResourceStore()
|
||||
const uiStore = useUIStore()
|
||||
|
||||
const resourceData = reactive<IResourcePayload>({
|
||||
title: props.payload.title ?? "",
|
||||
platform: props.payload.platform ?? "",
|
||||
appId: props.payload.appId ?? "",
|
||||
appSecret: props.payload.appSecret ?? ""
|
||||
})
|
||||
const resourceData = reactive<IResourcePayload>(props.payload)
|
||||
|
||||
// HACK: https://github.com/vuelidate/vuelidate/issues/1147
|
||||
const externalServerValidation = () => true
|
||||
|
||||
const rules = {
|
||||
title: { required, externalServerValidation },
|
||||
platform: { required, externalServerValidation },
|
||||
appId: { required, externalServerValidation },
|
||||
appSecret: { required, externalServerValidation }
|
||||
platform: { required, externalServerValidation }
|
||||
}
|
||||
|
||||
const $externalResults = reactive({})
|
||||
@ -103,8 +53,11 @@ const closeModal = (skipConfirmation: boolean = false) => {
|
||||
}
|
||||
|
||||
const editResource = async () => {
|
||||
if (!(await v$.value.$validate())) return
|
||||
if (!(await v$.value.$validate())) return
|
||||
const validate = await v$.value.$validate()
|
||||
if (!validate){
|
||||
return
|
||||
}
|
||||
|
||||
resourceStore
|
||||
.editResource(props.payload.uuid, resourceData)
|
||||
.then((editedResource) => {
|
||||
@ -120,11 +73,5 @@ const editResource = async () => {
|
||||
})
|
||||
}
|
||||
|
||||
const platformsOptions = ref<Array<IPlatform>>([])
|
||||
|
||||
const getPlatforms = async () => {
|
||||
platformsOptions.value = await resourceStore.getPlatforms()
|
||||
}
|
||||
|
||||
getPlatforms()
|
||||
</script>
|
||||
|
182
gui/src/components/Modal/ResourceForm.vue
Normal file
182
gui/src/components/Modal/ResourceForm.vue
Normal file
@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<Dialog.Description class="grid grid-cols-3 gap-4 p-4" :class=" availableInPlatform(['OPENSTACK','AWS']) ? 'grid-cols-3' : 'grid-cols-2' "
|
||||
>
|
||||
<div class="col-span-3 p-0">
|
||||
<div class="mb-3">
|
||||
<Label>Name</Label>
|
||||
<Input
|
||||
type="email"
|
||||
v-model="resourceData.title"
|
||||
:class="{
|
||||
'input--invalid': v$.title.$error
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<Label>Platform</Label>
|
||||
<Select
|
||||
v-model="resourceData.platform.uuid"
|
||||
:class="{
|
||||
'input--invalid': v$.platform.$error
|
||||
}"
|
||||
>
|
||||
<option v-for="(platform, index) in platformsOptions" :key="index" :value="platform.uuid">
|
||||
{{ platform.title }}
|
||||
</option>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<h3 class="font-bold text-lg mb-2">General</h3>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<Label>Default Network</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.defaultNetwork"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<Label>Subnet</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.subnet"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<Label>Endpoint</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.endpoint"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<Label>Identity Version</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.identityVersion"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<Label>Security Group</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.securityGroup"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<h3 class="font-bold text-lg mb-2">Credentials</h3>
|
||||
<div class="flex flex-col">
|
||||
<Label>Username</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.credentials.user"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<Label>Secret</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.credentials.secret"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<Label>Domain</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.credentials.domain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
|
||||
<h3 class="font-bold text-lg mb-2"
|
||||
v-if="availableInPlatform(['OPENSTACK','AWS'])"
|
||||
>SSH Credentials</h3>
|
||||
|
||||
|
||||
<div class="flex flex-col"
|
||||
v-if="availableInPlatform(['OPENSTACK','AWS'])"
|
||||
>
|
||||
<Label>Username</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.sshCredentials.username"
|
||||
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col"
|
||||
v-if="availableInPlatform(['OPENSTACK','AWS'])"
|
||||
>
|
||||
<Label>Key Pair Name</Label>
|
||||
<Input
|
||||
type="text"
|
||||
v-model="resourceData.sshCredentials.keyPairName"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col"
|
||||
v-if="availableInPlatform(['OPENSTACK','AWS'])"
|
||||
>
|
||||
<Label>Key Private Key</Label>
|
||||
<FormTextarea
|
||||
v-model="resourceData.sshCredentials.privateKey"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Dialog.Description>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from "vue"
|
||||
import {FormTextarea} from "@/base-components/Form";
|
||||
import { Dialog } from "@/base-components/Headless"
|
||||
import Input from "../../base-components/Form/FormInput.vue";
|
||||
import Label from "../../base-components/Form/FormLabel.vue";
|
||||
import Select from "../../base-components/Form/FormSelect.vue";
|
||||
import _ from "lodash";
|
||||
import {IPlatform} from "@/interfaces/platform.interface.ts";
|
||||
import {useResourceStore} from "@/store/modules/resources.ts";
|
||||
import {IResourcePayload} from "@/types/resource.ts";
|
||||
import {useVuelidate} from "@vuelidate/core";
|
||||
|
||||
const resourceStore = useResourceStore()
|
||||
|
||||
const props = defineProps(['resourceData','rules'])
|
||||
|
||||
const resourceData = ref<IResourcePayload>(props.resourceData)
|
||||
const rules = ref<IResourcePayload>(props.rules)
|
||||
|
||||
console.log("Resource Data", resourceData)
|
||||
|
||||
const $externalResults = reactive({})
|
||||
const v$ = useVuelidate(rules.value, resourceData.value, { $externalResults })
|
||||
|
||||
|
||||
const platformsOptions = ref<Array<IPlatform>>([])
|
||||
const availableInPlatform = (platforms:Array<string>): boolean => {
|
||||
|
||||
const to_uuid:Array<string> = []
|
||||
const availableOptions = _.each(platformsOptions.value, (k)=>{
|
||||
if(platforms.includes(k.title)){
|
||||
to_uuid.push(k.uuid)
|
||||
}
|
||||
})
|
||||
return to_uuid.includes(resourceData.value.platform.uuid)
|
||||
}
|
||||
const getPlatforms = async () => {
|
||||
platformsOptions.value = await resourceStore.getPlatforms()
|
||||
}
|
||||
getPlatforms()
|
||||
|
||||
|
||||
|
||||
</script>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog static size="xl" :open="Boolean(openedModalWindow)" @close="() => null">
|
||||
<Dialog static scrollable size="xl" :open="Boolean(openedModalWindow)" @close="() => null">
|
||||
<Dialog.Panel>
|
||||
<Snackbar v-if="uiStore.snackbarMessage" />
|
||||
<component v-if="openedModalWindow" :is="components[openedModalWindow.name]" :payload="openedModalWindow.payload">
|
||||
|
@ -3,7 +3,7 @@ import MODAL_WINDOW_NAMES from "./modalWindowNames.ts"
|
||||
import PLATFORM_COLOR from "./platformColors.ts"
|
||||
import OPERATORS from "./operators.ts"
|
||||
import SNACKBAR_MESSAGE_TYPES from "./snackbarMessageTypes.ts"
|
||||
import { behaviorOptions as BEHAVIOR_OPTIONS, unitTimeOptions as UNIT_TIME_OPTIONS } from "./metricsOptions.ts"
|
||||
import { behaviorOptions as BEHAVIOR_OPTIONS, behaviorOptionsInput as BEHAVIOR_OPTIONS_INPUT,behaviorOptionsOutput as BEHAVIOR_OPTIONS_OUTPUT, unitTimeOptions as UNIT_TIME_OPTIONS } from "./metricsOptions.ts"
|
||||
|
||||
export {
|
||||
VALIDATION_MESSAGES,
|
||||
@ -11,6 +11,8 @@ export {
|
||||
PLATFORM_COLOR,
|
||||
OPERATORS,
|
||||
BEHAVIOR_OPTIONS,
|
||||
BEHAVIOR_OPTIONS_INPUT,
|
||||
BEHAVIOR_OPTIONS_OUTPUT,
|
||||
UNIT_TIME_OPTIONS,
|
||||
SNACKBAR_MESSAGE_TYPES
|
||||
}
|
||||
|
@ -1,2 +1,4 @@
|
||||
export const behaviorOptions = ["all", "sliding"] as const
|
||||
export const unitTimeOptions = ["ms", "sec", "min", "hour", "day"] as const
|
||||
export const behaviorOptionsInput = ["batch", "sliding"] as const
|
||||
export const behaviorOptionsOutput = ["all", "first","last"] as const
|
||||
export const unitTimeOptions = ["ms", "sec", "min", "hour", "day", "events",] as const
|
||||
|
@ -28,7 +28,7 @@
|
||||
</div>
|
||||
<div class="flex space-x-2">
|
||||
|
||||
<Lucide v-if="application.status=='ready'" icon="PlayCircle" class="w-10 text-white" @click="deployApplication(application)" />
|
||||
<Lucide icon="PlayCircle" class="w-10 text-white" @click="deployApplication(application)" />
|
||||
<Lucide v-if="application.status=='draft' || application.status=='ready' || !application.status" icon="Pencil" class="w-10 text-warning" @click="toApplicationEditing(application)" />
|
||||
<Lucide v-if="application.status=='draft' || application.status=='ready' || !application.status" icon="Trash2" class="w-10 text-danger" @click="removeApplication(application.uuid)" />
|
||||
</div>
|
||||
@ -164,9 +164,7 @@ const removeApplication = (uuid: string) => {
|
||||
}
|
||||
|
||||
const toApplicationEditing = (application: IApplication) => {
|
||||
if(application.status == 'draft' || !application.status){
|
||||
router.push({ name: "application", params: { appUuid: application.uuid } })
|
||||
}
|
||||
router.push({ name: "application", params: { appUuid: application.uuid } })
|
||||
}
|
||||
const deployApplication = (application: IApplication) =>{
|
||||
|
||||
|
32
gui/src/containers/Applications/PolicyEditor/index.vue
Normal file
32
gui/src/containers/Applications/PolicyEditor/index.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-8 intro-y">
|
||||
<div class="flex flex-row justify-between items-center mb-4">
|
||||
<h2 class="text-base uppercase">Policy Editor</h2>
|
||||
<Button variant="primary" class="uppercase" @click="publishPolicy">Publish</Button>
|
||||
</div>
|
||||
|
||||
<div class="md:box flex-grow overflow-x-auto md:p-5" >
|
||||
<MonacoEditor
|
||||
v-model="policyStore.rules"
|
||||
language="json"
|
||||
class="min-h-[400px]"
|
||||
id="json_policy_editor"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {usePolicyStore} from "@/store/modules/policies.ts"
|
||||
import Button from "@/base-components/Button/Button.vue"
|
||||
import MonacoEditor from "@/base-components/MonacoEditor";
|
||||
|
||||
const policyStore = usePolicyStore()
|
||||
|
||||
const publishPolicy = async () => {
|
||||
await policyStore.publishPolicies(policyStore.rules)
|
||||
}
|
||||
|
||||
|
||||
</script>
|
@ -11,7 +11,7 @@
|
||||
<Table.Tr>
|
||||
<Table.Th class="border-b-0 whitespace-nowrap"> Name </Table.Th>
|
||||
<Table.Th class="border-b-0 whitespace-nowrap"> Platform </Table.Th>
|
||||
<Table.Th class="border-b-0 whitespace-nowrap"> AppId </Table.Th>
|
||||
<Table.Th class="border-b-0 whitespace-nowrap"> UUID </Table.Th>
|
||||
<Table.Th class="border-b-0 whitespace-nowrap"> App Secret </Table.Th>
|
||||
<Table.Th class="border-b-0 whitespace-nowrap w-24"> Action </Table.Th>
|
||||
</Table.Tr>
|
||||
@ -21,35 +21,37 @@
|
||||
<Table.Td
|
||||
class="first:rounded-l-md last:rounded-r-md bg-white border-b-0 dark:bg-darkmode-600 shadow-[20px_3px_20px_#0000000b]"
|
||||
>
|
||||
<div>{{ resource.title }}</div>
|
||||
<div>
|
||||
<strong>{{ resource.title }}</strong>
|
||||
</div>
|
||||
</Table.Td>
|
||||
<Table.Td
|
||||
class="first:rounded-l-md last:rounded-r-md bg-white border-b-0 dark:bg-darkmode-600 shadow-[20px_3px_20px_#0000000b]"
|
||||
>
|
||||
<div
|
||||
v-if="resource.platform"
|
||||
class="px-4 rounded-2xl w-20 text-center uppercase"
|
||||
:class="generateColor(resource.platform)"
|
||||
class="px-4 rounded-2xl w-40 text-center uppercase"
|
||||
:class="generateColor(resource.platform.uuid)"
|
||||
>
|
||||
{{ resource.platform }}
|
||||
{{ resource.platform.title }}
|
||||
</div>
|
||||
</Table.Td>
|
||||
<Table.Td
|
||||
class="first:rounded-l-md last:rounded-r-md bg-white border-b-0 dark:bg-darkmode-600 shadow-[20px_3px_20px_#0000000b]"
|
||||
>
|
||||
<div>{{ resource.appId }}</div>
|
||||
<div>{{ resource.uuid }}</div>
|
||||
</Table.Td>
|
||||
<Table.Td
|
||||
class="first:rounded-l-md last:rounded-r-md bg-white border-b-0 dark:bg-darkmode-600 shadow-[20px_3px_20px_#0000000b]"
|
||||
>
|
||||
<div>{{ resource.appSecret }}</div>
|
||||
<div>*************</div>
|
||||
</Table.Td>
|
||||
<Table.Td
|
||||
class="first:rounded-l-md last:rounded-r-md bg-white border-b-0 dark:bg-darkmode-600 shadow-[20px_3px_20px_#0000000b]"
|
||||
>
|
||||
<div class="flex space-x-3">
|
||||
<Lucide icon="Trash2" class="text-danger" @click="removeResource(resource.uuid)" />
|
||||
<Lucide icon="Eye" @click="loadNodeCandidates(resource.uuid)" />
|
||||
<Lucide icon="Eye" @click="retrieveAllCandidates(resource.uuid)" />
|
||||
<Lucide icon="MoreVertical" :data-tooltip="`user-tooltip-${index}`" />
|
||||
<TippyContent :to="`user-tooltip-${index}`" class="p-2">
|
||||
<Button variant="outline-warning" @click="openResourceEditingModal(resource)">Edit Resource</Button>
|
||||
@ -70,8 +72,43 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box p-5 mt-3">
|
||||
<NodeCandidatesTable />
|
||||
<div class="box p-5 mt-3" >
|
||||
<div class="flex-grow overflow-x-auto">
|
||||
<Table class="border-spacing-y-[10px] border-separate -mt-2 min-w-full max-w-max w-max">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th class="border-b-0 whitespace-nowrap"> Region </Table.Th>
|
||||
<Table.Th class="border-b-0 whitespace-nowrap"> Instance Type </Table.Th>
|
||||
<Table.Th class="border-b-0 whitespace-nowrap"> Vcores </Table.Th>
|
||||
<Table.Th class="border-b-0 whitespace-nowrap"> Memory (GB) </Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
<Table.Tr v-for="(node, index) in nodeCandidates" :key="index">
|
||||
<Table.Td
|
||||
class="first:rounded-l-md last:rounded-r-md bg-white border-b-0 dark:bg-darkmode-600 shadow-[20px_3px_20px_#0000000b]"
|
||||
>
|
||||
{{ node.region }}
|
||||
</Table.Td>
|
||||
<Table.Td
|
||||
class="first:rounded-l-md last:rounded-r-md bg-white border-b-0 dark:bg-darkmode-600 shadow-[20px_3px_20px_#0000000b]"
|
||||
>
|
||||
{{ node.instanceType }}
|
||||
</Table.Td>
|
||||
<Table.Td
|
||||
class="first:rounded-l-md last:rounded-r-md bg-white border-b-0 dark:bg-darkmode-600 shadow-[20px_3px_20px_#0000000b]"
|
||||
>
|
||||
{{ node.virtualCores }}
|
||||
</Table.Td>
|
||||
<Table.Td
|
||||
class="first:rounded-l-md last:rounded-r-md bg-white border-b-0 dark:bg-darkmode-600 shadow-[20px_3px_20px_#0000000b]"
|
||||
>
|
||||
{{ node.memory }}
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -80,8 +117,7 @@
|
||||
import { computed, ref } from "vue"
|
||||
import { useResourceStore } from "@/store/modules/resources.ts"
|
||||
import { useUIStore } from "@/store/modules/ui.ts"
|
||||
import NodeCandidatesTable from "./NodeCandidatesTable.vue"
|
||||
import { IResource } from "@/interfaces/resources.interface.ts"
|
||||
import {INodeCandidate, IResource} from "@/interfaces/resources.interface.ts"
|
||||
import { MODAL_WINDOW_NAMES, SNACKBAR_MESSAGE_TYPES } from "@/constants"
|
||||
import Button from "@/base-components/Button/Button.vue"
|
||||
import Lucide from "@/base-components/Lucide/Lucide.vue"
|
||||
@ -91,12 +127,18 @@ import TippyContent from "@/base-components/TippyContent/TippyContent.vue"
|
||||
import { usePagination } from "@/composables/usePagination.ts"
|
||||
import { generateColor } from "@/utils/colors.ts"
|
||||
|
||||
|
||||
|
||||
|
||||
const resourceStore = useResourceStore()
|
||||
const uiStore = useUIStore()
|
||||
|
||||
const resources = computed<Array<IResource>>(() => resourceStore.resources.results)
|
||||
const nodeCandidates = computed<Array<INodeCandidate>>(() => resourceStore.candidates)
|
||||
|
||||
const currentPage = ref(1)
|
||||
|
||||
|
||||
const rowsPerPage = ref(10)
|
||||
|
||||
const rowsPerPageChange = (rows: number) => {
|
||||
@ -109,10 +151,15 @@ const { paginatedArray, numberOfPages } = usePagination<IResource>({
|
||||
currentPage: currentPage
|
||||
})
|
||||
|
||||
|
||||
const retrieveAllResources = async () => {
|
||||
await resourceStore.getAllResources()
|
||||
}
|
||||
|
||||
const retrieveAllCandidates = async (uuid:string) => {
|
||||
await resourceStore.getAllNodeCandidate(uuid)
|
||||
}
|
||||
|
||||
const removeResource = (uuid: string) => {
|
||||
uiStore.setModalWindowState({
|
||||
name: MODAL_WINDOW_NAMES.CONFIRM_DELETING_MODAL,
|
||||
@ -130,8 +177,6 @@ const removeResource = (uuid: string) => {
|
||||
})
|
||||
}
|
||||
|
||||
const loadNodeCandidates = (uuid: string) => {}
|
||||
|
||||
const openResourceEditingModal = (resource: IResource) => {
|
||||
uiStore.setModalWindowState({
|
||||
name: MODAL_WINDOW_NAMES.RESOURCE_EDITING,
|
||||
|
@ -4,6 +4,8 @@ const ApplicationsOverview = () =>
|
||||
import(/* webpackChunkName: "ApplicationsOverview" */ "@/containers/Applications/Overview")
|
||||
const ApplicationsResources = () =>
|
||||
import(/* webpackChunkName: "ApplicationsResources" */ "@/containers/Applications/Resources")
|
||||
const ApplicationsPolicyEditor = () =>
|
||||
import(/* webpackChunkName: "ApplicationsResources" */ "@/containers/Applications/PolicyEditor")
|
||||
const ApplicationCreation = () =>
|
||||
import(/* webpackChunkName: "ApplicationCreation" */ "@/containers/Applications/ApplicationCreation")
|
||||
|
||||
@ -32,6 +34,12 @@ const ApplicationsRoute: RouteRecordRaw = {
|
||||
name: "applications-resources",
|
||||
component: ApplicationsResources
|
||||
},
|
||||
{
|
||||
path: "policy-editor",
|
||||
name: "policy-editor",
|
||||
component: ApplicationsPolicyEditor
|
||||
},
|
||||
|
||||
{
|
||||
path: "creation",
|
||||
name: "application-creation",
|
||||
|
@ -24,6 +24,14 @@
|
||||
<!-- <p class="text-right">
|
||||
<a class="text-blue-600 text-sm font-light hover:underline"> Forgot Password? </a>
|
||||
</p> -->
|
||||
|
||||
<div class="text-slate-200 dark:text-slate-800 text-center">
|
||||
{{ environment }} | {{ build_id }} | {{ context }}
|
||||
</div>
|
||||
<div class="text-white dark:text-slate-800 text-center">
|
||||
{{ backend }}
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</template>
|
||||
|
||||
@ -38,8 +46,12 @@ import { FormInput } from "@/base-components/Form"
|
||||
import { useUserStore } from "@/store/modules/user.ts"
|
||||
import { ICredentials } from "@/interfaces/user.interface.ts"
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const userStore = useUserStore()
|
||||
const backend = import.meta.env.VITE_API_URL
|
||||
const environment = import.meta.env.NODE_VERSION
|
||||
const build_id = import.meta.env.BUILD_ID
|
||||
const context = import.meta.env.CONTEXT
|
||||
const form = reactive<ICredentials>({
|
||||
username: "",
|
||||
password: ""
|
||||
|
@ -7,14 +7,15 @@ export interface IWindowController {
|
||||
}
|
||||
|
||||
export interface IMetric {
|
||||
name: string
|
||||
name: string,
|
||||
level: "global" | "components"
|
||||
components?: Array<string>
|
||||
}
|
||||
|
||||
export interface IMetricComposite extends IMetric {
|
||||
type: "composite"
|
||||
level: "global" | "components"
|
||||
components?: Array<string>
|
||||
formula: string
|
||||
template: string
|
||||
isWindowInput: boolean
|
||||
isWindowOutput: boolean
|
||||
input: IWindowController | null
|
||||
@ -24,9 +25,7 @@ export interface IMetricComposite extends IMetric {
|
||||
export interface IMetricRaw extends IMetric {
|
||||
type: "raw"
|
||||
sensor: string
|
||||
isWindowInputRaw: boolean
|
||||
isWindowOutputRaw: boolean
|
||||
inputRaw: IWindowController | null
|
||||
outputRaw: IWindowController | null
|
||||
config: Array<RawMetricConfigType>
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
export interface IPlatform {
|
||||
uuid: string
|
||||
type: string
|
||||
title: string
|
||||
}
|
||||
|
@ -1,15 +1,49 @@
|
||||
import {IPlatform} from "@/interfaces/platform.interface.ts";
|
||||
|
||||
export interface ISSHCredentials{
|
||||
username: string
|
||||
keyPairName: string
|
||||
privateKey: string
|
||||
|
||||
}
|
||||
|
||||
export interface ICredentials{
|
||||
user: string
|
||||
secret: string
|
||||
domain: string
|
||||
|
||||
}
|
||||
|
||||
|
||||
export interface IResource {
|
||||
uuid: string
|
||||
title: string
|
||||
platform: string
|
||||
securityGroup: string
|
||||
subnet: string
|
||||
endpoint: string
|
||||
identityVersion: string
|
||||
defaultNetwork: string
|
||||
enabled: boolean
|
||||
appId: string
|
||||
appSecret: string
|
||||
credentials: ICredentials
|
||||
sshCredentials: ISSHCredentials
|
||||
platform: IPlatform,
|
||||
_platform: Array<IPlatform>
|
||||
}
|
||||
|
||||
|
||||
export interface IAppResource {
|
||||
uuid: string
|
||||
title: string
|
||||
platform: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface INodeCandidate {
|
||||
id: number
|
||||
region: string
|
||||
instanceType: string
|
||||
virtualCores: number
|
||||
memory: number
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
export interface IUtilityFunction {
|
||||
functionName: string
|
||||
functionType: "maximize" | "constant"
|
||||
functionType: "maximize" | "constant" | "minimize"
|
||||
functionExpression: string
|
||||
functionExpressionVariables: Array<{ nameVariable: string; valueVariable: string }>
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import axios from "axios"
|
||||
import {IApplication, IApplicationOverview} from "@/interfaces/application.interface.ts"
|
||||
import {DeleteResponseType, DeployResponseType} from "@/types/responses.ts"
|
||||
import {DeleteResponseType, DeployResponseType, PolicyResponseType} from "@/types/responses.ts"
|
||||
import {IVariable} from "@/interfaces/variables.interface.ts"
|
||||
import {IResource} from "@/interfaces/resources.interface.ts"
|
||||
import {ITemplate} from "@/interfaces/template.interface.ts"
|
||||
@ -66,6 +66,7 @@ export default {
|
||||
level: metric.level,
|
||||
components: metric.components,
|
||||
name: metric.name,
|
||||
template: metric.template,
|
||||
formula: metric.formula,
|
||||
isWindowInput: metric.isWindowInput,
|
||||
input: {
|
||||
@ -84,14 +85,10 @@ export default {
|
||||
return {
|
||||
type: metric.type,
|
||||
name: metric.name,
|
||||
level: metric.level,
|
||||
components: metric.components,
|
||||
sensor: metric.sensor,
|
||||
config: metric.config,
|
||||
isWindowInputRaw: metric.isWindowInputRaw,
|
||||
inputRaw: {
|
||||
type: metric.inputRaw?.type ?? "all",
|
||||
interval: metric.inputRaw?.interval ?? 30,
|
||||
unit: metric.inputRaw?.unit ?? "sec"
|
||||
},
|
||||
isWindowOutputRaw: metric.isWindowOutputRaw,
|
||||
outputRaw: {
|
||||
type: metric.outputRaw?.type ?? "all",
|
||||
@ -137,6 +134,11 @@ export default {
|
||||
},
|
||||
async deployApplication(uuid: string): Promise<DeployResponseType> {
|
||||
return axios.post(`/api/v1/application/${uuid}/uuid/deploy`).then(({data}) => data)
|
||||
},
|
||||
async publishPolicies(policies:string): Promise<PolicyResponseType> {
|
||||
console.log("Publishing policies" , policies)
|
||||
return axios.post(`/api/v1/policies/publish`,{policies:policies}).then(({data}) => data)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,14 +1,19 @@
|
||||
import axios from "axios"
|
||||
import { IResource } from "@/interfaces/resources.interface.ts"
|
||||
import {INodeCandidate, IResource} from "@/interfaces/resources.interface.ts"
|
||||
import { IResourcePayload } from "@/types/resource.ts"
|
||||
import { DeleteResponseType } from "@/types/responses.ts"
|
||||
import { IPlatform } from "@/interfaces/platform.interface.ts"
|
||||
|
||||
export default {
|
||||
async getAllResources(): Promise<IPagination<IResource>> {
|
||||
return axios.get("/api/v1/resources").then(({ data }) => data)
|
||||
return axios.get("/api/v1/resources/all").then(({ data }) => data)
|
||||
},
|
||||
async getCandidates(uuid:string): Promise<Array<INodeCandidate>> {
|
||||
return axios.get(`/api/v1/resources/${uuid}/candidates`).then(({ data }) => data)
|
||||
},
|
||||
|
||||
async createResource(payload: IResourcePayload): Promise<IResource> {
|
||||
|
||||
return axios.post("/api/v1/resources", payload).then(({ data }) => data)
|
||||
},
|
||||
async editResource(uuid: string, payload: IResourcePayload): Promise<IResource> {
|
||||
|
@ -30,6 +30,12 @@ export const useSideMenuStore = defineStore("sideMenu", {
|
||||
pageName: "applications-resources",
|
||||
title: "Resources"
|
||||
}
|
||||
,
|
||||
{
|
||||
pageName: "policy-editor",
|
||||
title: "Security Policies"
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
|
24
gui/src/store/modules/policies.ts
Normal file
24
gui/src/store/modules/policies.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { defineStore } from "pinia"
|
||||
import { IResource, INodeCandidate } from "@/interfaces/resources.interface.ts"
|
||||
|
||||
import resourceService from "@/store/api-services/resources.service.ts"
|
||||
import { IResourcePayload } from "@/types/resource.ts"
|
||||
import { IPlatform } from "@/interfaces/platform.interface.ts"
|
||||
import applicationService from "@/store/api-services/application.service.ts";
|
||||
|
||||
interface PoliciesState {
|
||||
rules: string
|
||||
}
|
||||
|
||||
export const usePolicyStore = defineStore("policies", {
|
||||
state: (): PoliciesState => ({
|
||||
rules: ""
|
||||
}),
|
||||
actions: {
|
||||
async publishPolicies(rules:string): Promise<string> {
|
||||
return applicationService.publishPolicies(rules).then((status) => {
|
||||
return status.status
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
@ -1,29 +1,39 @@
|
||||
import { defineStore } from "pinia"
|
||||
import { IResource } from "@/interfaces/resources.interface.ts"
|
||||
import { IResource, INodeCandidate } from "@/interfaces/resources.interface.ts"
|
||||
|
||||
import resourceService from "@/store/api-services/resources.service.ts"
|
||||
import { IResourcePayload } from "@/types/resource.ts"
|
||||
import { IPlatform } from "@/interfaces/platform.interface.ts"
|
||||
|
||||
interface ResourcesState {
|
||||
resources: IPagination<IResource>
|
||||
candidates: Array<INodeCandidate>
|
||||
platforms: Array<IPlatform>
|
||||
}
|
||||
|
||||
export const useResourceStore = defineStore("resource", {
|
||||
state: (): ResourcesState => ({
|
||||
resources: { pages: 0, currentPage: 0, results: [] },
|
||||
candidates: [],
|
||||
platforms: []
|
||||
}),
|
||||
actions: {
|
||||
async createResource(payload: IResourcePayload): Promise<IResource> {
|
||||
const createdResource = await resourceService.createResource(payload)
|
||||
createdResource.platform = {'uuid':createdResource._platform[0].uuid, 'title':createdResource._platform[0].title}
|
||||
this.resources.results.unshift(createdResource)
|
||||
return createdResource
|
||||
},
|
||||
async getAllResources(): Promise<IPagination<IResource>> {
|
||||
this.platforms = await this.getPlatforms()
|
||||
this.resources = await resourceService.getAllResources()
|
||||
return this.resources
|
||||
},
|
||||
async getAllNodeCandidate(uuid:string): Promise<Array<INodeCandidate>> {
|
||||
this.candidates = await resourceService.getCandidates(uuid)
|
||||
return this.candidates
|
||||
},
|
||||
|
||||
async deleteResource(uuid: string): Promise<IPagination<IResource>> {
|
||||
return await resourceService.deleteResource(uuid).then(() => {
|
||||
const removedResourceIndex = this.resources.results.findIndex((res) => res.uuid === uuid)
|
||||
|
@ -42,6 +42,7 @@ export const useUIStore = defineStore("ui", {
|
||||
controlledWindowClosure: true,
|
||||
isConfirmPresent: true,
|
||||
confirmAction: () => {
|
||||
console.log("Confirm action")
|
||||
this.openedModalWindow = null
|
||||
this.setSnackbarMessage()
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
export type MetricType = "raw" | "composite"
|
||||
export type BehaviorType = "all" | "sliding"
|
||||
export type BehaviorType = "all" | "sliding" | "batch" | "first" | "last"
|
||||
export type UnitTimeType = "ms" | "sec" | "min" | "hour" | "day"
|
||||
export type RawMetricConfigType = { name: string; value: string }
|
||||
export type OperatorType = ">" | "<" | "<=" | ">=" | "==" | "!=="
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { IResource } from "@/interfaces/resources.interface.ts"
|
||||
import {INodeCandidate, IResource} from "@/interfaces/resources.interface.ts"
|
||||
|
||||
export type IResourcePayload = Omit<IResource, "uuid" | "enabled">
|
||||
export type ICandidatesPayload = Omit<INodeCandidate, "uuid" | "enabled">
|
||||
|
@ -1,2 +1,3 @@
|
||||
export type DeleteResponseType = { status: "success"; message: string }
|
||||
export type DeployResponseType = { status: "success"; message: string }
|
||||
export type PolicyResponseType = { status: "success"; message: string }
|
||||
|
@ -54,6 +54,9 @@ const getColor = (colorKey: DotNestedKeys<Colors>, opacity: number = 1) => {
|
||||
}
|
||||
|
||||
const generateColor = (experimental: string): string => {
|
||||
if(!experimental){
|
||||
return PLATFORM_COLOR['bg-indigo-600']
|
||||
}
|
||||
const colorKey = [...experimental].reduce((acc, char) => acc + char.charCodeAt(0), 0) % PLATFORM_COLOR.length
|
||||
return PLATFORM_COLOR[colorKey]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user