Frontend: asset identifiers, statuses, printer types, UI fixes

- Wire gauge/maintenance refs + identifier toggles into equipment pages.
- Repoint status dropdowns and Settings>Statuses to asset statuses.
- Printer type column/filter; require vendor before model; printer-model picker.
- Per-type identifier labels; printer detail title fallback.
- Fixes: truncate long description cells, opaque supply modal, readable
  dark-mode autofill, drum/waste default colorless.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-06-26 08:35:32 -04:00
parent 0436e8b0af
commit e631564377
22 changed files with 786 additions and 124 deletions

View File

@@ -16,7 +16,7 @@
<div class="hero-content">
<div class="hero-title">
<h1>{{ computer.assetnumber }}</h1>
<span v-if="computer.computer?.hostname" class="hero-alias">{{ computer.computer.hostname }}</span>
<span v-if="identifierflags.fqdn && computer.computer?.hostname" class="hero-alias">{{ computer.computer.hostname }}</span>
</div>
<div class="hero-meta">
<span class="badge badge-lg badge-info">Computer</span>
@@ -57,7 +57,7 @@
<span class="info-label">Name</span>
<span class="info-value">{{ computer.name }}</span>
</div>
<div class="info-row" v-if="computer.computer?.hostname">
<div class="info-row" v-if="identifierflags.fqdn && computer.computer?.hostname">
<span class="info-label">Hostname</span>
<span class="info-value mono">{{ computer.computer.hostname }}</span>
</div>
@@ -207,8 +207,10 @@ import { ref, onMounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import { computersApi, applicationsApi, assetsApi } from '../../api'
import LocationMapTooltip from '../../components/LocationMapTooltip.vue'
import { useIdentifierFlags } from '../../composables/identifierSettings'
const route = useRoute()
const identifierflags = useIdentifierFlags()
const loading = ref(true)
const computer = ref(null)

View File

@@ -32,7 +32,7 @@
</div>
<div class="form-row">
<div class="form-group">
<div class="form-group" v-if="identifierflags.fqdn">
<label for="hostname">Hostname</label>
<input
id="hostname"
@@ -268,10 +268,13 @@
<script setup>
import { ref, onMounted, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { machinesApi, machinetypesApi, statusesApi, vendorsApi, locationsApi, modelsApi, operatingsystemsApi } from '../../api'
import { machinesApi, machinetypesApi, assetsApi, vendorsApi, locationsApi, modelsApi, operatingsystemsApi } from '../../api'
import ShopFloorMap from '../../components/ShopFloorMap.vue'
import Modal from '../../components/Modal.vue'
import { currentTheme } from '../../stores/theme'
import { useIdentifierFlags } from '../../composables/identifierSettings'
const identifierflags = useIdentifierFlags()
const route = useRoute()
const router = useRouter()
@@ -317,7 +320,9 @@ const filteredModels = computed(() => {
if (form.value.vendorid && m.vendorid !== form.value.vendorid) {
return false
}
if (form.value.machinetypeid && m.machinetypeid !== form.value.machinetypeid) {
// only exclude models that have a type set and it differs; most models
// have no machinetypeid, so a strict check hides the PC's own model
if (form.value.machinetypeid && m.machinetypeid && m.machinetypeid !== form.value.machinetypeid) {
return false
}
return true
@@ -327,19 +332,20 @@ const filteredModels = computed(() => {
onMounted(async () => {
try {
// Load reference data
const [ptRes, statusRes, vendorRes, modelsRes, locRes, osRes] = await Promise.all([
machinetypesApi.list({ category: 'PC' }),
statusesApi.list(),
vendorsApi.list(),
modelsApi.list(),
locationsApi.list(),
operatingsystemsApi.list()
// perpage 100 so dropdowns aren't truncated to the default 20-row page
const [ptRes, statusRes, vendorRes, allModels, locRes, osRes] = await Promise.all([
machinetypesApi.list({ category: 'PC', perpage: 100 }),
assetsApi.statuses.list(),
vendorsApi.list({ perpage: 100 }),
modelsApi.listAll(), // backend caps perpage at 100; page through all
locationsApi.list({ perpage: 100 }),
operatingsystemsApi.list({ perpage: 100 })
])
pcTypes.value = ptRes.data.data || []
statuses.value = statusRes.data.data || []
vendors.value = vendorRes.data.data || []
models.value = modelsRes.data.data || []
models.value = allModels
locations.value = locRes.data.data || []
operatingsystems.value = osRes.data.data || []

View File

@@ -24,7 +24,7 @@
<table>
<thead>
<tr>
<th>Asset #</th>
<th>Asset Tag</th>
<th>Hostname</th>
<th>Serial Number</th>
<th>Type</th>
@@ -153,13 +153,15 @@ function getStatusClass(status) {
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
}
/* keep the cell as a table-cell; flex on a <td> strips table-cell layout and
offsets the row. lay tags out inline instead. */
.features {
display: flex;
gap: 0.375rem;
white-space: nowrap;
}
.feature-tag {
display: inline-block;
margin-right: 0.375rem;
padding: 0.3rem 0.625rem;
font-size: 0.875rem;
border-radius: 5px;
@@ -167,6 +169,17 @@ function getStatusClass(status) {
color: var(--text-light);
}
.feature-tag:last-child {
margin-right: 0;
}
/* the global .actions rule is inline-flex, which also breaks table-cell
alignment when applied straight on a <td>; pin it back to a cell here. */
td.actions {
display: table-cell;
vertical-align: middle;
}
.feature-tag.active {
background: #e3f2fd;
color: #1976d2;