diff --git a/app/components/inventory/AddItemForm.vue b/app/components/inventory/AddItemForm.vue index cf16d44..310cbd2 100644 --- a/app/components/inventory/AddItemForm.vue +++ b/app/components/inventory/AddItemForm.vue @@ -57,6 +57,21 @@ /> + + + + + { quantity: form.quantity, unit_id: form.unit_id, expiry_date: form.expiry_date || null, + low_stock_threshold: form.low_stock_threshold, notes: form.notes.trim() || null }) diff --git a/app/components/inventory/EditItemModal.vue b/app/components/inventory/EditItemModal.vue index 2d4e270..baa2115 100644 --- a/app/components/inventory/EditItemModal.vue +++ b/app/components/inventory/EditItemModal.vue @@ -55,6 +55,21 @@ /> + + + + + props.item, (newItem) => { form.quantity = Number(newItem.quantity) form.unit_id = newItem.unit_id form.expiry_date = newItem.expiry_date || '' + form.low_stock_threshold = newItem.low_stock_threshold || null form.notes = newItem.notes || '' isOpen.value = true } @@ -168,6 +185,7 @@ const handleSubmit = async () => { quantity: form.quantity, unit_id: form.unit_id, expiry_date: form.expiry_date || null, + low_stock_threshold: form.low_stock_threshold, notes: form.notes.trim() || null }) diff --git a/app/components/inventory/InventoryCard.vue b/app/components/inventory/InventoryCard.vue index 242fd0d..b63fbd1 100644 --- a/app/components/inventory/InventoryCard.vue +++ b/app/components/inventory/InventoryCard.vue @@ -72,6 +72,18 @@ {{ expiryText }} + + +
+ + + Low stock ({{ item.quantity }}/{{ item.low_stock_threshold }}) + +
@@ -145,4 +157,10 @@ const expiryText = computed(() => { if (daysUntilExpiry.value === 1) return 'Expires tomorrow' return `Expires in ${daysUntilExpiry.value} days` }) + +// Low stock detection +const isLowStock = computed(() => { + if (!props.item.low_stock_threshold) return false + return Number(props.item.quantity) <= Number(props.item.low_stock_threshold) +}) diff --git a/app/types/database.types.ts b/app/types/database.types.ts index 2a30c5b..5d3a8ff 100644 --- a/app/types/database.types.ts +++ b/app/types/database.types.ts @@ -26,6 +26,8 @@ export interface Database { quantity: number unit_id: string expiry_date: string | null + expires_at: string | null + low_stock_threshold: number | null notes: string | null added_by: string created_at: string @@ -38,6 +40,8 @@ export interface Database { quantity: number unit_id: string expiry_date?: string | null + expires_at?: string | null + low_stock_threshold?: number | null notes?: string | null added_by: string created_at?: string @@ -50,6 +54,8 @@ export interface Database { quantity?: number unit_id?: string expiry_date?: string | null + expires_at?: string | null + low_stock_threshold?: number | null notes?: string | null added_by?: string created_at?: string diff --git a/supabase/migrations/006_add_expiry_lowstock.sql b/supabase/migrations/006_add_expiry_lowstock.sql new file mode 100644 index 0000000..d2bfa4f --- /dev/null +++ b/supabase/migrations/006_add_expiry_lowstock.sql @@ -0,0 +1,26 @@ +-- Migration: Add expiry date and low-stock threshold tracking +-- Issues: #63 (expiry tracking), #67 (low-stock threshold) +-- Created: 2026-02-25 + +-- Note: expiry_date already exists as DATE type. Adding expires_at as TIMESTAMPTZ for consistency +-- and low_stock_threshold for threshold tracking. + +-- Add expires_at column for precise expiry date/time tracking (complementing existing expiry_date) +-- We'll keep both: expiry_date (DATE) for simple day-based expiry, expires_at (TIMESTAMPTZ) for precise tracking +ALTER TABLE inventory_items +ADD COLUMN expires_at TIMESTAMP WITH TIME ZONE DEFAULT NULL; + +-- Add low_stock_threshold column for low-stock alerts +ALTER TABLE inventory_items +ADD COLUMN low_stock_threshold NUMERIC(10,2) DEFAULT NULL; + +-- Add comments for documentation +COMMENT ON COLUMN inventory_items.expires_at IS 'Optional precise expiration timestamp. Complements expiry_date for items needing time-specific expiry.'; +COMMENT ON COLUMN inventory_items.low_stock_threshold IS 'Minimum quantity threshold. Item is considered low-stock when quantity <= threshold. Null means no threshold set.'; + +-- Create index for efficient expiry queries (finding items expiring soon) +CREATE INDEX idx_inventory_items_expires_at ON inventory_items(expires_at) WHERE expires_at IS NOT NULL; + +-- Create index for efficient low-stock queries +CREATE INDEX idx_inventory_items_low_stock ON inventory_items(quantity, low_stock_threshold) +WHERE low_stock_threshold IS NOT NULL;