Make gauge/maintenance identifiers per-asset-type and extend to all types

Optional asset identifiers (gauge lab reference, maintenance reference, FQDN)
were global per-identifier and only surfaced on equipment. Now they are
toggleable per asset type and rendered on every asset type.

- settings: replace 3 global identifier toggles with a per-type matrix. New
  keys identifier_<name>_<assettype>_enabled (3 identifiers x 4 types).
  IDENTIFIER_LABELS / IDENTIFIER_ASSETTYPES constants drive the seed (API seed
  and CLI seed settings).
- composable: identifierSettings now exposes isEnabled(name, assettype),
  per-type flag winning over the legacy global key, defaulting on.
- backend writes: computers, network, printers asset create + update now
  accept gaugelabreference and maintenancereference (equipment already did).
  Reads already flowed through Asset.to_dict.
- frontend: Settings page renders an identifier x asset-type toggle matrix.
  Equipment, PC, printer, network forms and detail pages show gauge/maintenance
  (and FQDN where applicable) gated by isEnabled(name, type).

Legacy global identifier_<name>_enabled keys are still honored as a fallback
for older installs. SystemSettings toggles upsert (create on 404) so a deploy
that has not re-seeded still works on first toggle.

144 tests pass, naming/style check green, frontend builds. Verified live:
matrix renders, PC form shows the fields, PUT persists gauge/maintenance on a
PC and reads back.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-06-26 16:34:50 -04:00
parent b567de14ac
commit 37ffb4add5
15 changed files with 330 additions and 109 deletions

View File

@@ -382,50 +382,35 @@
<div class="setting-group">
<p class="setting-description">
Enable or disable optional asset identifiers. When disabled, the identifier
is hidden from asset forms and detail pages across the system.
Enable or disable optional asset identifiers per asset type. When disabled
for a type, the identifier is hidden from that type's forms and detail
pages across the system.
</p>
<div class="setting-row">
<label class="toggle-label">
<span>Gauge Lab Reference</span>
<button
class="toggle-btn"
:class="{ active: settings.identifier_gaugelabreference_enabled }"
@click="toggleSetting('identifier_gaugelabreference_enabled')"
:disabled="saving"
>
<span class="toggle-slider"></span>
</button>
</label>
</div>
<div class="setting-row">
<label class="toggle-label">
<span>Maintenance Reference</span>
<button
class="toggle-btn"
:class="{ active: settings.identifier_maintenancereference_enabled }"
@click="toggleSetting('identifier_maintenancereference_enabled')"
:disabled="saving"
>
<span class="toggle-slider"></span>
</button>
</label>
</div>
<div class="setting-row">
<label class="toggle-label">
<span>FQDN / Hostname</span>
<button
class="toggle-btn"
:class="{ active: settings.identifier_fqdn_enabled }"
@click="toggleSetting('identifier_fqdn_enabled')"
:disabled="saving"
>
<span class="toggle-slider"></span>
</button>
</label>
<div class="table-container">
<table class="identifier-matrix">
<thead>
<tr>
<th>Identifier</th>
<th v-for="col in assetTypeCols" :key="col.key">{{ col.label }}</th>
</tr>
</thead>
<tbody>
<tr v-for="row in identifierRows" :key="row.name">
<td class="identifier-name">{{ row.label }}</td>
<td v-for="col in assetTypeCols" :key="col.key">
<button
class="toggle-btn"
:class="{ active: matrixValue(row.name, col.key) }"
@click="toggleIdentifier(row.name, col.key)"
:disabled="saving"
>
<span class="toggle-slider"></span>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
@@ -463,13 +448,33 @@ const settings = reactive({
saml_acs_url: '',
saml_allow_local_login: true,
saml_auto_create_users: true,
saml_admin_group: '',
// Asset identifiers
identifier_gaugelabreference_enabled: true,
identifier_maintenancereference_enabled: true,
identifier_fqdn_enabled: true
saml_admin_group: ''
})
// Asset identifier matrix: identifier x asset type. Keys follow
// identifier_<name>_<assettype>_enabled. Missing = enabled (default on).
const identifierRows = [
{ name: 'gaugelabreference', label: 'Gauge Lab Reference' },
{ name: 'maintenancereference', label: 'Maintenance Reference' },
{ name: 'fqdn', label: 'FQDN / Hostname' }
]
const assetTypeCols = [
{ key: 'equipment', label: 'Equipment' },
{ key: 'computer', label: 'PC' },
{ key: 'printer', label: 'Printer' },
{ key: 'network_device', label: 'Network' }
]
const identifierMatrix = reactive({})
function identifierKey(name, assettype) {
return `identifier_${name}_${assettype}_enabled`
}
function matrixValue(name, assettype) {
const key = identifierKey(name, assettype)
return key in identifierMatrix ? identifierMatrix[key] : true
}
const loading = ref(true)
const saving = ref(false)
const testingEmail = ref(false)
@@ -530,6 +535,8 @@ async function loadSettings() {
for (const setting of data.data) {
if (setting.key in settings) {
settings[setting.key] = setting.value
} else if (/^identifier_.+_(equipment|computer|printer|network_device)_enabled$/.test(setting.key)) {
identifierMatrix[setting.key] = setting.value !== false
}
}
} catch (e) {
@@ -545,6 +552,41 @@ async function toggleSetting(key) {
await saveSetting(key, newValue)
}
// Toggle a per-type identifier flag. The key may not be seeded yet on older
// installs, so fall back to creating it when the update returns 404.
async function toggleIdentifier(name, assettype) {
const key = identifierKey(name, assettype)
const newValue = !matrixValue(name, assettype)
try {
saving.value = true
error.value = ''
success.value = ''
try {
await settingsApi.update(key, newValue)
} catch (e) {
if (e.response?.status === 404) {
await settingsApi.create({
key,
value: newValue,
valuetype: 'boolean',
category: 'identifiers',
description: `Show the ${name} identifier on ${assettype} assets`
})
} else {
throw e
}
}
identifierMatrix[key] = newValue
success.value = 'Setting saved'
setTimeout(() => { success.value = '' }, 2000)
} catch (e) {
error.value = e.response?.data?.message || 'Failed to save setting'
console.error(e)
} finally {
saving.value = false
}
}
async function saveSetting(key, value) {
try {
saving.value = true
@@ -797,4 +839,31 @@ onMounted(loadSettings)
margin-top: -0.5rem;
margin-left: 0;
}
.identifier-matrix {
width: 100%;
border-collapse: collapse;
}
.identifier-matrix th,
.identifier-matrix td {
padding: 0.6rem 0.75rem;
text-align: center;
border-bottom: 1px solid var(--border);
}
.identifier-matrix th:first-child,
.identifier-matrix td.identifier-name {
text-align: left;
}
.identifier-matrix th {
color: var(--text-light);
font-weight: 600;
font-size: 0.9rem;
}
.identifier-matrix .identifier-name {
color: var(--text);
}
</style>