Files
adam-acs/inertia/components/configuration/CriteriaEditor.vue
T
L HEREEC Kevin INNOV/PTFM 645e845b95
Build & Deploy / build (push) Failing after 35s
Build & Deploy / docker (push) Has been skipped
Build & Deploy / deploy (push) Has been skipped
fix: add /internal routes protected by session for Vue frontend fetches, replace /api/v1/ calls in all Vue components
2026-07-13 14:59:01 +02:00

220 lines
8.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import AutocompleteInput from '~/components/AutocompleteInput.vue'
import type { ConfigurationCriteria, ParameterCriteria } from '#domain/configuration/configuration'
interface Props {
modelValue: ConfigurationCriteria
}
interface Emits {
(e: 'update:modelValue', value: ConfigurationCriteria): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const { t } = useI18n()
const allowedFields = ['manufacturer', 'oui', 'productClass', 'serialNumber'] as const
const suggestions = ref<Record<string, string[]>>({
manufacturer: [],
oui: [],
productClass: [],
serialNumber: [],
modelName: [],
hardwareVersion: [],
softwareVersion: [],
})
onMounted(async () => {
try {
const res = await fetch('/internal/devices/criteria-values')
if (res.ok) {
const data = await res.json()
// API returns plural keys, map to singular keys used by the form fields
suggestions.value = {
...suggestions.value,
manufacturer: data.manufacturers ?? [],
oui: data.ouis ?? [],
productClass: data.productClasses ?? [],
serialNumber: data.serialNumbers ?? [],
modelName: data.modelNames ?? [],
hardwareVersion: data.hardwareVersions ?? [],
softwareVersion: data.softwareVersions ?? [],
}
}
} catch {
// silently fail
}
})
function updateCriteria(field: string, value: string) {
const updated = { ...props.modelValue }
if (value) {
updated[field as keyof ConfigurationCriteria] = value as any
} else {
delete updated[field as keyof ConfigurationCriteria]
}
emit('update:modelValue', updated)
}
// ── Parameter Criteria ──────────────────────────────────────────────────────
const presetParams: { labelKey: string; tr181: string; tr098: string; placeholderKey: string; suggestionsKey: string }[] = [
{ labelKey: 'criteriaEditor.presets.modelName', tr181: 'Device.DeviceInfo.ModelName', tr098: 'InternetGatewayDevice.DeviceInfo.ModelName', placeholderKey: 'criteriaEditor.presets.modelNamePlaceholder', suggestionsKey: 'modelName' },
{ labelKey: 'criteriaEditor.presets.hardwareVersion', tr181: 'Device.DeviceInfo.HardwareVersion', tr098: 'InternetGatewayDevice.DeviceInfo.HardwareVersion', placeholderKey: 'criteriaEditor.presets.hardwareVersionPlaceholder', suggestionsKey: 'hardwareVersion' },
{ labelKey: 'criteriaEditor.presets.softwareVersion', tr181: 'Device.DeviceInfo.SoftwareVersion', tr098: 'InternetGatewayDevice.DeviceInfo.SoftwareVersion', placeholderKey: 'criteriaEditor.presets.softwareVersionPlaceholder', suggestionsKey: 'softwareVersion' },
]
// Paths already covered by preset fields — excluded from custom conditions suggestions
const presetPaths = new Set(presetParams.flatMap((p) => [p.tr181, p.tr098]))
const customSuggestions = computed(() =>
(suggestions.value.parameterNames || []).filter((name) => !presetPaths.has(name))
)
function getPresetValue(preset: typeof presetParams[0]): string {
const list = props.modelValue.parameterCriteria || []
const entry = list.find((p) => p.name === preset.tr181 || p.name === preset.tr098)
return entry ? String(entry.value) : ''
}
function updatePresetParam(preset: typeof presetParams[0], value: string) {
const updated = { ...props.modelValue }
const list = [...(updated.parameterCriteria || [])]
const idx = list.findIndex((p) => p.name === preset.tr181 || p.name === preset.tr098)
if (value) {
if (idx >= 0) {
list[idx] = { name: preset.tr181, value }
} else {
list.push({ name: preset.tr181, value })
}
} else {
if (idx >= 0) list.splice(idx, 1)
}
updated.parameterCriteria = list.length > 0 ? list : undefined
emit('update:modelValue', updated)
}
const paramCriteria = computed<ParameterCriteria[]>(() => props.modelValue.parameterCriteria || [])
function addParamCriteria() {
const updated = { ...props.modelValue }
updated.parameterCriteria = [...(updated.parameterCriteria || []), { name: '', value: '' }]
emit('update:modelValue', updated)
}
function removeParamCriteria(index: number) {
const updated = { ...props.modelValue }
const list = [...(updated.parameterCriteria || [])]
list.splice(index, 1)
updated.parameterCriteria = list.length > 0 ? list : undefined
emit('update:modelValue', updated)
}
function updateParamCriteria(index: number, field: 'name' | 'value', value: string) {
const updated = { ...props.modelValue }
const list = [...(updated.parameterCriteria || [])]
list[index] = { ...list[index], [field]: value }
updated.parameterCriteria = list
emit('update:modelValue', updated)
}
function knownValuesLabel(count: number): string {
return count > 1 ? t('criteriaEditor.knownValues') : t('criteriaEditor.knownValue')
}
</script>
<template>
<!-- DeviceId criteria -->
<p class="fw-semibold small mb-2">{{ t('criteriaEditor.deviceId') }}</p>
<div class="row g-3">
<div v-for="field in allowedFields" :key="field" class="col-md-6">
<label :for="`criteria-${field}`" class="form-label">{{ t(`criteriaEditor.fields.${field}.label`) }}</label>
<AutocompleteInput
:id="`criteria-${field}`"
:model-value="modelValue[field] || ''"
:suggestions="suggestions[field]"
:placeholder="t(`criteriaEditor.fields.${field}.placeholder`)"
monospace
@update:model-value="updateCriteria(field, $event)"
/>
<div class="form-text">
{{ t(`criteriaEditor.fields.${field}.help`) }}
<span v-if="suggestions[field]?.length" class="text-muted">
· {{ suggestions[field].length }} {{ knownValuesLabel(suggestions[field].length) }}
</span>
</div>
</div>
</div>
<!-- Parameter Criteria — Preset fields -->
<div class="mt-4">
<p class="fw-semibold small mb-2">{{ t('criteriaEditor.standardParams') }}</p>
<div class="row g-3 mb-3">
<div v-for="preset in presetParams" :key="preset.tr181" class="col-md-6">
<label class="form-label">{{ t(preset.labelKey) }}</label>
<AutocompleteInput
:model-value="getPresetValue(preset)"
:suggestions="suggestions[preset.suggestionsKey]"
:placeholder="t(preset.placeholderKey)"
monospace
@update:model-value="updatePresetParam(preset, $event)"
/>
<div class="form-text text-muted" style="font-size:0.7rem">
{{ preset.tr181 }}
<span v-if="suggestions[preset.suggestionsKey]?.length" class="ms-1">
· {{ suggestions[preset.suggestionsKey].length }} {{ knownValuesLabel(suggestions[preset.suggestionsKey].length) }}
</span>
</div>
</div>
</div>
</div>
<!-- Parameter Criteria — conditions libres -->
<div class="mt-4">
<div class="d-flex align-items-center justify-content-between mb-2">
<div>
<span class="fw-semibold small">{{ t('criteriaEditor.customCriteria') }}</span>
<span class="form-text ms-2">{{ t('criteriaEditor.customCriteriaHelp') }}</span>
</div>
<button type="button" class="btn btn-outline-secondary btn-sm" @click="addParamCriteria">
{{ t('criteriaEditor.addCondition') }}
</button>
</div>
<div v-if="paramCriteria.length === 0" class="text-muted small fst-italic">
{{ t('criteriaEditor.noConditions') }}
</div>
<div v-for="(pc, idx) in paramCriteria" :key="idx" class="d-flex align-items-center gap-2 mb-2">
<div class="flex-grow-1">
<AutocompleteInput
:model-value="pc.name"
:suggestions="customSuggestions"
placeholder="e.g. Device.DeviceInfo.Manufacturer"
monospace
@update:model-value="updateParamCriteria(idx, 'name', $event)"
/>
</div>
<span class="text-muted small">=</span>
<input
type="text"
class="form-control form-control-sm font-monospace"
style="max-width: 160px"
:value="String(pc.value)"
placeholder="e.g. true"
@input="updateParamCriteria(idx, 'value', ($event.target as HTMLInputElement).value)"
/>
<button type="button" class="btn btn-outline-danger btn-sm" :title="t('button.delete')" @click="removeParamCriteria(idx)">
✕
</button>
</div>
<div v-if="paramCriteria.length > 0" class="form-text mt-1">
ℹ️ {{ t('criteriaEditor.paramCriteriaWarning') }}
</div>
</div>
</template>