diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index e884c03..5dad473 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -264,9 +264,26 @@ export const printersApi = { get(id) { return api.get(`/printers/${id}`) }, + // create/update write asset core + printer extension in one call (the + // printers plugin owns both). Use these instead of the legacy machinesApi. + create(data) { + return api.post('/printers', data) + }, + update(id, data) { + return api.put(`/printers/${id}`, data) + }, updateExtension(id, data) { return api.put(`/printers/${id}/printerdata`, data) }, + // printer sub-types (Laser, Inkjet, Label, Card, Wide Format, ...) + types: { + list(params = {}) { + return api.get('/printers/types', { params }) + }, + create(data) { + return api.post('/printers/types', data) + } + }, updateCommunication(id, data) { return api.put(`/printers/${id}/communication`, data) }, @@ -279,6 +296,12 @@ export const printersApi = { lowSupplies() { return api.get('/printers/lowsupplies') }, + refreshSupplies() { + return api.post('/printers/supplies/refresh') + }, + lookup({ ip, fqdn } = {}) { + return api.get('/printers/lookup', { params: { ip, fqdn } }) + }, dashboardSummary() { return api.get('/printers/dashboard/summary') }, @@ -303,6 +326,27 @@ export const printersApi = { create(data) { return api.post('/printers/supplytypes', data) } + }, + // model -> toner/drum/waste part-number management + modelSupplies: { + meta() { + return api.get('/printers/supplies/meta') + }, + listModels(params = {}) { + return api.get('/printers/models', { params }) + }, + list(modelnumberid) { + return api.get(`/printers/models/${modelnumberid}/supplies`) + }, + create(modelnumberid, data) { + return api.post(`/printers/models/${modelnumberid}/supplies`, data) + }, + update(modelsupplyid, data) { + return api.put(`/printers/supplies/${modelsupplyid}`, data) + }, + delete(modelsupplyid) { + return api.delete(`/printers/supplies/${modelsupplyid}`) + } } } @@ -321,6 +365,23 @@ export const modelsApi = { list(params = {}) { return api.get('/models', { params }) }, + // Backend caps perpage at 100, so page through every model. Returns the + // full array directly (not an axios response). Use in forms whose model + // dropdown must include the editing record's model regardless of page. + async listAll() { + const first = await api.get('/models', { params: { perpage: 100, page: 1 } }) + let items = first.data.data || [] + const totalpages = first.data.meta?.pagination?.totalpages || 1 + if (totalpages > 1) { + const rest = await Promise.all( + Array.from({ length: totalpages - 1 }, (_, i) => + api.get('/models', { params: { perpage: 100, page: i + 2 } }) + ) + ) + rest.forEach(r => { items = items.concat(r.data.data || []) }) + } + return items + }, get(id) { return api.get(`/models/${id}`) }, @@ -528,8 +589,17 @@ export const assetsApi = { } }, statuses: { - list() { - return api.get('/assets/statuses') + list(params = {}) { + return api.get('/assets/statuses', { params }) + }, + create(data) { + return api.post('/assets/statuses', data) + }, + update(id, data) { + return api.put(`/assets/statuses/${id}`, data) + }, + delete(id) { + return api.delete(`/assets/statuses/${id}`) } } } diff --git a/frontend/src/assets/style.css b/frontend/src/assets/style.css index 148299e..681aeb3 100644 --- a/frontend/src/assets/style.css +++ b/frontend/src/assets/style.css @@ -372,6 +372,15 @@ th, td { border-top: 1px solid var(--border); } +/* Cap a long free-text column (e.g. Description) so it truncates with an + ellipsis instead of widening the row and pushing the Actions column out of + view. Pair with a title attribute to show the full text on hover. */ +td.cell-truncate { + max-width: 32rem; + overflow: hidden; + text-overflow: ellipsis; +} + th { font-weight: 600; font-size: 11px; diff --git a/frontend/src/router/routes/printers.js b/frontend/src/router/routes/printers.js index 0e50cfc..2dd83d8 100644 --- a/frontend/src/router/routes/printers.js +++ b/frontend/src/router/routes/printers.js @@ -23,5 +23,12 @@ export default [ name: 'printer-edit', component: () => import('../../views/printers/PrinterForm.vue'), meta: { requiresAuth: true } + }, + // printer-specific settings + { + path: 'settings/modelsupplies', + name: 'model-supplies', + component: () => import('../../views/settings/ModelSuppliesList.vue'), + meta: { requiresAuth: true } } ] diff --git a/frontend/src/views/machines/MachineDetail.vue b/frontend/src/views/machines/MachineDetail.vue index 97cebef..e1c5585 100644 --- a/frontend/src/views/machines/MachineDetail.vue +++ b/frontend/src/views/machines/MachineDetail.vue @@ -68,6 +68,14 @@ Name {{ equipment.name }} +
| Asset # | +Machine # | Name | Serial Number | Type | diff --git a/frontend/src/views/network/NetworkDevicesList.vue b/frontend/src/views/network/NetworkDevicesList.vue index 1fb111d..53dbd15 100644 --- a/frontend/src/views/network/NetworkDevicesList.vue +++ b/frontend/src/views/network/NetworkDevicesList.vue @@ -54,7 +54,7 @@
|---|
| Asset # | +Asset Tag | Hostname | Serial Number | Type | diff --git a/frontend/src/views/pcs/PCDetail.vue b/frontend/src/views/pcs/PCDetail.vue index 3152ff1..b6aca38 100644 --- a/frontend/src/views/pcs/PCDetail.vue +++ b/frontend/src/views/pcs/PCDetail.vue @@ -16,7 +16,7 @@
|---|
| Asset # | +Asset Tag | Hostname | Serial Number | Type | @@ -153,13 +153,15 @@ function getStatusClass(status) { font-family: 'SF Mono', 'Monaco', 'Consolas', monospace; } +/* keep the cell as a table-cell; flex on astrips 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 | ; pin it back to a cell here. */
+td.actions {
+ display: table-cell;
+ vertical-align: middle;
+}
+
.feature-tag.active {
background: #e3f2fd;
color: #1976d2;
diff --git a/frontend/src/views/printers/PrinterDetail.vue b/frontend/src/views/printers/PrinterDetail.vue
index 15a9789..da13633 100644
--- a/frontend/src/views/printers/PrinterDetail.vue
+++ b/frontend/src/views/printers/PrinterDetail.vue
@@ -18,7 +18,7 @@
-
- {{ printer.name || printer.assetnumber }}+{{ displayTitle }}
+
Hostname / FQDN
{{ printer.printer.hostname }}
@@ -172,23 +172,30 @@
-
+
@@ -238,14 +245,26 @@ import { ref, onMounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import { printersApi } from '../../api'
import LocationMapTooltip from '../../components/LocationMapTooltip.vue'
+import { useIdentifierFlags } from '../../composables/identifierSettings'
const route = useRoute()
+const identifierflags = useIdentifierFlags()
const loading = ref(true)
const printer = ref(null)
const supplies = ref([])
const drivers = ref([])
+// Best display identifier for a printer. name is often the literal "NONE",
+// so fall back to the Windows name, then hostname, then asset number.
+const displayTitle = computed(() => {
+ const p = printer.value
+ if (!p) return ''
+ const name = (p.name || '').trim()
+ if (name && name.toUpperCase() !== 'NONE') return name
+ return p.printer?.windowsname || p.printer?.hostname || p.assetnumber
+})
+
// Get IP address from communications
const ipAddress = computed(() => {
if (!printer.value?.communications) return null
@@ -262,7 +281,8 @@ onMounted(async () => {
])
printer.value = printerRes.data.data
- supplies.value = suppliesRes.data.data || []
+ // supplies endpoint returns {ipaddress, pingstatus, supplies:[...]}
+ supplies.value = suppliesRes.data.data?.supplies || []
drivers.value = driversRes.data.data || []
} catch (error) {
console.error('Error loading printer:', error)
@@ -280,11 +300,9 @@ function getStatusClass(status) {
return 'badge-info'
}
-function getSupplyLevelClass(level) {
- if (level === null || level === undefined) return ''
- if (level <= 10) return 'critical'
- if (level <= 25) return 'low'
- return 'ok'
+function formatSupplyType(supplytype) {
+ if (!supplytype) return ''
+ return supplytype.charAt(0).toUpperCase() + supplytype.slice(1)
}
function formatDate(dateStr) {
@@ -360,4 +378,33 @@ function formatDate(dateStr) {
color: var(--text-light);
margin-top: 0.625rem;
}
+
+.supply-parts {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ justify-content: flex-end;
+}
+
+.supply-part {
+ cursor: help;
+ border-bottom: 1px dotted var(--text-light);
+ position: relative;
+}
+
+/* instant CSS tooltip, no native title delay */
+.supply-part:hover::after {
+ content: attr(data-tip);
+ position: absolute;
+ bottom: 125%;
+ right: 0;
+ white-space: nowrap;
+ background: var(--text);
+ color: var(--bg-card);
+ padding: 0.35rem 0.6rem;
+ border-radius: 4px;
+ font-size: 0.85rem;
+ z-index: 10;
+ pointer-events: none;
+}
diff --git a/frontend/src/views/printers/PrinterForm.vue b/frontend/src/views/printers/PrinterForm.vue
index abf5c05..a22ee39 100644
--- a/frontend/src/views/printers/PrinterForm.vue
+++ b/frontend/src/views/printers/PrinterForm.vue
@@ -37,7 +37,7 @@
- {{ supply.supplyname }}
-
- {{ supply.currentlevel !== null ? `${supply.currentlevel}%` : 'N/A' }}
+ {{ supply.name }}
+
+ {{ supply.level !== null ? `${supply.level}%` : 'N/A' }}
-
@@ -266,10 +267,13 @@
+
+
diff --git a/frontend/src/views/settings/PCTypesList.vue b/frontend/src/views/settings/PCTypesList.vue
index d60ff3d..b2311d5 100644
--- a/frontend/src/views/settings/PCTypesList.vue
+++ b/frontend/src/views/settings/PCTypesList.vue
@@ -21,7 +21,7 @@
+
{{ pt.pctype }} |
- {{ pt.description || '-' }} |
+ {{ pt.description || '-' }} |
diff --git a/frontend/src/views/settings/SettingsIndex.vue b/frontend/src/views/settings/SettingsIndex.vue
index 5bb3241..c816beb 100644
--- a/frontend/src/views/settings/SettingsIndex.vue
+++ b/frontend/src/views/settings/SettingsIndex.vue
@@ -27,6 +27,12 @@
| Manage equipment models by vendor +Model Toners and Supplies+Map toner, drum, and waste part numbers to printer models +Machine Types@@ -85,7 +91,7 @@ |
|---|