feat(part-models): detail page with fleet insights
CI / Playwright (smoke) (push) Has been cancelled
CI / Lint · Typecheck · Test · Build (push) Successful in 46s
CI / Build & push images (push) Successful in 1m6s

Adds /part-models/:id mirroring host/part detail pattern: KPIs for
units, spend, avg price, failure counts, and FMs implicating the
model, a state-breakdown bar chart, and the parts-of-this-model
table. New GET /part-models/:id/insights aggregates via part.groupBy
+ aggregate and repair/fm counts.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 14:51:39 -04:00
parent 13e3444258
commit c6fb839005
13 changed files with 738 additions and 27 deletions
+6
View File
@@ -1,5 +1,6 @@
import type {
CreatePartModelRequest,
PartModelInsights,
UpdatePartModelRequest,
} from '@vector/shared';
import { api } from './client.js';
@@ -40,3 +41,8 @@ export async function updatePartModel(
export async function deletePartModel(id: string): Promise<void> {
await api.delete(`/part-models/${id}`);
}
export async function getPartModelInsights(id: string): Promise<PartModelInsights> {
const res = await api.get<PartModelInsights>(`/part-models/${id}/insights`);
return res.data;
}
+1
View File
@@ -15,6 +15,7 @@ export type PartListFilters = {
state?: string;
manufacturerId?: string;
categoryId?: string;
partModelId?: string;
binId?: string;
tagId?: string;
eolOnly?: boolean;
+1
View File
@@ -73,6 +73,7 @@ export const queryKeys = {
list: (filters?: Record<string, unknown>) =>
[...queryKeys.partModels.all, 'list', filters ?? {}] as const,
detail: (id: string) => [...queryKeys.partModels.all, 'detail', id] as const,
insights: (id: string) => [...queryKeys.partModels.all, 'insights', id] as const,
},
tags: {
all: ['tags'] as const,