From 6863323cceac6b2a5001e9d0d645b0e9e3ea914b Mon Sep 17 00:00:00 2001 From: toanhoang Date: Mon, 24 Aug 2026 20:41:50 -0400 Subject: [PATCH] Add AssetItemStatus.lost, category filter on /assets page lost added via ALTER TYPE ... ADD VALUE (hand-written migration, stays an enum -- see the schema.prisma comment on why: status values are read directly by business logic like createAssignment()s in_stock gate, a lookup table would not actually decouple that). IMPORT_STATUS_MAP gains lost/missing -> lost mappings for CSV import/re-sync. /assets page gets a Category filter dropdown (alongside the existing Status/Model/search filters) that also narrows the Model dropdown to that category -- with 135 models after the 2026-08-24 real-data import, an unfiltered Model list is unwieldy. Picking a Category resets the Model filter if the currently-selected model does not belong to it. tsc/lint clean, no Vitest coverage -- same deviation as the rest of this branch, see CLAUDE.md. --- .../migration.sql | 2 + prisma/schema.prisma | 9 ++- src/app/assets/page.tsx | 72 +++++++++++++++++-- src/lib/assets.ts | 4 +- 4 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 prisma/migrations/20260825073402_add_asset_item_status_lost/migration.sql diff --git a/prisma/migrations/20260825073402_add_asset_item_status_lost/migration.sql b/prisma/migrations/20260825073402_add_asset_item_status_lost/migration.sql new file mode 100644 index 0000000..5dab1fe --- /dev/null +++ b/prisma/migrations/20260825073402_add_asset_item_status_lost/migration.sql @@ -0,0 +1,2 @@ +-- Hand-written (same deviation as other 2026-08-2x migrations -- see CLAUDE.md). +ALTER TYPE "AssetItemStatus" ADD VALUE 'lost'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e52e431..80a0d8f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -84,14 +84,19 @@ model Employee { @@map("employees") } -// Fixed, closed set of 4 values tied to the domain (see docs/ +// Fixed, closed set of values tied to the domain (see docs/ // asset-license-management.md's "Data model (sơ bộ)" table) — native -// Postgres enum, same rationale as RequestType above. +// Postgres enum, same rationale as RequestType above. `lost` added +// 2026-08-25 (ALTER TYPE ... ADD VALUE, not a table -- see the +// 2026-08-24/25 discussion in CLAUDE.md on why this stays an enum: the +// values are read directly by business logic, e.g. createAssignment()'s +// `status !== "in_stock"` gate, not just display labels). enum AssetItemStatus { in_stock assigned repair retired + lost } // Hardware inventory — see docs/asset-license-management.md's "Data model diff --git a/src/app/assets/page.tsx b/src/app/assets/page.tsx index 19fd7bb..9fc4bb0 100644 --- a/src/app/assets/page.tsx +++ b/src/app/assets/page.tsx @@ -30,7 +30,7 @@ interface AssetItemRecord { model_id: string; purchase_date: string | null; warranty_end_date: string | null; - status: "in_stock" | "assigned" | "repair" | "retired"; + status: "in_stock" | "assigned" | "repair" | "retired" | "lost"; cost: number | null; cost_currency: string | null; purchase_order_id: string | null; @@ -40,13 +40,24 @@ interface AssetItemRecord { // Shape returned by GET /api/asset-models — only the fields used to populate // the modelId here. +interface AssetCategoryRecord { + id: string; + name: string; +} + // Shape returned by GET /api/purchase-orders — only the id, used to populate // the purchaseOrderId + + +
+ +
@@ -275,7 +339,7 @@ export default function AssetsPage() { All - {assetModels.map((assetModel) => ( + {modelsForCategoryFilter.map((assetModel) => ( {assetModel.manufacturer} {assetModel.name} diff --git a/src/lib/assets.ts b/src/lib/assets.ts index 92c500d..3e7cbe8 100644 --- a/src/lib/assets.ts +++ b/src/lib/assets.ts @@ -3,7 +3,7 @@ import { Prisma } from "@prisma/client"; import { prisma } from "./prisma"; import { splitCsvLine, splitCsvLines, normalizeHeader, csvField } from "./csv"; -export type AssetItemStatus = "in_stock" | "assigned" | "repair" | "retired"; +export type AssetItemStatus = "in_stock" | "assigned" | "repair" | "retired" | "lost"; export interface AssetItemRecord { id: string; @@ -455,6 +455,8 @@ const IMPORT_STATUS_MAP: Record = { "in repair": "repair", retired: "retired", disposed: "retired", + lost: "lost", + missing: "lost", }; function statusFromImportText(text: string): AssetItemStatus | null {