Files
shopdb-flask/plugins/machines/frontend/views/MachinesList.vue
cproudlock 85931db0fa
Some checks failed
CI / backend (push) Failing after 7m9s
CI / naming (push) Failing after 7m10s
CI / frontend (push) Failing after 7m18s
CI / migrations-mysql (push) Failing after 7m18s
Network devices: filter models by vendor, and style the hero like every other page
Three fixes, all of them a page not doing what its siblings already do.

MODELS WERE NOT FILTERED BY VENDOR. PCForm, MachineForm and PrinterForm each
narrow the model list once a vendor is chosen; NetworkDeviceForm bound the whole
catalogue, so picking Palo Alto still offered every Dell and Zebra model. Same
computed as the others, including the same rule that no vendor selected shows
everything - an empty dropdown reads as "no models exist" when it means "pick a
vendor first". Audited the rest: this was the only gap. The other views holding
a modelnumberid have no vendor picker to filter against, and settings ModelsList
is where a model's vendor is ASSIGNED, where filtering would be circular.

THE HERO RAN THE LABEL INTO THE VALUE - "Asset #FW-OAV..." as one string. The
page used detail-item / label / value, which match nothing in the stylesheet, so
the two spans got no layout at all. Every other detail page uses hero-detail /
hero-detail-label / hero-detail-value, which stacks a small uppercase label above
the value. Renamed to those; no CSS added, because the styles already existed and
this page simply was not using them. It was the last page using the unstyled
names.

MACHINES LIST LINKED BY THE WRONG ID on its fallback path. `/machines/:id` keys
on machineid, the plugin extension id, and the row click and View button fell
back to `item.assetid` - which lands on whichever machine happens to carry that
number: a wrong page that looks right, which is worse than a 404. That defect has
been fixed twice before in other views (AssetRelationships, then the GE-Enforce
reports table) and BackupHistory carries a comment warning about it; this was the
fourth copy. The list endpoint always sets item.machine, so the fallback could
not actually fire here - it is removed as a latent trap rather than a live bug,
and with no machineid the cell now shows plain text rather than a link that
misleads.
2026-08-20 15:10:01 -04:00

165 lines
5.2 KiB
Vue

<template>
<div>
<div class="page-header">
<h2>Machines</h2>
<router-link to="/print/asset-label-batch/machine" class="btn btn-secondary" target="_blank">Print Labels</router-link>
<router-link to="/machines/new" class="btn btn-primary">Add Machine</router-link>
</div>
<!-- Filters -->
<div class="filters">
<input
v-model="search"
type="text"
class="form-control"
placeholder="Search machines..."
@input="debouncedSearch"
/>
</div>
<div class="card">
<div v-if="loading" class="loading">Loading...</div>
<template v-else>
<div class="table-container">
<table>
<thead>
<tr>
<th>Machine #</th>
<th>Name</th>
<th>Serial Number</th>
<th>Model Type</th>
<th>Vendor</th>
<th>Status</th>
<th>Location</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="item in machines" :key="item.assetid"
:class="{ 'clickable-row': item.machine?.machineid }"
@click="item.machine?.machineid && $router.push(`/machines/${item.machine.machineid}`)">
<td>
{{ item.assetnumber }}<template v-if="item.dualpathpartner"> / {{ item.dualpathpartner.assetnumber }}</template>
</td>
<td>{{ item.name || '-' }}</td>
<td class="mono">{{ item.serialnumber || '-' }}</td>
<!--
The catalog model's type, not the machine's own. 134 machines
arrived from the classic ASP database on machinetypeid=1 - a
LocationOnly placeholder the import refuses to carry across as
a real subtype - so Machine Type is blank for them while the
model knows exactly what they are.
-->
<td>{{ item.machine?.modeltypename || '-' }}</td>
<td>{{ item.machine?.vendorname || '-' }}</td>
<td>
<span class="badge" :style="colorStyle(item.statuscolor)">
{{ item.statusname || 'Unknown' }}
</span>
</td>
<td>{{ item.locationname || '-' }}</td>
<td class="actions" @click.stop>
<!-- /machines/:id keys on machineid, the plugin extension id, NOT
the assetid. Falling back to the assetid lands on whichever
machine happens to carry that number: a wrong page that looks
right, which is worse than a 404 (see the same fix in
EnforcementReports and the warning in BackupHistory). With no
machineid there is no page to link to, so show nothing. -->
<router-link
v-if="item.machine?.machineid"
:to="`/machines/${item.machine.machineid}`"
class="btn btn-secondary btn-sm"
>
View
</router-link>
<span v-else>-</span>
</td>
</tr>
<tr v-if="machines.length === 0">
<td colspan="8" style="text-align: center; color: var(--text-light);">
No machines found
</td>
</tr>
</tbody>
</table>
</div>
<!-- Pagination -->
<PaginationBar
:page="page"
:totalPages="totalPages"
:perPage="perPage"
@update:page="goToPage"
@update:perPage="changePerPage"
/>
</template>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { colorStyle } from "@/utils/colorStyle"
import { machinesApi } from '@/api'
import PaginationBar from '@/components/PaginationBar.vue'
import { useListQuery } from '@/composables/listQuery'
const machines = ref([])
const loading = ref(true)
const { page, search, setPage, setSearch } = useListQuery({ onChange: loadMachines })
const totalPages = ref(1)
const perPage = ref(20)
let searchTimeout = null
onMounted(() => {
loadMachines()
})
async function loadMachines() {
loading.value = true
try {
const params = {
page: page.value,
perpage: perPage.value
}
if (search.value) params.search = search.value
const response = await machinesApi.list(params)
machines.value = response.data.data || []
totalPages.value = response.data.meta?.pagination?.totalpages || response.data.meta?.pagination?.total_pages || 1
} catch (error) {
console.error('Error loading machines:', error)
} finally {
loading.value = false
}
}
function debouncedSearch() {
clearTimeout(searchTimeout)
searchTimeout = setTimeout(() => {
setSearch(search.value)
loadMachines()
}, 300)
}
function goToPage(p) {
setPage(p)
loadMachines()
}
function changePerPage(newPerPage) {
perPage.value = newPerPage
setPage(1)
loadMachines()
}
</script>
<style scoped>
.mono {
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
}
</style>