CATEGORY-CARD-FIELDS.PDF · THE IMPLEMENTATION RECORD
What shipped
Every field on the card, traced to its source. Ten already existed and were only re-presented; eleven are new on the API; five are computed in the browser and never leave it.
- Re-presented
- 10no backend change
- New API fields
- 11additive to the DTO
- Derived in the browser
- 5no API field
- Migrations
- 0no table, column or cron
Purely additive, and entirely on existing tables. No migration, no new route — the DTO grew and the query was restructured. Six tables already in the schema carry all of it: camp_inventory_items, equipment_models, equipment_categories, service_contracts, manufacturers, work_orders.
One existing output was checked for drift rather than assumed safe: TierTotal.manufacturerCount is mathematically unchanged despite its query being restructured.
Already there, only re-presented10
These needed no backend work — the card just shows them differently. The last one is not data at all.
equipment_categories.name, ’Uncategorized’ when nullclinical_importance (1–4, NULL→4); badge when === 1COUNT(*) FILTER (risk_level=’Healthy’) / COUNT(*)COUNT(*)COUNT(DISTINCT equipment_model_id), non-null onlyCOUNT(*) FILTER (risk_level=’At Risk’)service_contracts.id IS NOT NULL and the contract has not expiredSUM(equipment_models.benchmark_new_cost) — an AI benchmark, not purchase pricemanufacturers.namedata/manage-inventory-config.ts, not from the databaseNew on the API11
Inventory.manufacturer_id, pre-grouped then countedROUND(AVG(CASE WHEN purchase_year > 0 THEN <year> - purchase_year ELSE 0 END), 1)SUM(benchmark_new_cost) where COALESCE(purchase_date, Jan 1 of purchase_year) + useful_life lands in the next 12 monthswork_orders.status IN (’Open’,’In Progress’), deleted_at IS NULL, joined on wo.inventory_idwo.due_at IS NOT NULL AND wo.due_at < NOW()compute_risk_level_for_rowCOUNT(*) per (category, model)COUNT(*) FILTER (risk_level=’At Risk’) ÷ units — that model’s share, not the category’sended = is_past_eos · announced = is_eol_notice · supported = neither · null = no linked modelis_past_eol AND NOT <contract on>, grouped by manufacturer, top 1How “why units are at risk” adds upriskDrivers[]
Each asset is attributed to one driver by precedence, so the five counts sum exactly to atRisk. Precedence and display order are deliberately different.
recall_status=’Open, Classified’ AND recalls_count>0 AND not is_recall_resolvedequipment_models.is_past_eolage > equipment_categories.useful_lifecontract_expiration_date < CURRENT_DATEWorth noting: other is unreachable unless compute_risk_level_for_row gains a fifth arm — it exists so the bar still balances if the database rule changes before the card does.
Derived in the browser5 — no API field
atRisk / countonContract / count, one decimalcount - onContractdrillIntoGroup(group), the same as clicking the cardcount DESC orderThe docx asked “How are these ordered?”. The sort was already correct — count DESC on the server — so the fix was a caption saying so. No query change, no new field.