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:
@@ -1,36 +1,51 @@
|
|||||||
// Global enable/disable flags for optional asset identifiers, read from the
|
// Per-type enable/disable flags for optional asset identifiers, read from the
|
||||||
// settings table. Loaded once and shared across components. Defaults to
|
// settings table. Keys follow identifier_<name>_<assettype>_enabled. A legacy
|
||||||
// enabled when a flag is missing so a fresh install shows the identifiers.
|
// global key identifier_<name>_enabled is honored as a fallback for older
|
||||||
|
// installs. Missing = enabled, so a fresh install shows every identifier.
|
||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
import { settingsApi } from '../api'
|
import { settingsApi } from '../api'
|
||||||
|
|
||||||
const identifierflags = reactive({
|
// scope[name][assettype] = boolean. legacy[name] = boolean (old global flag).
|
||||||
gaugelabreference: true,
|
const state = reactive({
|
||||||
maintenancereference: true,
|
scope: {},
|
||||||
fqdn: true,
|
legacy: {},
|
||||||
loaded: false
|
loaded: false
|
||||||
})
|
})
|
||||||
|
|
||||||
let inflight = null
|
let inflight = null
|
||||||
|
|
||||||
export function useIdentifierFlags() {
|
function loadFlags() {
|
||||||
if (!identifierflags.loaded && !inflight) {
|
if (!state.loaded && !inflight) {
|
||||||
inflight = settingsApi.list()
|
inflight = settingsApi.list()
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
const map = {}
|
;(data.data || []).forEach(s => {
|
||||||
;(data.data || []).forEach(s => { map[s.key] = s.value })
|
const match = /^identifier_(.+?)(?:_(equipment|computer|printer|network_device))?_enabled$/.exec(s.key)
|
||||||
if ('identifier_gaugelabreference_enabled' in map) {
|
if (!match) return
|
||||||
identifierflags.gaugelabreference = map.identifier_gaugelabreference_enabled !== false
|
const name = match[1]
|
||||||
}
|
const assettype = match[2]
|
||||||
if ('identifier_maintenancereference_enabled' in map) {
|
if (assettype) {
|
||||||
identifierflags.maintenancereference = map.identifier_maintenancereference_enabled !== false
|
if (!state.scope[name]) state.scope[name] = {}
|
||||||
}
|
state.scope[name][assettype] = s.value !== false
|
||||||
if ('identifier_fqdn_enabled' in map) {
|
} else {
|
||||||
identifierflags.fqdn = map.identifier_fqdn_enabled !== false
|
state.legacy[name] = s.value !== false
|
||||||
}
|
}
|
||||||
identifierflags.loaded = true
|
})
|
||||||
|
state.loaded = true
|
||||||
})
|
})
|
||||||
.catch(() => { identifierflags.loaded = true })
|
.catch(() => { state.loaded = true })
|
||||||
}
|
}
|
||||||
return identifierflags
|
}
|
||||||
|
|
||||||
|
// True when identifier `name` should show on `assettype`. Per-type flag wins,
|
||||||
|
// then the legacy global flag, then default-on.
|
||||||
|
function isEnabled(name, assettype) {
|
||||||
|
const perType = state.scope[name]
|
||||||
|
if (perType && assettype in perType) return perType[assettype]
|
||||||
|
if (name in state.legacy) return state.legacy[name]
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useIdentifierFlags() {
|
||||||
|
loadFlags()
|
||||||
|
return { state, isEnabled }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,11 +68,11 @@
|
|||||||
<span class="info-label">Name</span>
|
<span class="info-label">Name</span>
|
||||||
<span class="info-value">{{ equipment.name }}</span>
|
<span class="info-value">{{ equipment.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row" v-if="identifierflags.gaugelabreference && equipment.gaugelabreference">
|
<div class="info-row" v-if="isEnabled('gaugelabreference', 'equipment') && equipment.gaugelabreference">
|
||||||
<span class="info-label">Gauge Lab Reference</span>
|
<span class="info-label">Gauge Lab Reference</span>
|
||||||
<span class="info-value mono">{{ equipment.gaugelabreference }}</span>
|
<span class="info-value mono">{{ equipment.gaugelabreference }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row" v-if="identifierflags.maintenancereference && equipment.maintenancereference">
|
<div class="info-row" v-if="isEnabled('maintenancereference', 'equipment') && equipment.maintenancereference">
|
||||||
<span class="info-label">Maintenance Reference</span>
|
<span class="info-label">Maintenance Reference</span>
|
||||||
<span class="info-value mono">{{ equipment.maintenancereference }}</span>
|
<span class="info-value mono">{{ equipment.maintenancereference }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -240,7 +240,7 @@ import LocationMapTooltip from '../../components/LocationMapTooltip.vue'
|
|||||||
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const identifierflags = useIdentifierFlags()
|
const { isEnabled } = useIdentifierFlags()
|
||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const equipment = ref(null)
|
const equipment = ref(null)
|
||||||
|
|||||||
@@ -34,8 +34,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-row" v-if="identifierflags.gaugelabreference || identifierflags.maintenancereference">
|
<div class="form-row" v-if="isEnabled('gaugelabreference', 'equipment') || isEnabled('maintenancereference', 'equipment')">
|
||||||
<div class="form-group" v-if="identifierflags.gaugelabreference">
|
<div class="form-group" v-if="isEnabled('gaugelabreference', 'equipment')">
|
||||||
<label for="gaugelabreference">Gauge Lab Reference</label>
|
<label for="gaugelabreference">Gauge Lab Reference</label>
|
||||||
<input
|
<input
|
||||||
id="gaugelabreference"
|
id="gaugelabreference"
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
<small class="form-help">Authoritative gauge lab asset reference (if tracked)</small>
|
<small class="form-help">Authoritative gauge lab asset reference (if tracked)</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" v-if="identifierflags.maintenancereference">
|
<div class="form-group" v-if="isEnabled('maintenancereference', 'equipment')">
|
||||||
<label for="maintenancereference">Maintenance Reference</label>
|
<label for="maintenancereference">Maintenance Reference</label>
|
||||||
<input
|
<input
|
||||||
id="maintenancereference"
|
id="maintenancereference"
|
||||||
@@ -358,7 +358,7 @@ import Modal from '../../components/Modal.vue'
|
|||||||
import { currentTheme } from '../../stores/theme'
|
import { currentTheme } from '../../stores/theme'
|
||||||
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
||||||
|
|
||||||
const identifierflags = useIdentifierFlags()
|
const { isEnabled } = useIdentifierFlags()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|||||||
@@ -111,6 +111,14 @@
|
|||||||
<span class="info-label">Serial Number</span>
|
<span class="info-label">Serial Number</span>
|
||||||
<span class="info-value mono">{{ device.serialnumber || '-' }}</span>
|
<span class="info-value mono">{{ device.serialnumber || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="info-row" v-if="isEnabled('gaugelabreference', 'network_device') && device.gaugelabreference">
|
||||||
|
<span class="info-label">Gauge Lab Reference</span>
|
||||||
|
<span class="info-value mono">{{ device.gaugelabreference }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row" v-if="isEnabled('maintenancereference', 'network_device') && device.maintenancereference">
|
||||||
|
<span class="info-label">Maintenance Reference</span>
|
||||||
|
<span class="info-value mono">{{ device.maintenancereference }}</span>
|
||||||
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<span class="info-label">Vendor</span>
|
<span class="info-label">Vendor</span>
|
||||||
<span class="info-value">{{ device.networkdevice?.vendorname || '-' }}</span>
|
<span class="info-value">{{ device.networkdevice?.vendorname || '-' }}</span>
|
||||||
@@ -181,6 +189,9 @@ import { Network, Router, Shield, Wifi, Camera, Server, Server as Rack, Globe }
|
|||||||
import { useAuthStore } from '../../stores/auth'
|
import { useAuthStore } from '../../stores/auth'
|
||||||
import { networkApi } from '../../api'
|
import { networkApi } from '../../api'
|
||||||
import AssetRelationships from '../../components/AssetRelationships.vue'
|
import AssetRelationships from '../../components/AssetRelationships.vue'
|
||||||
|
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
||||||
|
|
||||||
|
const { isEnabled } = useIdentifierFlags()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|||||||
@@ -54,6 +54,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row" v-if="isEnabled('gaugelabreference', 'network_device') || isEnabled('maintenancereference', 'network_device')">
|
||||||
|
<div class="form-group" v-if="isEnabled('gaugelabreference', 'network_device')">
|
||||||
|
<label for="gaugelabreference">Gauge Lab Reference</label>
|
||||||
|
<input
|
||||||
|
id="gaugelabreference"
|
||||||
|
v-model="form.gaugelabreference"
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" v-if="isEnabled('maintenancereference', 'network_device')">
|
||||||
|
<label for="maintenancereference">Maintenance Reference</label>
|
||||||
|
<input
|
||||||
|
id="maintenancereference"
|
||||||
|
v-model="form.maintenancereference"
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="locationid">Location</label>
|
<label for="locationid">Location</label>
|
||||||
@@ -229,6 +250,9 @@ import {
|
|||||||
assetsApi,
|
assetsApi,
|
||||||
businessunitsApi
|
businessunitsApi
|
||||||
} from '../../api'
|
} from '../../api'
|
||||||
|
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
||||||
|
|
||||||
|
const { isEnabled } = useIdentifierFlags()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -240,6 +264,8 @@ const form = ref({
|
|||||||
assetnumber: '',
|
assetnumber: '',
|
||||||
name: '',
|
name: '',
|
||||||
serialnumber: '',
|
serialnumber: '',
|
||||||
|
gaugelabreference: '',
|
||||||
|
maintenancereference: '',
|
||||||
statusid: '',
|
statusid: '',
|
||||||
locationid: '',
|
locationid: '',
|
||||||
businessunitid: '',
|
businessunitid: '',
|
||||||
@@ -332,6 +358,8 @@ async function loadDevice() {
|
|||||||
form.value.assetnumber = data.assetnumber || ''
|
form.value.assetnumber = data.assetnumber || ''
|
||||||
form.value.name = data.name || ''
|
form.value.name = data.name || ''
|
||||||
form.value.serialnumber = data.serialnumber || ''
|
form.value.serialnumber = data.serialnumber || ''
|
||||||
|
form.value.gaugelabreference = data.gaugelabreference || ''
|
||||||
|
form.value.maintenancereference = data.maintenancereference || ''
|
||||||
form.value.statusid = data.statusid || ''
|
form.value.statusid = data.statusid || ''
|
||||||
form.value.locationid = data.locationid || ''
|
form.value.locationid = data.locationid || ''
|
||||||
form.value.businessunitid = data.businessunitid || ''
|
form.value.businessunitid = data.businessunitid || ''
|
||||||
@@ -365,6 +393,8 @@ async function submitForm() {
|
|||||||
assetnumber: form.value.assetnumber,
|
assetnumber: form.value.assetnumber,
|
||||||
name: form.value.name || null,
|
name: form.value.name || null,
|
||||||
serialnumber: form.value.serialnumber || null,
|
serialnumber: form.value.serialnumber || null,
|
||||||
|
gaugelabreference: form.value.gaugelabreference || null,
|
||||||
|
maintenancereference: form.value.maintenancereference || null,
|
||||||
statusid: form.value.statusid || null,
|
statusid: form.value.statusid || null,
|
||||||
locationid: form.value.locationid || null,
|
locationid: form.value.locationid || null,
|
||||||
businessunitid: form.value.businessunitid || null,
|
businessunitid: form.value.businessunitid || null,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<div class="hero-content">
|
<div class="hero-content">
|
||||||
<div class="hero-title">
|
<div class="hero-title">
|
||||||
<h1>{{ computer.assetnumber }}</h1>
|
<h1>{{ computer.assetnumber }}</h1>
|
||||||
<span v-if="identifierflags.fqdn && computer.computer?.hostname" class="hero-alias">{{ computer.computer.hostname }}</span>
|
<span v-if="isEnabled('fqdn', 'computer') && computer.computer?.hostname" class="hero-alias">{{ computer.computer.hostname }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="hero-meta">
|
<div class="hero-meta">
|
||||||
<span class="badge badge-lg badge-info">Computer</span>
|
<span class="badge badge-lg badge-info">Computer</span>
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
<span class="info-label">Name</span>
|
<span class="info-label">Name</span>
|
||||||
<span class="info-value">{{ computer.name }}</span>
|
<span class="info-value">{{ computer.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row" v-if="identifierflags.fqdn && computer.computer?.hostname">
|
<div class="info-row" v-if="isEnabled('fqdn', 'computer') && computer.computer?.hostname">
|
||||||
<span class="info-label">Hostname</span>
|
<span class="info-label">Hostname</span>
|
||||||
<span class="info-value mono">{{ computer.computer.hostname }}</span>
|
<span class="info-value mono">{{ computer.computer.hostname }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -65,6 +65,14 @@
|
|||||||
<span class="info-label">Serial Number</span>
|
<span class="info-label">Serial Number</span>
|
||||||
<span class="info-value mono">{{ computer.serialnumber }}</span>
|
<span class="info-value mono">{{ computer.serialnumber }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="info-row" v-if="isEnabled('gaugelabreference', 'computer') && computer.gaugelabreference">
|
||||||
|
<span class="info-label">Gauge Lab Reference</span>
|
||||||
|
<span class="info-value mono">{{ computer.gaugelabreference }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row" v-if="isEnabled('maintenancereference', 'computer') && computer.maintenancereference">
|
||||||
|
<span class="info-label">Maintenance Reference</span>
|
||||||
|
<span class="info-value mono">{{ computer.maintenancereference }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -210,7 +218,7 @@ import LocationMapTooltip from '../../components/LocationMapTooltip.vue'
|
|||||||
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const identifierflags = useIdentifierFlags()
|
const { isEnabled } = useIdentifierFlags()
|
||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const computer = ref(null)
|
const computer = ref(null)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group" v-if="identifierflags.fqdn">
|
<div class="form-group" v-if="isEnabled('fqdn', 'computer')">
|
||||||
<label for="hostname">Hostname</label>
|
<label for="hostname">Hostname</label>
|
||||||
<input
|
<input
|
||||||
id="hostname"
|
id="hostname"
|
||||||
@@ -55,6 +55,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row" v-if="isEnabled('gaugelabreference', 'computer') || isEnabled('maintenancereference', 'computer')">
|
||||||
|
<div class="form-group" v-if="isEnabled('gaugelabreference', 'computer')">
|
||||||
|
<label for="gaugelabreference">Gauge Lab Reference</label>
|
||||||
|
<input
|
||||||
|
id="gaugelabreference"
|
||||||
|
v-model="form.gaugelabreference"
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" v-if="isEnabled('maintenancereference', 'computer')">
|
||||||
|
<label for="maintenancereference">Maintenance Reference</label>
|
||||||
|
<input
|
||||||
|
id="maintenancereference"
|
||||||
|
v-model="form.maintenancereference"
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="machinetypeid">PC Type *</label>
|
<label for="machinetypeid">PC Type *</label>
|
||||||
@@ -276,7 +298,7 @@ import Modal from '../../components/Modal.vue'
|
|||||||
import { currentTheme } from '../../stores/theme'
|
import { currentTheme } from '../../stores/theme'
|
||||||
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
||||||
|
|
||||||
const identifierflags = useIdentifierFlags()
|
const { isEnabled } = useIdentifierFlags()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -301,6 +323,8 @@ const form = ref({
|
|||||||
alias: '',
|
alias: '',
|
||||||
hostname: '',
|
hostname: '',
|
||||||
serialnumber: '',
|
serialnumber: '',
|
||||||
|
gaugelabreference: '',
|
||||||
|
maintenancereference: '',
|
||||||
machinetypeid: '',
|
machinetypeid: '',
|
||||||
statusid: '',
|
statusid: '',
|
||||||
vendorid: '',
|
vendorid: '',
|
||||||
@@ -371,6 +395,8 @@ onMounted(async () => {
|
|||||||
alias: pc.name && pc.name.toUpperCase() !== 'NONE' ? pc.name : '',
|
alias: pc.name && pc.name.toUpperCase() !== 'NONE' ? pc.name : '',
|
||||||
hostname: ext.hostname || '',
|
hostname: ext.hostname || '',
|
||||||
serialnumber: pc.serialnumber || '',
|
serialnumber: pc.serialnumber || '',
|
||||||
|
gaugelabreference: pc.gaugelabreference || '',
|
||||||
|
maintenancereference: pc.maintenancereference || '',
|
||||||
machinetypeid: ext.computertypeid || '',
|
machinetypeid: ext.computertypeid || '',
|
||||||
statusid: pc.statusid || '',
|
statusid: pc.statusid || '',
|
||||||
vendorid: ext.vendorid || '',
|
vendorid: ext.vendorid || '',
|
||||||
@@ -423,6 +449,8 @@ async function savePC() {
|
|||||||
assetnumber: form.value.machinenumber,
|
assetnumber: form.value.machinenumber,
|
||||||
hostname: form.value.hostname || null,
|
hostname: form.value.hostname || null,
|
||||||
serialnumber: form.value.serialnumber || null,
|
serialnumber: form.value.serialnumber || null,
|
||||||
|
gaugelabreference: form.value.gaugelabreference || null,
|
||||||
|
maintenancereference: form.value.maintenancereference || null,
|
||||||
computertypeid: form.value.machinetypeid || null,
|
computertypeid: form.value.machinetypeid || null,
|
||||||
statusid: form.value.statusid || null,
|
statusid: form.value.statusid || null,
|
||||||
vendorid: form.value.vendorid || null,
|
vendorid: form.value.vendorid || null,
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
<span class="info-label">Windows Name</span>
|
<span class="info-label">Windows Name</span>
|
||||||
<span class="info-value mono">{{ printer.printer.windowsname }}</span>
|
<span class="info-value mono">{{ printer.printer.windowsname }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row" v-if="identifierflags.fqdn && printer.printer?.hostname">
|
<div class="info-row" v-if="isEnabled('fqdn', 'printer') && printer.printer?.hostname">
|
||||||
<span class="info-label">Hostname / FQDN</span>
|
<span class="info-label">Hostname / FQDN</span>
|
||||||
<span class="info-value mono">{{ printer.printer.hostname }}</span>
|
<span class="info-value mono">{{ printer.printer.hostname }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -75,6 +75,14 @@
|
|||||||
<span class="info-label">Serial Number</span>
|
<span class="info-label">Serial Number</span>
|
||||||
<span class="info-value mono">{{ printer.serialnumber }}</span>
|
<span class="info-value mono">{{ printer.serialnumber }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="info-row" v-if="isEnabled('gaugelabreference', 'printer') && printer.gaugelabreference">
|
||||||
|
<span class="info-label">Gauge Lab Reference</span>
|
||||||
|
<span class="info-value mono">{{ printer.gaugelabreference }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row" v-if="isEnabled('maintenancereference', 'printer') && printer.maintenancereference">
|
||||||
|
<span class="info-label">Maintenance Reference</span>
|
||||||
|
<span class="info-value mono">{{ printer.maintenancereference }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -248,7 +256,7 @@ import LocationMapTooltip from '../../components/LocationMapTooltip.vue'
|
|||||||
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const identifierflags = useIdentifierFlags()
|
const { isEnabled } = useIdentifierFlags()
|
||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const printer = ref(null)
|
const printer = ref(null)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group" v-if="identifierflags.fqdn">
|
<div class="form-group" v-if="isEnabled('fqdn', 'printer')">
|
||||||
<label for="hostname">Hostname (FQDN)</label>
|
<label for="hostname">Hostname (FQDN)</label>
|
||||||
<input
|
<input
|
||||||
id="hostname"
|
id="hostname"
|
||||||
@@ -61,6 +61,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row" v-if="isEnabled('gaugelabreference', 'printer') || isEnabled('maintenancereference', 'printer')">
|
||||||
|
<div class="form-group" v-if="isEnabled('gaugelabreference', 'printer')">
|
||||||
|
<label for="gaugelabreference">Gauge Lab Reference</label>
|
||||||
|
<input
|
||||||
|
id="gaugelabreference"
|
||||||
|
v-model="form.gaugelabreference"
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" v-if="isEnabled('maintenancereference', 'printer')">
|
||||||
|
<label for="maintenancereference">Maintenance Reference</label>
|
||||||
|
<input
|
||||||
|
id="maintenancereference"
|
||||||
|
v-model="form.maintenancereference"
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="machinetypeid">Printer Type *</label>
|
<label for="machinetypeid">Printer Type *</label>
|
||||||
@@ -273,7 +295,7 @@ import Modal from '../../components/Modal.vue'
|
|||||||
import { currentTheme } from '../../stores/theme'
|
import { currentTheme } from '../../stores/theme'
|
||||||
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
||||||
|
|
||||||
const identifierflags = useIdentifierFlags()
|
const { isEnabled } = useIdentifierFlags()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -293,6 +315,8 @@ const form = ref({
|
|||||||
alias: '',
|
alias: '',
|
||||||
hostname: '',
|
hostname: '',
|
||||||
serialnumber: '',
|
serialnumber: '',
|
||||||
|
gaugelabreference: '',
|
||||||
|
maintenancereference: '',
|
||||||
machinetypeid: '',
|
machinetypeid: '',
|
||||||
statusid: '',
|
statusid: '',
|
||||||
vendorid: '',
|
vendorid: '',
|
||||||
@@ -462,6 +486,8 @@ onMounted(async () => {
|
|||||||
alias: printer.name && printer.name.toUpperCase() !== 'NONE' ? printer.name : '',
|
alias: printer.name && printer.name.toUpperCase() !== 'NONE' ? printer.name : '',
|
||||||
hostname: ext.hostname || '',
|
hostname: ext.hostname || '',
|
||||||
serialnumber: printer.serialnumber || '',
|
serialnumber: printer.serialnumber || '',
|
||||||
|
gaugelabreference: printer.gaugelabreference || '',
|
||||||
|
maintenancereference: printer.maintenancereference || '',
|
||||||
machinetypeid: ext.printertypeid || '',
|
machinetypeid: ext.printertypeid || '',
|
||||||
statusid: printer.statusid || '',
|
statusid: printer.statusid || '',
|
||||||
vendorid: ext.vendorid || '',
|
vendorid: ext.vendorid || '',
|
||||||
@@ -520,6 +546,8 @@ async function savePrinter() {
|
|||||||
windowsname: form.value.machinenumber || null,
|
windowsname: form.value.machinenumber || null,
|
||||||
hostname: form.value.hostname || null,
|
hostname: form.value.hostname || null,
|
||||||
serialnumber: form.value.serialnumber || null,
|
serialnumber: form.value.serialnumber || null,
|
||||||
|
gaugelabreference: form.value.gaugelabreference || null,
|
||||||
|
maintenancereference: form.value.maintenancereference || null,
|
||||||
printertypeid: form.value.machinetypeid || null,
|
printertypeid: form.value.machinetypeid || null,
|
||||||
statusid: form.value.statusid || null,
|
statusid: form.value.statusid || null,
|
||||||
vendorid: form.value.vendorid || null,
|
vendorid: form.value.vendorid || null,
|
||||||
|
|||||||
@@ -382,50 +382,35 @@
|
|||||||
|
|
||||||
<div class="setting-group">
|
<div class="setting-group">
|
||||||
<p class="setting-description">
|
<p class="setting-description">
|
||||||
Enable or disable optional asset identifiers. When disabled, the identifier
|
Enable or disable optional asset identifiers per asset type. When disabled
|
||||||
is hidden from asset forms and detail pages across the system.
|
for a type, the identifier is hidden from that type's forms and detail
|
||||||
|
pages across the system.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="setting-row">
|
<div class="table-container">
|
||||||
<label class="toggle-label">
|
<table class="identifier-matrix">
|
||||||
<span>Gauge Lab Reference</span>
|
<thead>
|
||||||
<button
|
<tr>
|
||||||
class="toggle-btn"
|
<th>Identifier</th>
|
||||||
:class="{ active: settings.identifier_gaugelabreference_enabled }"
|
<th v-for="col in assetTypeCols" :key="col.key">{{ col.label }}</th>
|
||||||
@click="toggleSetting('identifier_gaugelabreference_enabled')"
|
</tr>
|
||||||
:disabled="saving"
|
</thead>
|
||||||
>
|
<tbody>
|
||||||
<span class="toggle-slider"></span>
|
<tr v-for="row in identifierRows" :key="row.name">
|
||||||
</button>
|
<td class="identifier-name">{{ row.label }}</td>
|
||||||
</label>
|
<td v-for="col in assetTypeCols" :key="col.key">
|
||||||
</div>
|
<button
|
||||||
|
class="toggle-btn"
|
||||||
<div class="setting-row">
|
:class="{ active: matrixValue(row.name, col.key) }"
|
||||||
<label class="toggle-label">
|
@click="toggleIdentifier(row.name, col.key)"
|
||||||
<span>Maintenance Reference</span>
|
:disabled="saving"
|
||||||
<button
|
>
|
||||||
class="toggle-btn"
|
<span class="toggle-slider"></span>
|
||||||
:class="{ active: settings.identifier_maintenancereference_enabled }"
|
</button>
|
||||||
@click="toggleSetting('identifier_maintenancereference_enabled')"
|
</td>
|
||||||
:disabled="saving"
|
</tr>
|
||||||
>
|
</tbody>
|
||||||
<span class="toggle-slider"></span>
|
</table>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -463,13 +448,33 @@ const settings = reactive({
|
|||||||
saml_acs_url: '',
|
saml_acs_url: '',
|
||||||
saml_allow_local_login: true,
|
saml_allow_local_login: true,
|
||||||
saml_auto_create_users: true,
|
saml_auto_create_users: true,
|
||||||
saml_admin_group: '',
|
saml_admin_group: ''
|
||||||
// Asset identifiers
|
|
||||||
identifier_gaugelabreference_enabled: true,
|
|
||||||
identifier_maintenancereference_enabled: true,
|
|
||||||
identifier_fqdn_enabled: true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 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 loading = ref(true)
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
const testingEmail = ref(false)
|
const testingEmail = ref(false)
|
||||||
@@ -530,6 +535,8 @@ async function loadSettings() {
|
|||||||
for (const setting of data.data) {
|
for (const setting of data.data) {
|
||||||
if (setting.key in settings) {
|
if (setting.key in settings) {
|
||||||
settings[setting.key] = setting.value
|
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) {
|
} catch (e) {
|
||||||
@@ -545,6 +552,41 @@ async function toggleSetting(key) {
|
|||||||
await saveSetting(key, newValue)
|
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) {
|
async function saveSetting(key, value) {
|
||||||
try {
|
try {
|
||||||
saving.value = true
|
saving.value = true
|
||||||
@@ -797,4 +839,31 @@ onMounted(loadSettings)
|
|||||||
margin-top: -0.5rem;
|
margin-top: -0.5rem;
|
||||||
margin-left: 0;
|
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>
|
</style>
|
||||||
|
|||||||
@@ -328,6 +328,8 @@ def create_computer():
|
|||||||
assetnumber=data['assetnumber'],
|
assetnumber=data['assetnumber'],
|
||||||
name=data.get('name'),
|
name=data.get('name'),
|
||||||
serialnumber=data.get('serialnumber'),
|
serialnumber=data.get('serialnumber'),
|
||||||
|
gaugelabreference=data.get('gaugelabreference'),
|
||||||
|
maintenancereference=data.get('maintenancereference'),
|
||||||
assettypeid=computer_type.assettypeid,
|
assettypeid=computer_type.assettypeid,
|
||||||
statusid=data.get('statusid', 1),
|
statusid=data.get('statusid', 1),
|
||||||
locationid=data.get('locationid'),
|
locationid=data.get('locationid'),
|
||||||
@@ -424,7 +426,8 @@ def update_computer(computer_id: int):
|
|||||||
changes = {}
|
changes = {}
|
||||||
|
|
||||||
# Update asset fields
|
# Update asset fields
|
||||||
asset_fields = ['assetnumber', 'name', 'serialnumber', 'statusid',
|
asset_fields = ['assetnumber', 'name', 'serialnumber', 'gaugelabreference',
|
||||||
|
'maintenancereference', 'statusid',
|
||||||
'locationid', 'businessunitid', 'mapx', 'mapy', 'notes', 'isactive']
|
'locationid', 'businessunitid', 'mapx', 'mapy', 'notes', 'isactive']
|
||||||
for key in asset_fields:
|
for key in asset_fields:
|
||||||
if key in data:
|
if key in data:
|
||||||
|
|||||||
@@ -324,6 +324,8 @@ def create_network_device():
|
|||||||
assetnumber=data['assetnumber'],
|
assetnumber=data['assetnumber'],
|
||||||
name=data.get('name'),
|
name=data.get('name'),
|
||||||
serialnumber=data.get('serialnumber'),
|
serialnumber=data.get('serialnumber'),
|
||||||
|
gaugelabreference=data.get('gaugelabreference'),
|
||||||
|
maintenancereference=data.get('maintenancereference'),
|
||||||
assettypeid=network_type.assettypeid,
|
assettypeid=network_type.assettypeid,
|
||||||
statusid=data.get('statusid', 1),
|
statusid=data.get('statusid', 1),
|
||||||
locationid=data.get('locationid'),
|
locationid=data.get('locationid'),
|
||||||
@@ -406,7 +408,8 @@ def update_network_device(device_id: int):
|
|||||||
changes = {}
|
changes = {}
|
||||||
|
|
||||||
# Update asset fields
|
# Update asset fields
|
||||||
asset_fields = ['assetnumber', 'name', 'serialnumber', 'statusid',
|
asset_fields = ['assetnumber', 'name', 'serialnumber', 'gaugelabreference',
|
||||||
|
'maintenancereference', 'statusid',
|
||||||
'locationid', 'businessunitid', 'mapx', 'mapy', 'notes', 'isactive']
|
'locationid', 'businessunitid', 'mapx', 'mapy', 'notes', 'isactive']
|
||||||
for key in asset_fields:
|
for key in asset_fields:
|
||||||
if key in data:
|
if key in data:
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ def create_printer():
|
|||||||
assetnumber=data['assetnumber'],
|
assetnumber=data['assetnumber'],
|
||||||
name=data.get('name'),
|
name=data.get('name'),
|
||||||
serialnumber=data.get('serialnumber'),
|
serialnumber=data.get('serialnumber'),
|
||||||
|
gaugelabreference=data.get('gaugelabreference'),
|
||||||
|
maintenancereference=data.get('maintenancereference'),
|
||||||
assettypeid=printer_type.assettypeid,
|
assettypeid=printer_type.assettypeid,
|
||||||
statusid=data.get('statusid', 1),
|
statusid=data.get('statusid', 1),
|
||||||
locationid=data.get('locationid'),
|
locationid=data.get('locationid'),
|
||||||
@@ -364,8 +366,9 @@ def update_printer(printer_id: int):
|
|||||||
http_code=409
|
http_code=409
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update asset fields (gauge lab / maintenance refs are equipment-only)
|
# Update asset fields (optional identifiers gated per-type in Settings)
|
||||||
asset_fields = ['assetnumber', 'name', 'serialnumber', 'statusid',
|
asset_fields = ['assetnumber', 'name', 'serialnumber', 'gaugelabreference',
|
||||||
|
'maintenancereference', 'statusid',
|
||||||
'locationid', 'businessunitid', 'mapx', 'mapy',
|
'locationid', 'businessunitid', 'mapx', 'mapy',
|
||||||
'notes', 'isactive']
|
'notes', 'isactive']
|
||||||
for key in asset_fields:
|
for key in asset_fields:
|
||||||
|
|||||||
@@ -361,6 +361,18 @@ def seed_settings():
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Asset identifier toggles, per identifier AND per asset type (ADR-001).
|
||||||
|
from shopdb.core.api.settings import IDENTIFIER_LABELS, IDENTIFIER_ASSETTYPES
|
||||||
|
for name, label in IDENTIFIER_LABELS.items():
|
||||||
|
for assettype in IDENTIFIER_ASSETTYPES:
|
||||||
|
defaults.append({
|
||||||
|
'key': f'identifier_{name}_{assettype}_enabled',
|
||||||
|
'value': 'true',
|
||||||
|
'valuetype': 'boolean',
|
||||||
|
'category': 'identifiers',
|
||||||
|
'description': f'Show the {label} identifier on {assettype} assets',
|
||||||
|
})
|
||||||
|
|
||||||
created = 0
|
created = 0
|
||||||
for d in defaults:
|
for d in defaults:
|
||||||
if not Setting.query.filter_by(key=d['key']).first():
|
if not Setting.query.filter_by(key=d['key']).first():
|
||||||
|
|||||||
@@ -17,6 +17,16 @@ SETTINGS_CACHE_TTL = 300 # 5 minutes
|
|||||||
# exposed in plaintext. Sending it back on update is treated as "unchanged".
|
# exposed in plaintext. Sending it back on update is treated as "unchanged".
|
||||||
SECRET_MASK = '********'
|
SECRET_MASK = '********'
|
||||||
|
|
||||||
|
# Optional asset identifiers and the asset types they can be toggled on.
|
||||||
|
# Drives per-type seed keys and the Settings matrix UI. The asset type names
|
||||||
|
# match the AssetType.assettype values seeded by each plugin.
|
||||||
|
IDENTIFIER_LABELS = {
|
||||||
|
'gaugelabreference': 'Gauge Lab Reference',
|
||||||
|
'maintenancereference': 'Maintenance Reference',
|
||||||
|
'fqdn': 'FQDN / hostname',
|
||||||
|
}
|
||||||
|
IDENTIFIER_ASSETTYPES = ['equipment', 'computer', 'printer', 'network_device']
|
||||||
|
|
||||||
|
|
||||||
def _is_secret(key: str) -> bool:
|
def _is_secret(key: str) -> bool:
|
||||||
return 'password' in key or 'token' in key or 'secret' in key
|
return 'password' in key or 'token' in key or 'secret' in key
|
||||||
@@ -155,29 +165,22 @@ def create_setting():
|
|||||||
@jwt_required()
|
@jwt_required()
|
||||||
def seed_default_settings():
|
def seed_default_settings():
|
||||||
"""Seed default settings if they don't exist."""
|
"""Seed default settings if they don't exist."""
|
||||||
defaults = [
|
# Asset identifier feature toggles, per identifier AND per asset type.
|
||||||
# Asset identifier feature toggles (global, per identifier)
|
# Key format: identifier_<name>_<assettype>_enabled (boolean). Admins pick
|
||||||
|
# which optional identifiers show on which asset types. See ADR-001.
|
||||||
|
identifierdefaults = [
|
||||||
{
|
{
|
||||||
'key': 'identifier_gaugelabreference_enabled',
|
'key': f'identifier_{name}_{assettype}_enabled',
|
||||||
'value': 'true',
|
'value': 'true',
|
||||||
'valuetype': 'boolean',
|
'valuetype': 'boolean',
|
||||||
'category': 'identifiers',
|
'category': 'identifiers',
|
||||||
'description': 'Show the Gauge Lab Reference identifier on assets'
|
'description': f'Show the {label} identifier on {assettype} assets',
|
||||||
},
|
}
|
||||||
{
|
for name, label in IDENTIFIER_LABELS.items()
|
||||||
'key': 'identifier_maintenancereference_enabled',
|
for assettype in IDENTIFIER_ASSETTYPES
|
||||||
'value': 'true',
|
]
|
||||||
'valuetype': 'boolean',
|
|
||||||
'category': 'identifiers',
|
defaults = identifierdefaults + [
|
||||||
'description': 'Show the Maintenance Reference identifier on assets'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'identifier_fqdn_enabled',
|
|
||||||
'value': 'true',
|
|
||||||
'valuetype': 'boolean',
|
|
||||||
'category': 'identifiers',
|
|
||||||
'description': 'Show the FQDN / hostname identifier on assets'
|
|
||||||
},
|
|
||||||
# Zabbix integration
|
# Zabbix integration
|
||||||
{
|
{
|
||||||
'key': 'zabbix_enabled',
|
'key': 'zabbix_enabled',
|
||||||
|
|||||||
Reference in New Issue
Block a user