mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-28 22:01:40 +00:00
FIX - allow configurable radio channel jobs (#46)
Mark locked-channel job maps mutable in the configurator schema and initialize new structured list rows from their schema template so required frequency ranges remain valid.
This commit is contained in:
@@ -380,10 +380,10 @@ function toggleOptionalString(event: Event): void {
|
|||||||
function addListRow(): void {
|
function addListRow(): void {
|
||||||
const rows = Array.isArray(props.modelValue) ? [...props.modelValue] : []
|
const rows = Array.isArray(props.modelValue) ? [...props.modelValue] : []
|
||||||
const index = rows.length
|
const index = rows.length
|
||||||
const value = rows.length
|
const value = listTemplate.value
|
||||||
? blankLike(rows[0])
|
? blankFromConfiguratorStructure(listTemplate.value)
|
||||||
: listTemplate.value
|
: rows.length
|
||||||
? blankFromConfiguratorStructure(listTemplate.value)
|
? blankLike(rows[0])
|
||||||
: blankValue(newArrayKind.value)
|
: blankValue(newArrayKind.value)
|
||||||
rows.push(value)
|
rows.push(value)
|
||||||
emit('update:modelValue', rows)
|
emit('update:modelValue', rows)
|
||||||
|
|||||||
@@ -440,6 +440,9 @@ describe('standalone admin panel contracts', () => {
|
|||||||
expect(source).toContain("selectConfiguratorScope('media')")
|
expect(source).toContain("selectConfiguratorScope('media')")
|
||||||
expect(source).not.toContain('class="admin-panel-config-meta"')
|
expect(source).not.toContain('class="admin-panel-config-meta"')
|
||||||
expect(configuratorValueEditor).toContain('function addListRow()')
|
expect(configuratorValueEditor).toContain('function addListRow()')
|
||||||
|
expect(configuratorValueEditor).toMatch(
|
||||||
|
/const value = listTemplate\.value\s+\? blankFromConfiguratorStructure\(listTemplate\.value\)\s+: rows\.length\s+\? blankLike\(rows\[0\]\)/,
|
||||||
|
)
|
||||||
expect(configuratorValueEditor).toContain('function addTableField()')
|
expect(configuratorValueEditor).toContain('function addTableField()')
|
||||||
expect(configuratorValueEditor).toContain(
|
expect(configuratorValueEditor).toContain(
|
||||||
'const canExtendTable = computed(',
|
'const canExtendTable = computed(',
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
import type { AdminConfiguratorStructure } from '@/types/admin'
|
import type { AdminConfiguratorStructure } from '@/types/admin'
|
||||||
import { createMutableTableEntry } from '@/utils/adminConfiguratorDefaults'
|
import {
|
||||||
|
blankFromConfiguratorStructure,
|
||||||
|
createMutableTableEntry,
|
||||||
|
} from '@/utils/adminConfiguratorDefaults'
|
||||||
|
|
||||||
describe('admin configurator defaults', () => {
|
describe('admin configurator defaults', () => {
|
||||||
it('creates a usable company draft from its key and the server defaults', () => {
|
it('creates a usable company draft from its key and the server defaults', () => {
|
||||||
@@ -111,4 +114,33 @@ describe('admin configurator defaults', () => {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('preserves required nested list fields in schema-derived rows', () => {
|
||||||
|
const structure: AdminConfiguratorStructure = {
|
||||||
|
fields: {
|
||||||
|
jobs: {
|
||||||
|
fields: {
|
||||||
|
police: { kind: 'value', valueType: 'boolean' },
|
||||||
|
},
|
||||||
|
kind: 'table',
|
||||||
|
mutableKeys: true,
|
||||||
|
template: { kind: 'value', valueType: 'boolean' },
|
||||||
|
},
|
||||||
|
range: {
|
||||||
|
items: [
|
||||||
|
{ kind: 'value', valueType: 'number' },
|
||||||
|
{ kind: 'value', valueType: 'number' },
|
||||||
|
],
|
||||||
|
kind: 'list',
|
||||||
|
template: { kind: 'value', valueType: 'number' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
kind: 'table',
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(blankFromConfiguratorStructure(structure)).toEqual({
|
||||||
|
jobs: { police: false },
|
||||||
|
range: [0, 0],
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -540,6 +540,25 @@ function buildStructure(value, scope, path) {
|
|||||||
if (scope === 'config' && path === 'Phone.Keybind') {
|
if (scope === 'config' && path === 'Phone.Keybind') {
|
||||||
return { kind: 'optionalString' }
|
return { kind: 'optionalString' }
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
scope === 'config' &&
|
||||||
|
/^Radio\.LockedChannels\.\d+\.jobs$/.test(path) &&
|
||||||
|
value !== null &&
|
||||||
|
typeof value === 'object' &&
|
||||||
|
!Array.isArray(value)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
fields: Object.fromEntries(
|
||||||
|
Object.entries(value).map(([key, child]) => [
|
||||||
|
key,
|
||||||
|
buildStructure(child, scope, `${path}.${key}`),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
kind: 'table',
|
||||||
|
mutableKeys: true,
|
||||||
|
template: { kind: 'value', valueType: 'boolean' },
|
||||||
|
}
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
scope === 'config' &&
|
scope === 'config' &&
|
||||||
path === 'Companies.Definitions' &&
|
path === 'Companies.Definitions' &&
|
||||||
|
|||||||
@@ -193,6 +193,29 @@ describe('admin configurator fixture', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('allows custom jobs in locked radio channel entries', () => {
|
||||||
|
const radio = loadConfiguratorSections()
|
||||||
|
.flatMap((section) => section.fields)
|
||||||
|
.find((field) => field.path === 'Radio')
|
||||||
|
const lockedChannels = radio?.structure?.fields?.LockedChannels
|
||||||
|
const jobs = lockedChannels?.items?.[0]?.fields?.jobs
|
||||||
|
|
||||||
|
expect(jobs).toMatchObject({
|
||||||
|
fields: {
|
||||||
|
ambulance: { kind: 'value', valueType: 'boolean' },
|
||||||
|
police: { kind: 'value', valueType: 'boolean' },
|
||||||
|
},
|
||||||
|
kind: 'table',
|
||||||
|
mutableKeys: true,
|
||||||
|
template: { kind: 'value', valueType: 'boolean' },
|
||||||
|
})
|
||||||
|
expect(lockedChannels?.template?.fields?.jobs).toMatchObject({
|
||||||
|
kind: 'table',
|
||||||
|
mutableKeys: true,
|
||||||
|
template: { kind: 'value', valueType: 'boolean' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('publishes fixed schemas for every empty configurable collection', () => {
|
it('publishes fixed schemas for every empty configurable collection', () => {
|
||||||
const fields = loadConfiguratorSections().flatMap(
|
const fields = loadConfiguratorSections().flatMap(
|
||||||
(section) => section.fields,
|
(section) => section.fields,
|
||||||
|
|||||||
@@ -637,6 +637,21 @@ local function build_structure(value, scope, path)
|
|||||||
if scope == "config" and path == "Phone.Keybind" then
|
if scope == "config" and path == "Phone.Keybind" then
|
||||||
return { kind = "optionalString" }
|
return { kind = "optionalString" }
|
||||||
end
|
end
|
||||||
|
if scope == "config"
|
||||||
|
and path:match("^Radio%.LockedChannels%.%d+%.jobs$")
|
||||||
|
and value_type == "table"
|
||||||
|
then
|
||||||
|
local fields = {}
|
||||||
|
for key, child in pairs(value) do
|
||||||
|
fields[key] = build_structure(child, scope, path .. "." .. tostring(key))
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
fields = fields,
|
||||||
|
kind = "table",
|
||||||
|
mutableKeys = true,
|
||||||
|
template = { kind = "value", valueType = "boolean" },
|
||||||
|
}
|
||||||
|
end
|
||||||
if scope == "config" and path == "Companies.Definitions" and value_type == "table" then
|
if scope == "config" and path == "Companies.Definitions" and value_type == "table" then
|
||||||
local keys = {}
|
local keys = {}
|
||||||
for key in pairs(value) do
|
for key in pairs(value) do
|
||||||
|
|||||||
Reference in New Issue
Block a user