feat: Implement category and unit selection for inventory items
- Added a new 'category' field to the InventoryItem entity and updated the database schema. - Replaced free-text input for 'unit' and 'category' with select dropdowns in the AddItemModal. - Introduced a new API endpoint to fetch metadata for units and categories. - Updated inventory filtering logic to use the new 'category' field instead of 'consumable'. - Enhanced validation for item creation and updates to ensure valid unit and category values. - Updated tests to cover new functionality and ensure proper validation.
This commit is contained in:
@@ -11,6 +11,7 @@ export interface InventoryItem {
|
||||
uuid: string;
|
||||
name: string;
|
||||
consumable: string | null;
|
||||
category: string | null;
|
||||
unit: string;
|
||||
price: number; // Rial
|
||||
stock: number;
|
||||
@@ -18,6 +19,12 @@ export interface InventoryItem {
|
||||
status: InventoryStatus;
|
||||
}
|
||||
|
||||
/** Backend-owned option lists (GET /api/v1/inventory-meta) — never hardcode client-side. */
|
||||
export interface InventoryMeta {
|
||||
units: string[];
|
||||
categories: string[];
|
||||
}
|
||||
|
||||
export interface InventoryStats {
|
||||
total: number;
|
||||
low: number;
|
||||
@@ -44,6 +51,7 @@ export interface InventoryPackage {
|
||||
export interface ItemPayload {
|
||||
name: string;
|
||||
consumable?: string | null;
|
||||
category?: string | null;
|
||||
unit?: string;
|
||||
price?: number;
|
||||
stock?: number;
|
||||
@@ -59,6 +67,7 @@ const EMPTY_STATS: InventoryStats = { total: 0, low: 0, inStock: 0, outOfStock:
|
||||
const EMPTY_ITEMS: InventoryItem[] = [];
|
||||
const EMPTY_PACKAGES: InventoryPackage[] = [];
|
||||
const EMPTY_CATS: string[] = [];
|
||||
const EMPTY_META: InventoryMeta = { units: [], categories: [] };
|
||||
|
||||
// ── Hook ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -81,6 +90,13 @@ export function useInventory() {
|
||||
queryFn: () => api.get('/api/v1/inventory-categories'),
|
||||
});
|
||||
|
||||
// Option lists are effectively static — fetch once, never refetch.
|
||||
const metaQuery = useQuery<ApiResponse<InventoryMeta>>({
|
||||
queryKey: ['inventory-meta'],
|
||||
queryFn: () => api.get('/api/v1/inventory-meta'),
|
||||
staleTime: Infinity,
|
||||
});
|
||||
|
||||
const invalidateItems = () => {
|
||||
qc.invalidateQueries({ queryKey: ['inventory-items'] });
|
||||
qc.invalidateQueries({ queryKey: ['inventory-categories'] });
|
||||
@@ -125,6 +141,7 @@ export function useInventory() {
|
||||
stats: itemsQuery.data?.data?.stats ?? EMPTY_STATS,
|
||||
packages: packagesQuery.data?.data ?? EMPTY_PACKAGES,
|
||||
categories: categoriesQuery.data?.data ?? EMPTY_CATS,
|
||||
meta: metaQuery.data?.data ?? EMPTY_META,
|
||||
itemsLoading: itemsQuery.isLoading,
|
||||
packagesLoading: packagesQuery.isLoading,
|
||||
createItem, updateItem, deleteItem,
|
||||
|
||||
Reference in New Issue
Block a user