feat: laundry-list polish pass
CI / Lint · Typecheck · Test · Build (push) Successful in 44s
CI / Playwright (smoke) (push) Has been skipped
CI / Build & push images (push) Successful in 59s

Seven bundled improvements:
- PartModel combobox on Add Part + Log Repair (known MPN auto-fills;
  unknown reveals manufacturer picker for catalog upsert).
- Host lifecycle: state (DEPLOYED/DEGRADED/TESTING) and stack
  (PRODUCTION/VETTING) fields, driven by external clients via the API.
- Locations page redesigned as a 2-pane tree + bin grid with breadcrumb.
- PENDING_REPAIR custody state: tech takes a SPARE into custody for a
  future swap; resolves to DEPLOYED via Repair or back to SPARE via a
  bin-required drop-off.
- Move Category from Part to PartModel; seed common categories
  (GPU/RAM/SSD/HDD/NIC/CPU/PSU/MOBO). Parts table gets a Category
  column and filter sourced from the model.
- Fix Deployed Value 100x bug on the Dashboard (price is stored as
  dollars, not cents).
- PartModels table shows "No" instead of "--" when destroyOnFail=false.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 13:36:11 -04:00
parent 3d77f2846d
commit 60255f20bb
39 changed files with 1731 additions and 630 deletions
+5
View File
@@ -11,3 +11,8 @@ export async function dropOff(partId: string, input: DropOffRequest): Promise<Pa
const res = await api.post<Part>(`/custody/${partId}/drop-off`, input);
return res.data;
}
export async function takeForRepair(partId: string): Promise<Part> {
const res = await api.post<Part>(`/custody/${partId}/take-for-repair`);
return res.data;
}
+2
View File
@@ -7,6 +7,8 @@ export type HostListFilters = {
page?: number;
pageSize?: number;
q?: string;
state?: string;
stack?: string;
};
export function listHosts(filters: HostListFilters = {}) {
+1
View File
@@ -10,6 +10,7 @@ export type PartModelListFilters = {
page?: number;
pageSize?: number;
manufacturerId?: string;
categoryId?: string;
q?: string;
eolBefore?: string;
};
+12 -2
View File
@@ -1,4 +1,11 @@
import type { FmStatus, PartEventType, PartState, Role } from '@vector/shared';
import type {
FmStatus,
HostState,
HostStack,
PartEventType,
PartState,
Role,
} from '@vector/shared';
// Shapes mirror Prisma rows the API returns (dates serialized as ISO strings).
// Keep these in sync with apps/api/src/services responses.
@@ -14,12 +21,14 @@ export interface PartModel {
id: string;
manufacturerId: string;
mpn: string;
categoryId: string | null;
eolDate: string | null;
destroyOnFail: boolean;
notes: string | null;
createdAt: string;
updatedAt: string;
manufacturer?: Manufacturer;
category?: Category | null;
_count?: { parts: number };
}
@@ -59,7 +68,6 @@ export interface Part {
price: number | null;
state: PartState;
binId: string | null;
categoryId: string | null;
hostId: string | null;
custodianId: string | null;
notes: string | null;
@@ -99,6 +107,8 @@ export interface Host {
name: string;
location: string | null;
notes: string | null;
state: HostState;
stack: HostStack;
createdAt: string;
updatedAt: string;
}