Frontend naming + CSS-variable cleanups (review low)
Some checks failed
CI / backend (push) Has been cancelled
CI / naming (push) Has been cancelled
CI / frontend (push) Has been cancelled

Naming convention (LOCKED): rename the ManifestEditor simulate state sim ->
simulateInputs / simResult -> simulateResult (+ .sim-result CSS class) - 'sim'
was banned standalone shorthand. Rename AssetRelationships props assetId ->
assetid and machineNumber -> machinenumber so a prop holding a DB field value
mirrors it verbatim; updated the five detail-page call sites (:assetid=).

CSS variables: SearchResults per-domain badge palette moved into CSS variables
on the container; the duplicated prefers-color-scheme dark block collapses to a
single set of variable overrides instead of restating all ten selectors.

frontend build green; vitest 49 pass; naming green; search badges + detail
relationships verified rendering with no console errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-13 08:48:50 -04:00
parent b99da362b5
commit cd02cd20f4
8 changed files with 64 additions and 125 deletions

View File

@@ -196,12 +196,12 @@ import { apiError } from '../utils/apiError'
const toast = useToast() const toast = useToast()
const props = defineProps({ const props = defineProps({
assetId: { assetid: {
type: Number, type: Number,
default: null default: null
}, },
// Alternative: lookup by machine/asset number // Alternative: lookup by machine/asset number
machineNumber: { machinenumber: {
type: String, type: String,
default: null default: null
} }
@@ -315,14 +315,14 @@ onMounted(async () => {
} }
}) })
watch(() => props.assetId, async () => { watch(() => props.assetid, async () => {
await resolveAssetId() await resolveAssetId()
if (resolvedAssetId.value) { if (resolvedAssetId.value) {
await loadRelationships() await loadRelationships()
} }
}) })
watch(() => props.machineNumber, async () => { watch(() => props.machinenumber, async () => {
await resolveAssetId() await resolveAssetId()
if (resolvedAssetId.value) { if (resolvedAssetId.value) {
await loadRelationships() await loadRelationships()
@@ -330,17 +330,17 @@ watch(() => props.machineNumber, async () => {
}) })
async function resolveAssetId() { async function resolveAssetId() {
// If assetId is provided directly, use it // If assetid is provided directly, use it
if (props.assetId) { if (props.assetid) {
resolvedAssetId.value = props.assetId resolvedAssetId.value = props.assetid
lookupFailed.value = false lookupFailed.value = false
return return
} }
// Otherwise, try to look up by machine number // Otherwise, try to look up by machine number
if (props.machineNumber) { if (props.machinenumber) {
try { try {
const response = await assetsApi.lookup(props.machineNumber) const response = await assetsApi.lookup(props.machinenumber)
resolvedAssetId.value = response.data.data?.assetid resolvedAssetId.value = response.data.data?.assetid
lookupFailed.value = !resolvedAssetId.value lookupFailed.value = !resolvedAssetId.value
} catch (error) { } catch (error) {

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="search-results">
<div class="page-header"> <div class="page-header">
<h2>Search Results</h2> <h2>Search Results</h2>
<span v-if="results.length" class="results-count"> <span v-if="results.length" class="results-count">
@@ -385,56 +385,33 @@ watch(results, () => {
flex-shrink: 0; flex-shrink: 0;
} }
.result-type.machine { /* Per-domain badge palette. Values live in CSS variables on the container so
background: #e3f2fd; the dark theme overrides them in one place (below) instead of restating
color: #1565c0; every selector. Each badge rule just references its pair. */
.search-results {
--rt-machine-bg: #e3f2fd; --rt-machine-fg: #1565c0;
--rt-computer-bg: #e8f5e9; --rt-computer-fg: #2e7d32;
--rt-application-bg: #fff3e0; --rt-application-fg: #e65100;
--rt-knowledgebase-bg: #f3e5f5; --rt-knowledgebase-fg: #7b1fa2;
--rt-printer-bg: #fce4ec; --rt-printer-fg: #c2185b;
--rt-network-bg: #fff8e1; --rt-network-fg: #f57f17;
--rt-measuring-bg: #e0f7fa; --rt-measuring-fg: #00838f;
--rt-employee-bg: #e0f2f1; --rt-employee-fg: #00695c;
--rt-notification-bg: #e8eaf6; --rt-notification-fg: #283593;
--rt-subnet-bg: #fbe9e7; --rt-subnet-fg: #bf360c;
} }
.result-type.machine { background: var(--rt-machine-bg); color: var(--rt-machine-fg); }
.result-type.pc, .result-type.pc,
.result-type.computer { .result-type.computer { background: var(--rt-computer-bg); color: var(--rt-computer-fg); }
background: #e8f5e9; .result-type.application { background: var(--rt-application-bg); color: var(--rt-application-fg); }
color: #2e7d32; .result-type.knowledgebase { background: var(--rt-knowledgebase-bg); color: var(--rt-knowledgebase-fg); }
} .result-type.printer { background: var(--rt-printer-bg); color: var(--rt-printer-fg); }
.result-type.network_device { background: var(--rt-network-bg); color: var(--rt-network-fg); }
.result-type.application { .result-type.measuring_tool { background: var(--rt-measuring-bg); color: var(--rt-measuring-fg); }
background: #fff3e0; .result-type.employee { background: var(--rt-employee-bg); color: var(--rt-employee-fg); }
color: #e65100; .result-type.notification { background: var(--rt-notification-bg); color: var(--rt-notification-fg); }
} .result-type.subnet { background: var(--rt-subnet-bg); color: var(--rt-subnet-fg); }
.result-type.knowledgebase {
background: #f3e5f5;
color: #7b1fa2;
}
.result-type.printer {
background: #fce4ec;
color: #c2185b;
}
.result-type.network_device {
background: #fff8e1;
color: #f57f17;
}
.result-type.measuring_tool {
background: #e0f7fa;
color: #00838f;
}
.result-type.employee {
background: #e0f2f1;
color: #00695c;
}
.result-type.notification {
background: #e8eaf6;
color: #283593;
}
.result-type.subnet {
background: #fbe9e7;
color: #bf360c;
}
.result-content { .result-content {
flex: 1; flex: 1;
@@ -486,55 +463,17 @@ watch(results, () => {
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.result-type.machine { .search-results {
background: rgba(21, 101, 192, 0.2); --rt-machine-bg: rgba(21, 101, 192, 0.2); --rt-machine-fg: #64b5f6;
color: #64b5f6; --rt-computer-bg: rgba(46, 125, 50, 0.2); --rt-computer-fg: #81c784;
} --rt-application-bg: rgba(230, 81, 0, 0.2); --rt-application-fg: #ffb74d;
--rt-knowledgebase-bg: rgba(123, 31, 162, 0.2); --rt-knowledgebase-fg: #ce93d8;
.result-type.pc, --rt-printer-bg: rgba(194, 24, 91, 0.2); --rt-printer-fg: #f48fb1;
.result-type.computer { --rt-network-bg: rgba(245, 127, 23, 0.2); --rt-network-fg: #ffd54f;
background: rgba(46, 125, 50, 0.2); --rt-measuring-bg: rgba(0, 131, 143, 0.2); --rt-measuring-fg: #80deea;
color: #81c784; --rt-employee-bg: rgba(0, 105, 92, 0.2); --rt-employee-fg: #80cbc4;
} --rt-notification-bg: rgba(40, 53, 147, 0.2); --rt-notification-fg: #9fa8da;
--rt-subnet-bg: rgba(191, 54, 12, 0.2); --rt-subnet-fg: #ffab91;
.result-type.application {
background: rgba(230, 81, 0, 0.2);
color: #ffb74d;
}
.result-type.knowledgebase {
background: rgba(123, 31, 162, 0.2);
color: #ce93d8;
}
.result-type.printer {
background: rgba(194, 24, 91, 0.2);
color: #f48fb1;
}
.result-type.network_device {
background: rgba(245, 127, 23, 0.2);
color: #ffd54f;
}
.result-type.measuring_tool {
background: rgba(0, 131, 143, 0.2);
color: #80deea;
}
.result-type.employee {
background: rgba(0, 105, 92, 0.2);
color: #80cbc4;
}
.result-type.notification {
background: rgba(40, 53, 147, 0.2);
color: #9fa8da;
}
.result-type.subnet {
background: rgba(191, 54, 12, 0.2);
color: #ffab91;
} }
} }
</style> </style>

View File

@@ -192,27 +192,27 @@
<h3 class="section-title">Simulate: what would a PC get?</h3> <h3 class="section-title">Simulate: what would a PC get?</h3>
<div class="settings-grid"> <div class="settings-grid">
<div class="setting-row"> <div class="setting-row">
<label><span>Subtype</span><input v-model="sim.subtype" placeholder="subtype" /></label> <label><span>Subtype</span><input v-model="simulateInputs.subtype" placeholder="subtype" /></label>
</div> </div>
<div class="setting-row"> <div class="setting-row">
<label><span>Hostname</span><input v-model="sim.hostname" placeholder="hostname" /></label> <label><span>Hostname</span><input v-model="simulateInputs.hostname" placeholder="hostname" /></label>
</div> </div>
<div class="setting-row"> <div class="setting-row">
<label><span>Machine #</span><input v-model="sim.machinenumber" placeholder="machine #" /></label> <label><span>Machine #</span><input v-model="simulateInputs.machinenumber" placeholder="machine #" /></label>
</div> </div>
<div class="setting-row"> <div class="setting-row">
<label><span>CMM version</span><input v-model="sim.cmmversion" placeholder="CMM version" /></label> <label><span>CMM version</span><input v-model="simulateInputs.cmmversion" placeholder="CMM version" /></label>
</div> </div>
</div> </div>
<button class="btn btn-primary" @click="runSimulate">Run</button> <button class="btn btn-primary" @click="runSimulate">Run</button>
<div v-if="simResult" class="sim-result"> <div v-if="simulateResult" class="simulate-result">
<div> <div>
<strong>Applies ({{ simResult.applied.length }}):</strong> <strong>Applies ({{ simulateResult.applied.length }}):</strong>
{{ simResult.applied.join(', ') || 'none' }} {{ simulateResult.applied.join(', ') || 'none' }}
</div> </div>
<div class="muted"> <div class="muted">
<strong>Filtered:</strong> <strong>Filtered:</strong>
<span v-for="filtered in simResult.filtered" :key="filtered.name"> <span v-for="filtered in simulateResult.filtered" :key="filtered.name">
{{ filtered.name }} ({{ filtered.filteredby.join(',') }}); {{ filtered.name }} ({{ filtered.filteredby.join(',') }});
</span> </span>
</div> </div>
@@ -618,8 +618,8 @@ const versions = ref([])
const showPreview = ref(false) const showPreview = ref(false)
const previewText = ref('') const previewText = ref('')
const sim = ref({ subtype: '', hostname: '', machinenumber: '', cmmversion: '' }) const simulateInputs = ref({ subtype: '', hostname: '', machinenumber: '', cmmversion: '' })
const simResult = ref(null) const simulateResult = ref(null)
const compliance = ref(null) const compliance = ref(null)
const complianceRows = computed(() => compliance.value?.rows || []) const complianceRows = computed(() => compliance.value?.rows || [])
@@ -655,7 +655,7 @@ async function exportShare() {
async function selectScope(id) { async function selectScope(id) {
selectedId.value = id selectedId.value = id
showVersions.value = false showVersions.value = false
simResult.value = null simulateResult.value = null
compliance.value = null compliance.value = null
try { try {
detail.value = payload(await api.get(`/geenforce/scopes/${id}`)) detail.value = payload(await api.get(`/geenforce/scopes/${id}`))
@@ -821,8 +821,8 @@ async function rollback(versionnumber) {
// -- simulate / preview -- // -- simulate / preview --
async function runSimulate() { async function runSimulate() {
try { try {
const params = { ...sim.value } const params = { ...simulateInputs.value }
simResult.value = payload(await api.get( simulateResult.value = payload(await api.get(
`/geenforce/scopes/${detail.value.scopeid}/simulate`, { params })) `/geenforce/scopes/${detail.value.scopeid}/simulate`, { params }))
} catch (e) { error.value = 'Simulate failed' } } catch (e) { error.value = 'Simulate failed' }
} }
@@ -929,7 +929,7 @@ loadApplications()
.muted { color: var(--text-light); } .muted { color: var(--text-light); }
/* Simulator result */ /* Simulator result */
.sim-result { .simulate-result {
margin-top: 1rem; margin-top: 1rem;
font-size: 0.9rem; font-size: 0.9rem;
display: flex; display: flex;

View File

@@ -208,7 +208,7 @@
<WarrantyPanel :assetid="machine.assetid" :items="warranties" /> <WarrantyPanel :assetid="machine.assetid" :items="warranties" />
<!-- All relationships (dualpath, controls, ...) --> <!-- All relationships (dualpath, controls, ...) -->
<AssetRelationships v-if="machine.assetid" :assetId="machine.assetid" /> <AssetRelationships v-if="machine.assetid" :assetid="machine.assetid" />
<!-- Notes --> <!-- Notes -->
<div class="section-card" v-if="machine.notes"> <div class="section-card" v-if="machine.notes">

View File

@@ -140,7 +140,7 @@
<WarrantyPanel :assetid="tool.assetid" :items="warranties" /> <WarrantyPanel :assetid="tool.assetid" :items="warranties" />
<!-- All relationships (partof, connectedto, ...) --> <!-- All relationships (partof, connectedto, ...) -->
<AssetRelationships v-if="tool.assetid" :assetId="tool.assetid" /> <AssetRelationships v-if="tool.assetid" :assetid="tool.assetid" />
<!-- Notes --> <!-- Notes -->
<div class="section-card" v-if="tool.notes"> <div class="section-card" v-if="tool.notes">

View File

@@ -154,7 +154,7 @@
<!-- Relationships --> <!-- Relationships -->
<AssetRelationships <AssetRelationships
v-if="device.assetid" v-if="device.assetid"
:assetId="device.assetid" :assetid="device.assetid"
/> />
<!-- Notes --> <!-- Notes -->

View File

@@ -213,7 +213,7 @@
<WarrantyPanel :assetid="computer.assetid" :items="warranties" /> <WarrantyPanel :assetid="computer.assetid" :items="warranties" />
<!-- All relationships (controls, defaultprinter, ...) --> <!-- All relationships (controls, defaultprinter, ...) -->
<AssetRelationships v-if="computer.assetid" :assetId="computer.assetid" /> <AssetRelationships v-if="computer.assetid" :assetid="computer.assetid" />
<!-- Notes --> <!-- Notes -->
<div class="section-card" v-if="computer.notes"> <div class="section-card" v-if="computer.notes">

View File

@@ -179,7 +179,7 @@
<WarrantyPanel :assetid="printer.assetid" :items="warranties" /> <WarrantyPanel :assetid="printer.assetid" :items="warranties" />
<!-- All relationships (defaultprinter, connectedto, ...) --> <!-- All relationships (defaultprinter, connectedto, ...) -->
<AssetRelationships v-if="printer.assetid" :assetId="printer.assetid" /> <AssetRelationships v-if="printer.assetid" :assetid="printer.assetid" />
<!-- Notes --> <!-- Notes -->
<div class="section-card" v-if="printer.notes"> <div class="section-card" v-if="printer.notes">