Skip to content

Commit

Permalink
Merge branch 'custom-default-settings' of https://github.com/socfortr…
Browse files Browse the repository at this point in the history
…ess/CoPilot into custom-default-settings
  • Loading branch information
taylorwalton committed Feb 23, 2024
2 parents 57577b2 + 900c08f commit 6590f3b
Show file tree
Hide file tree
Showing 7 changed files with 319 additions and 3 deletions.
37 changes: 36 additions & 1 deletion frontend/src/api/customers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import type {
CustomerAgentHealth,
CustomerDecomissionedData,
CustomerMeta,
CustomerProvision
CustomerProvision,
CustomerProvisioningDefaultSettings
} from "@/types/customers.d"
import type { Agent } from "@/types/agents.d"

Expand All @@ -15,6 +16,13 @@ export interface CustomerAgentsHealthcheckQuery {
days?: number
}

export interface ProvisioningDefaultSettingsPayload {
clusterName: string
clusterKey: string
masterIp: string
grafanaUrl: string
}

export default {
getCustomers(code?: string) {
return HttpClient.get<FlaskBaseResponse & { customers?: Customer[]; customer?: Customer }>(
Expand Down Expand Up @@ -121,5 +129,32 @@ export default {
return HttpClient.get<FlaskBaseResponse & { available_subscriptions: string[] }>(
`/customer_provisioning/provision/subscriptions`
)
},
getProvisioningDefaultSettings() {
return HttpClient.get<
FlaskBaseResponse & { customer_provisioning_default_settings: CustomerProvisioningDefaultSettings }
>(`/customer_provisioning/default_settings`)
},
setProvisioningDefaultSettings(payload: ProvisioningDefaultSettingsPayload) {
return HttpClient.post<
FlaskBaseResponse & { customer_provisioning_default_settings: CustomerProvisioningDefaultSettings }
>(`/customer_provisioning/default_settings`, {
id: 0,
cluster_name: payload.clusterName,
cluster_key: payload.clusterKey,
master_ip: payload.masterIp,
grafana_url: payload.grafanaUrl
})
},
updateProvisioningDefaultSettings(payload: ProvisioningDefaultSettingsPayload) {
return HttpClient.put<
FlaskBaseResponse & { customer_provisioning_default_settings: CustomerProvisioningDefaultSettings }
>(`/customer_provisioning/default_settings`, {
id: 0,
cluster_name: payload.clusterName,
cluster_key: payload.clusterKey,
master_ip: payload.masterIp,
grafana_url: payload.grafanaUrl
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<n-select v-model:value="form.action" :options="invokeActionOptions" />
</n-form-item>
<n-form-item label="IP Address" path="ip">
<n-input v-model:value.trim="form.ip" placeholder="Input the IP Address..." />
<n-input v-model:value.trim="form.ip" placeholder="Input the IP Address..." clearable />
</n-form-item>
</div>
</n-form>
Expand Down
235 changes: 235 additions & 0 deletions frontend/src/components/customers/CustomerDefaultSettingForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
<template>
<n-spin :show="loading" class="customer-provisioning-default-settings-form">
<n-form :label-width="80" :model="form" :rules="rules" ref="formRef">
<div class="flex flex-col gap-4">
<div class="flex flex-wrap gap-4">
<div v-for="(_, key) of form" :key="key" class="grow">
<n-form-item :label="fieldsMeta[key].label" :path="key" class="grow">
<n-input
v-model:value.trim="form[key]"
:placeholder="fieldsMeta[key].placeholder"
clearable
/>
</n-form-item>
</div>
</div>
<div class="flex justify-between gap-4">
<div class="flex gap-4">
<slot name="additionalActions"></slot>
</div>
<div class="flex gap-4">
<n-button @click="reset()" :disabled="loading">Reset</n-button>
<n-button
type="primary"
:disabled="!isValid"
@click="validate()"
:loading="submittingDefaultSettings"
>
Submit
</n-button>
</div>
</div>
</div>
</n-form>
</n-spin>
</template>

<script setup lang="ts">
import { computed, onBeforeMount, onMounted, ref, watch } from "vue"
import Api from "@/api"
import {
useMessage,
NForm,
NFormItem,
NInput,
NButton,
NSpin,
type FormValidationError,
type FormInst,
type FormRules,
type FormItemRule
} from "naive-ui"
import type { CustomerProvisioningDefaultSettings } from "@/types/customers.d"
import _trim from "lodash/trim"
import _get from "lodash/get"
import isURL from "validator/es/lib/isURL"
import isIP from "validator/es/lib/isIP"
const emit = defineEmits<{
(e: "update:loading", value: boolean): void
(
e: "mounted",
value: {
load: () => void
}
): void
}>()
const loadingDefaultSettings = ref(false)
const submittingDefaultSettings = ref(false)
const loading = computed(() => loadingDefaultSettings.value || submittingDefaultSettings.value)
const message = useMessage()
const form = ref<Omit<CustomerProvisioningDefaultSettings, "id">>(getClearForm())
const formRef = ref<FormInst | null>(null)
const isNew = ref(true)
const rules: FormRules = {
cluster_name: {
message: "Please input the Cluster Name",
trigger: ["input", "blur"]
},
cluster_key: {
message: "Please input the Cluster Key",
trigger: ["input", "blur"]
},
master_ip: {
validator: validateIp,
trigger: ["blur"]
},
grafana_url: {
validator: validateUrl,
trigger: ["blur"]
}
}
const fieldsMeta = {
cluster_name: {
label: "Cluster Name",
placeholder: "Insert the Cluster Name"
},
cluster_key: {
label: "Cluster Key",
placeholder: "Insert the Cluster Key"
},
master_ip: {
label: "Master IP",
placeholder: "Insert the Master IP"
},
grafana_url: {
label: "Grafana URL",
placeholder: "Insert the Grafana URL"
}
}
const isValid = computed(() => {
let valid = true
for (const key in rules) {
const rule = rules[key] as FormRules
if (rule.required && !_trim(_get(form.value, key))) {
valid = false
}
}
return valid
})
function validate() {
if (!formRef.value) return
formRef.value.validate((errors?: Array<FormValidationError>) => {
if (!errors) {
submit()
} else {
message.warning("You must fill in the required fields correctly.")
return false
}
})
}
function getClearForm(settings?: Omit<CustomerProvisioningDefaultSettings, "id">) {
return {
cluster_name: settings?.cluster_name || "",
cluster_key: settings?.cluster_key || "",
master_ip: settings?.master_ip || "",
grafana_url: settings?.grafana_url || ""
}
}
function reset() {
if (!loading.value) {
form.value = getClearForm()
}
}
function submit() {
submittingDefaultSettings.value = true
const method = isNew.value ? "setProvisioningDefaultSettings" : "updateProvisioningDefaultSettings"
const payload = {
clusterName: form.value.cluster_name,
clusterKey: form.value.cluster_key,
masterIp: form.value.master_ip,
grafanaUrl: form.value.grafana_url
}
Api.customers[method](payload)
.then(res => {
if (res.data.success) {
isNew.value = false
} else {
message.warning(res.data?.message || "An error occurred. Please try again later.")
}
})
.catch(err => {
message.error(err.response?.data?.message || "An error occurred. Please try again later.")
})
.finally(() => {
submittingDefaultSettings.value = false
})
}
function setForm(settings?: CustomerProvisioningDefaultSettings) {
form.value = getClearForm(settings)
}
function validateIp(rule: FormItemRule, value: string) {
if (value && !isIP(value)) {
return new Error("Please input a valid IP Address")
}
return true
}
function validateUrl(rule: FormItemRule, value: string) {
if (value && !isURL(value)) {
return new Error("Please input a valid URL")
}
return true
}
function getProvisioningDefaultSettings() {
if (!loadingDefaultSettings.value) {
loadingDefaultSettings.value = true
Api.customers
.getProvisioningDefaultSettings()
.then(res => {
if (res.data.success) {
isNew.value = false
setForm(res.data?.customer_provisioning_default_settings)
}
})
.finally(() => {
loadingDefaultSettings.value = false
})
}
}
watch(loading, val => {
emit("update:loading", val)
})
onBeforeMount(() => {
getProvisioningDefaultSettings()
})
onMounted(() => {
emit("mounted", {
load: getProvisioningDefaultSettings
})
})
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<template>
<n-button size="small" secondary @click="showForm = true" :loading="loading">
<template #icon>
<Icon :name="SettingsIcon" :size="14"></Icon>
</template>
Default Settings
</n-button>

<n-modal
v-model:show="showForm"
display-directive="show"
preset="card"
:style="{ maxWidth: 'min(600px, 90vw)', minHeight: 'min(300px, 90vh)', overflow: 'hidden' }"
title="Customer Provisioning Default Settings"
:bordered="false"
segmented
>
<CustomerDefaultSettingForm @mounted="settingsFormCTX = $event" v-model:loading="loading" />
</n-modal>
</template>

<script setup lang="ts">
import { ref, watch } from "vue"
import { NButton, NModal } from "naive-ui"
import Icon from "@/components/common/Icon.vue"
import CustomerDefaultSettingForm from "./CustomerDefaultSettingForm.vue"
const SettingsIcon = "carbon:settings-edit"
const settingsFormCTX = ref<{ load: () => void } | null>(null)
const showForm = ref(false)
const loading = ref(false)
watch(showForm, () => {
settingsFormCTX.value?.load()
})
</script>
2 changes: 1 addition & 1 deletion frontend/src/components/customers/CustomersList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Total:
<strong class="font-mono">{{ totalCustomers }}</strong>
</div>
<div>
<div class="flex items-center gap-3">
<slot></slot>
</div>
</div>
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/types/customers.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,11 @@ export interface CustomerDecomissionedData {
stream_deleted: string
index_deleted: string
}

export interface CustomerProvisioningDefaultSettings {
id: number
cluster_name: string
cluster_key: string
master_ip: string
grafana_url: string
}
2 changes: 2 additions & 0 deletions frontend/src/views/Customers.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<div class="page">
<CustomersList :highlight="highlight" :reload="reload" @reloaded="reload = false">
<CustomerDefaultSettingsButton />
<CustomerCreationButton v-model:openForm="openForm" @submitted="reload = true" />
</CustomersList>
</div>
Expand All @@ -9,6 +10,7 @@
<script setup lang="ts">
import CustomersList from "@/components/customers/CustomersList.vue"
import CustomerCreationButton from "@/components/customers/CustomerCreationButton.vue"
import CustomerDefaultSettingsButton from "@/components/customers/CustomerDefaultSettingsButton.vue"
import { onBeforeMount, onMounted, onUnmounted, ref } from "vue"
import { useRoute, useRouter } from "vue-router"
import { emitter } from "@/emitter"
Expand Down

0 comments on commit 6590f3b

Please sign in to comment.