mirror of
https://github.com/temetro/temetro.git
synced 2026-07-26 11:58:14 +00:00
0754ce96a4
Add a camera barcode/QR scanner (components/scan/barcode-scanner.tsx, @zxing/browser lazy-loaded) and a Barcode/NDC field to the pharmacy Add-item dialog. Scanning a GS1 DataMatrix auto-fills expiry (AI 17) and lot (AI 10 -> notes), with the GTIN (AI 01) as the barcode; plain 1D barcodes drop straight into the field. Persists a nullable inventory barcode column (migration 0036) through the type/validation/service, and surfaces it on the item detail. i18n added to all locales. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
69 lines
1.8 KiB
TypeScript
69 lines
1.8 KiB
TypeScript
import { apiFetch } from "@/lib/api-client";
|
|
|
|
// A pharmacy stock item. Mirrors the backend `src/types/inventory.ts`. Scoped to
|
|
// the active clinic. `expiresAt` is an ISO YYYY-MM-DD date (or null).
|
|
export type InventoryItem = {
|
|
id: string;
|
|
name: string;
|
|
form: string;
|
|
strength: string;
|
|
unit: string;
|
|
stockQuantity: number;
|
|
reorderThreshold: number;
|
|
location: string;
|
|
barcode: string | null;
|
|
expiresAt: string | null;
|
|
notes: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
// The fields the inventory dialog collects.
|
|
export type InventoryInput = {
|
|
name: string;
|
|
form?: string;
|
|
strength?: string;
|
|
unit?: string;
|
|
stockQuantity?: number;
|
|
reorderThreshold?: number;
|
|
location?: string;
|
|
barcode?: string | null;
|
|
expiresAt?: string | null;
|
|
notes?: string | null;
|
|
};
|
|
|
|
export type Availability = "in-stock" | "low" | "out";
|
|
|
|
// Derived stock status: out at zero, low at/under the reorder threshold,
|
|
// otherwise in stock. Computed on the client (not persisted).
|
|
export function availabilityOf(item: InventoryItem): Availability {
|
|
if (item.stockQuantity <= 0) return "out";
|
|
if (item.stockQuantity <= item.reorderThreshold) return "low";
|
|
return "in-stock";
|
|
}
|
|
|
|
export function listInventory(): Promise<InventoryItem[]> {
|
|
return apiFetch<InventoryItem[]>("/api/inventory");
|
|
}
|
|
|
|
export function createInventory(input: InventoryInput): Promise<InventoryItem> {
|
|
return apiFetch<InventoryItem>("/api/inventory", {
|
|
method: "POST",
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function updateInventory(
|
|
id: string,
|
|
input: InventoryInput,
|
|
): Promise<InventoryItem> {
|
|
return apiFetch<InventoryItem>(`/api/inventory/${id}`, {
|
|
method: "PUT",
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export function deleteInventory(id: string): Promise<void> {
|
|
return apiFetch<void>(`/api/inventory/${id}`, { method: "DELETE" });
|
|
}
|