- Docker infrastructure (Dockerfile, docker-compose.yml, genieacs-sim) - AdonisJS v7 app with Inertia.js + Vue 3 - MongoDB integration - CWMP/TR-069 state machine engine - Configuration engine with compliance tracking - Branding: ADAM ACS (favicon, navbar, locales) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
183 lines
7.4 KiB
Vue
183 lines
7.4 KiB
Vue
<script setup lang="ts">
|
||
import { ref } from 'vue'
|
||
import { router } from '@inertiajs/vue3'
|
||
import { useI18n } from 'vue-i18n'
|
||
import TitleBar from '~/components/TitleBar.vue'
|
||
import TagsInput from '~/components/configuration/TagsInput.vue'
|
||
import CriteriaEditor from '~/components/configuration/CriteriaEditor.vue'
|
||
|
||
const { t } = useI18n()
|
||
|
||
const form = ref({
|
||
identifier: '',
|
||
name: '',
|
||
version: '1.0.0',
|
||
priority: 1,
|
||
policy: 'maintain',
|
||
enabled: true,
|
||
tags: [] as string[],
|
||
criteria: {} as Record<string, string>,
|
||
inventory: [] as { path: string }[],
|
||
desiredParameters: [] as { name: string; value: string; type: string }[],
|
||
})
|
||
|
||
const errorMsg = ref('')
|
||
|
||
function addInventoryPath() {
|
||
form.value.inventory.push({ path: '' })
|
||
}
|
||
|
||
function removeInventoryPath(index: number) {
|
||
form.value.inventory.splice(index, 1)
|
||
}
|
||
|
||
function addParameter() {
|
||
form.value.desiredParameters.push({ name: '', value: '', type: 'string' })
|
||
}
|
||
|
||
function removeParameter(index: number) {
|
||
form.value.desiredParameters.splice(index, 1)
|
||
}
|
||
|
||
async function submitForm() {
|
||
errorMsg.value = ''
|
||
try {
|
||
const response = await fetch('/api/v1/configurations', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(form.value),
|
||
})
|
||
if (response.ok) {
|
||
router.get('/configurations')
|
||
} else {
|
||
const err = await response.json()
|
||
errorMsg.value = err.message || 'An error occurred'
|
||
}
|
||
} catch (e) {
|
||
errorMsg.value = 'Failed to create configuration'
|
||
}
|
||
}
|
||
|
||
function goBack() {
|
||
router.get('/configurations')
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<TitleBar section="configurations" subtitle="New Configuration" />
|
||
|
||
<div class="container-xxl py-4">
|
||
<div class="row">
|
||
<div class="col-12">
|
||
|
||
<div v-if="errorMsg" class="alert alert-danger mb-3">{{ errorMsg }}</div>
|
||
|
||
<form @submit.prevent="submitForm">
|
||
|
||
<!-- General Info -->
|
||
<div class="card border-0 shadow-sm mb-4">
|
||
<div class="card-header"><h2 class="h6 mb-0">{{ t('labels.general') }}</h2></div>
|
||
<div class="card-body">
|
||
<div class="row g-3">
|
||
<div class="col-md-6">
|
||
<label class="form-label">{{ t('labels.identifier') }} <span class="text-danger">*</span></label>
|
||
<input v-model="form.identifier" type="text" class="form-control" placeholder="my-config-001" required />
|
||
<div class="form-text">Kebab-case, e.g. bm632w-standard</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label class="form-label">{{ t('labels.name') }} <span class="text-danger">*</span></label>
|
||
<input v-model="form.name" type="text" class="form-control" placeholder="BM632w Standard" required />
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label">{{ t('labels.version') }} <span class="text-danger">*</span></label>
|
||
<input v-model="form.version" type="text" class="form-control" placeholder="1.0.0" pattern="\d+\.\d+\.\d+" required />
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label">{{ t('labels.priority') }} <span class="text-danger">*</span></label>
|
||
<input v-model.number="form.priority" type="number" class="form-control" min="1" required />
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label">{{ t('labels.policy') }} <span class="text-danger">*</span></label>
|
||
<select v-model="form.policy" class="form-select" required>
|
||
<option value="maintain">{{ t('labels.maintain') }}</option>
|
||
<option value="once">{{ t('labels.once') }}</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-3 d-flex align-items-end">
|
||
<div class="form-check form-switch mb-2">
|
||
<input v-model="form.enabled" type="checkbox" class="form-check-input" id="enabled" role="switch" />
|
||
<label class="form-check-label" for="enabled">{{ t('labels.enabled') }}</label>
|
||
</div>
|
||
</div>
|
||
<div class="col-12">
|
||
<label class="form-label">{{ t('labels.tags') }}</label>
|
||
<TagsInput v-model="form.tags" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Criteria -->
|
||
<div class="card border-0 shadow-sm mb-4">
|
||
<div class="card-header"><h2 class="h6 mb-0">{{ t('labels.matchingCriteria') }}</h2></div>
|
||
<div class="card-body">
|
||
<CriteriaEditor v-model="form.criteria" />
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Inventory Paths -->
|
||
<div class="card border-0 shadow-sm mb-4">
|
||
<div class="card-header"><h2 class="h6 mb-0">{{ t('labels.inventoryPaths') }}</h2></div>
|
||
<div class="card-body">
|
||
<div v-for="(path, i) in form.inventory" :key="i" class="input-group mb-2">
|
||
<input v-model="path.path" type="text" class="form-control font-monospace" placeholder="InternetGatewayDevice.DeviceInfo." />
|
||
<button type="button" class="btn btn-outline-danger" @click="removeInventoryPath(i)">×</button>
|
||
</div>
|
||
<button type="button" class="btn btn-outline-secondary btn-sm mt-1" @click="addInventoryPath">
|
||
+ {{ t('button.addModelPath') }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Desired Parameters -->
|
||
<div class="card border-0 shadow-sm mb-4">
|
||
<div class="card-header"><h2 class="h6 mb-0">{{ t('labels.desiredParameters') }}</h2></div>
|
||
<div class="card-body">
|
||
<div v-for="(param, i) in form.desiredParameters" :key="i" class="row g-2 mb-2 align-items-center">
|
||
<div class="col-md-5">
|
||
<input v-model="param.name" type="text" class="form-control form-control-sm font-monospace" placeholder="Parameter name" />
|
||
</div>
|
||
<div class="col-md-4">
|
||
<input v-model="param.value" type="text" class="form-control form-control-sm" placeholder="Value" />
|
||
</div>
|
||
<div class="col-md-2">
|
||
<select v-model="param.type" class="form-select form-select-sm">
|
||
<option value="string">string</option>
|
||
<option value="int">int</option>
|
||
<option value="unsignedInt">unsignedInt</option>
|
||
<option value="boolean">boolean</option>
|
||
<option value="dateTime">dateTime</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-1 text-end">
|
||
<button type="button" class="btn btn-outline-danger btn-sm" @click="removeParameter(i)">×</button>
|
||
</div>
|
||
</div>
|
||
<button type="button" class="btn btn-outline-secondary btn-sm mt-1" @click="addParameter">
|
||
+ {{ t('button.addParameter') }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Actions -->
|
||
<div class="d-flex gap-2 justify-content-end">
|
||
<button type="button" class="btn btn-outline-secondary" @click="goBack">{{ t('button.cancel') }}</button>
|
||
<button type="submit" class="btn btn-primary">{{ t('button.create') }}</button>
|
||
</div>
|
||
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|