A provider lookup answers whether a unit is covered. It does not produce the
invoice or the extended-warranty certificate, and a manually entered warranty
had nowhere to keep one - so the proof stayed in somebody's mailbox until they
left.
Two columns rather than one: the served URL of the stored document, and the
name the vendor sent it under, because "Dell invoice 4471.pdf" is what a person
recognises a year later and "warranty-12.pdf" is not. The download route sends
the original name back.
Authenticated in both directions, unlike an asset photo: an invoice carries
pricing and a service tag. One document per warranty, replacing any prior
extension so a re-upload as .pdf does not leave the old .png behind claiming to
be current. Capped at 25MB - a certificate is a document, not a disk image.
Office formats are allowed because purchase records genuinely arrive as .msg
and .xlsx, not only as PDFs.
A shopfloor PC is bought, warranted and replaced as a PC, but it is FOUND by
the machine it drives - nobody walks the floor looking for an asset number. The
warranty tables listed the covered asset and left the reader to work out where
that is.
Both tables gain a Machine # column. The payload resolves it by walking the
asset relationship graph in BOTH directions: the canonical edge is
PC --controls--> machine, but a dual-bay pair carries controls on both bays and
hand-made links are not reliably oriented.
Hovering the chip shows the floor map with the machine marked, so the row
answers "where do I go" without opening anything. The blueprint follows the
viewer's theme and the marker is placed from mapx/mapy as a percentage of the
configured map dimensions, since the preview is a few hundred pixels wide
rather than the full plan. It renders only while hovered, so a long table does
not build a blueprint per row.
A machine with no map position still gets its chip and says so, rather than
being dropped: against real data 142 PCs resolve to a machine and 125 of those
are placed, so 17 rows would otherwise have silently lost their number.
The chip is deliberately not a link. /machines/:id is keyed by machineid, not
assetid, and resolving one to the other here would make the warranty plugin
import the machines plugin (ADR-014). Worth noting separately: the existing
assetLink() in these tables already sends machine-type assets to
/machines/<assetid>, which is that same mismatch and predates this change.
Operators reported losing a part-filled form by clicking slightly outside it.
Every data-entry modal closed on a backdrop click with no warning and no way
back - the worst possible response to a misplaced click, and it happens most to
someone adding their first records at a new site.
Close-on-overlay is removed from 35 modals across 30 files: anything containing
an input, textarea, select or v-model. They still close by Cancel or the X.
Confirmation dialogs keep it, because a delete prompt holds nothing to lose and
dismissing one by clicking away is the behaviour people expect. VendorsList
shows the distinction - its edit form no longer closes that way, its delete
confirmation still does.
The shared Modal component now defaults closeOnOverlay to FALSE. Every current
caller holds a form, a checkout, a stock adjustment or a map position being
picked, and not one passed the prop, so all of them had the same fault. A modal
that genuinely wants dismissing that way opts in explicitly.
Also regroups the operator console menu, which had grown to numbers 1-9 plus
three letters bolted on with no order to them. Actions are now grouped by what
they touch, keyed by their first letter, and the old numbers still work so
nobody who has used it for months is stopped by a rearrangement.
The menu also warns when the server is not fully provisioned and names the key
that fixes it, instead of reporting it as ordinary status lines that read as
normal unless you already knew what to look for. That check is cached for the
session because it shells out to flask twice and the answer does not change
while somebody reads the screen.
core.js still routed plugin-owned pages directly. Extracted all 11 into the
owning plugin's route file + moved their views into plugins/<name>/frontend/:
- computers: reports/pc-relationships, settings/pctypemapping
- printers: reports/toner, settings/printertypes, settings/zabbix (toner/supply
monitoring)
- machines: settings/machinetypes
- network: settings/networktypes
- warranty: settings/dellwarranty
- slides: settings/slides (its route file gains a default export; it was
toplevel-only)
- employees: NEW plugin frontend (employees/:sso + settings/employeedirectory) -
employees had no route file before; its pages lived only in core.js.
core.js now holds only core routes; all 14 bundled plugins are self-contained
under plugins/<name>/frontend/. Verified live: the extracted Machine Types
settings page renders in the settings rail from the machines plugin frontend.
Build + 58 vitest + naming green.
Relocate warranty, measuringtools, network, printers, usb, notifications,
computers, and slides into plugins/<name>/frontend/. Each plugin's views are
pulled from wherever they lived (own dir, plus the shared views/settings/,
views/reports/, views/print/ dirs, and top-level views) into the plugin's
frontend/views/, and its route file becomes the self-contained routes.js.
Handled the messy cases:
- computers: name mismatch (its views live in views/pcs/) - moved by following
the route file's own imports, so the dir name did not matter. Its OS/access-
protocol/PC-type settings views move with it (only computers.js routed them).
- network: NetworkHub's sibling sub-views (NetworkDevicesList, SubnetsBrowse,
not directly routed) moved too so its `./` imports resolve.
- printers: the qrLogo helper is SHARED with core AssetLabel, so it stays in
views/print/ and PrinterQR imports it via @/views/print/qrLogo.
- slides: route file is toplevel-only (TVDashboard); SlideManager stays core
(core.js routes /settings/slides).
frontend/src/views/ now holds only core views; frontend/src/router/routes/ holds
only core.js. All 13 plugins are self-contained under plugins/<name>/frontend/.
Verified live: Network (hub + moved sub-views), Computers (name mismatch),
GE-Enforce (helper), printedparts all render from their staged frontends. Build +
58 vitest + naming green.