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;