import { useState, useEffect } from 'react'; import { Plus, Search, ShoppingCart, Package, Filter, AlertCircle, ExternalLink, X, Loader2, Minus, MoreVertical } from 'lucide-react'; import { suppliesApi, SupplyItem, SupplyCategory } from '../lib/suppliesApi'; import { PageHeader, EmptyState, StatusBadge } from '../components/ui/LinearPrimitives'; import { DataTable, Column } from '../components/ui/DataTable'; import { useToast } from '../context/ToastContext'; import { cn } from '../lib/utils'; import { motion, AnimatePresence } from 'framer-motion'; export default function SuppliesPage() { const { addToast } = useToast(); const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [view, setView] = useState<'all' | 'shopping'>('all'); const [search, setSearch] = useState(''); const [categoryFilter, setCategoryFilter] = useState('ALL'); const [isAddModalOpen, setIsAddModalOpen] = useState(false); const [newItem, setNewItem] = useState({ name: '', category: 'OTHER' as SupplyCategory, quantity: 0, minThreshold: 1, unit: 'each', location: '', vendor: '', productUrl: '', notes: '' }); useEffect(() => { loadItems(); }, []); const loadItems = async () => { setLoading(true); try { const data = await suppliesApi.getAll(); setItems(data); } catch (error) { console.error('Failed to load supplies', error); addToast('Failed to load inventory', 'error'); } finally { setLoading(false); } }; const handleQuantityAdjust = async (id: string, adjustment: number) => { // Optimistic update setItems(current => current.map(item => { if (item.id === id) { return { ...item, quantity: Math.max(0, item.quantity + adjustment) }; } return item; })); try { await suppliesApi.adjustQuantity(id, adjustment); } catch (error) { addToast('Failed to sync quantity', 'error'); loadItems(); } }; const handleCreate = async (e: React.FormEvent) => { e.preventDefault(); try { await suppliesApi.create(newItem); setIsAddModalOpen(false); setNewItem({ name: '', category: 'OTHER', quantity: 0, minThreshold: 1, unit: 'each', location: '', vendor: '', productUrl: '', notes: '' }); addToast('Item created', 'success'); loadItems(); } catch (error) { addToast('Failed to create item', 'error'); } }; const handleMarkOrdered = async (id: string) => { try { const updated = await suppliesApi.markOrdered(id); setItems(current => current.map(item => item.id === id ? updated : item)); addToast('Marked as ordered', 'success'); } catch (error) { addToast('Failed to update order status', 'error'); } }; const shoppingListCount = items.filter(i => i.quantity <= i.minThreshold).length; const filteredItems = items.filter(item => { const matchesSearch = item.name.toLowerCase().includes(search.toLowerCase()) || (item.location && item.location.toLowerCase().includes(search.toLowerCase())) || (item.vendor && item.vendor.toLowerCase().includes(search.toLowerCase())); const matchesCategory = categoryFilter === 'ALL' || item.category === categoryFilter; const matchesView = view === 'all' || (view === 'shopping' && item.quantity <= item.minThreshold); return matchesSearch && matchesCategory && matchesView; }); const categories: SupplyCategory[] = ['FILTER', 'CLEANING', 'PPE', 'OFFICE', 'BATHROOM', 'KITCHEN', 'MAINTENANCE', 'OTHER']; const columns: Column[] = [ { key: 'name', header: 'Item Name', cell: (item) => (
{item.name} {item.quantity <= item.minThreshold && ( )}
) }, { key: 'category', header: 'Category', cell: (item) => ( ), hideOnMobile: true }, { key: 'location', header: 'Location', cell: (item) => {item.location || '—'}, hideOnMobile: true }, { key: 'quantity', header: 'Quantity', className: 'w-48', cell: (item) => (
{item.quantity}
{item.unit}
) }, { key: 'minThreshold', header: 'Min', cell: (item) => (
Min: {item.minThreshold}
), hideOnMobile: true }, { key: 'actions', header: '', className: 'text-right', cell: (item) => (
{item.productUrl && ( e.stopPropagation()} className="p-2 text-indigo-500 hover:bg-indigo-50 dark:hover:bg-indigo-500/10 rounded-lg transition-colors" title="Buy Now" > )} {item.quantity <= item.minThreshold && ( )}
) } ]; return (
setIsAddModalOpen(true)} className="group relative bg-indigo-600 hover:bg-indigo-500 text-white px-5 py-2.5 rounded-xl font-bold text-sm tracking-tight transition-all active:scale-95 shadow-xl shadow-indigo-600/10 flex items-center gap-2" > Add Supply Record } /> {/* Dashboard Ribbon */}

Stock Health

{items.length} Unique SKUs

setSearch(e.target.value)} className="w-full bg-white dark:bg-slate-950 border border-[var(--color-border-subtle)] rounded-lg pl-9 pr-4 py-2 text-sm focus:ring-2 focus:ring-indigo-500/20 transition-all" />
{/* Controls & Table */}
{categories.map(cat => ( ))}
{ setSearch(''); setCategoryFilter('ALL'); }} className="font-bold text-indigo-500 hover:text-indigo-600 uppercase text-xs tracking-widest" > Reset Filters ) : ( ) } /> } />
{/* Add Modal */} {isAddModalOpen && (
setIsAddModalOpen(false)} className="absolute inset-0 bg-black/60 backdrop-blur-sm" />

Add Supply Record

setNewItem({ ...newItem, name: e.target.value })} placeholder="e.g. 5 Gallon Nursery Pots" />
setNewItem({ ...newItem, unit: e.target.value })} placeholder="box, each, gal..." />
setNewItem({ ...newItem, quantity: parseInt(e.target.value) || 0 })} />
setNewItem({ ...newItem, minThreshold: parseInt(e.target.value) || 0 })} />
setNewItem({ ...newItem, location: e.target.value })} placeholder="e.g. Warehouse Rack B4" />
setNewItem({ ...newItem, vendor: e.target.value })} placeholder="Amazon, Home Depot..." />
setNewItem({ ...newItem, productUrl: e.target.value })} placeholder="https://..." />
)}
); }