'use client'; import { useState, useEffect, useCallback, useMemo } from 'react'; import { useRouter } from 'next/navigation'; import { OrderItem } from '@/lib/types'; import { useAuth } from '@/components/AuthProvider'; import BottomNav from '@/components/BottomNav'; import LocationTracker from '@/components/LocationTracker'; import CollectionItemCard from '@/components/CollectionItemCard'; import SupplierGroup from '@/components/SupplierGroup'; import CollectModal from '@/components/CollectModal'; import ProblemModal from '@/components/ProblemModal'; import { getAssignedCollections, updateCollectionStatus, reportProblem, isAuthenticated, logout, } from '@/lib/pocketbase'; import { cn } from '@/lib/utils'; import { RefreshCw, LogOut, MapPin, AlertCircle, CheckCircle2, Search, X, Package, LayoutList, Columns, } from 'lucide-react'; const SUPPLIER_COLORS = [ { border: 'border-l-orange-500', dot: 'bg-orange-500', light: 'bg-orange-50', text: 'text-orange-700', ring: 'ring-orange-500/30' }, { border: 'border-l-blue-500', dot: 'bg-blue-500', light: 'bg-blue-50', text: 'text-blue-700', ring: 'ring-blue-500/30' }, { border: 'border-l-emerald-500', dot: 'bg-emerald-500', light: 'bg-emerald-50', text: 'text-emerald-700', ring: 'ring-emerald-500/30' }, { border: 'border-l-violet-500', dot: 'bg-violet-500', light: 'bg-violet-50', text: 'text-violet-700', ring: 'ring-violet-500/30' }, { border: 'border-l-rose-500', dot: 'bg-rose-500', light: 'bg-rose-50', text: 'text-rose-700', ring: 'ring-rose-500/30' }, { border: 'border-l-cyan-500', dot: 'bg-cyan-500', light: 'bg-cyan-50', text: 'text-cyan-700', ring: 'ring-cyan-500/30' }, { border: 'border-l-amber-500', dot: 'bg-amber-500', light: 'bg-amber-50', text: 'text-amber-700', ring: 'ring-amber-500/30' }, { border: 'border-l-pink-500', dot: 'bg-pink-500', light: 'bg-pink-50', text: 'text-pink-700', ring: 'ring-pink-500/30' }, { border: 'border-l-teal-500', dot: 'bg-teal-500', light: 'bg-teal-50', text: 'text-teal-700', ring: 'ring-teal-500/30' }, { border: 'border-l-indigo-500', dot: 'bg-indigo-500', light: 'bg-indigo-50', text: 'text-indigo-700', ring: 'ring-indigo-500/30' }, ]; export default function CollectionsPage() { const router = useRouter(); const { isLoggedIn, user, isLoading: authLoading } = useAuth(); const [items, setItems] = useState([]); const [filteredItems, setFilteredItems] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); const [error, setError] = useState(''); const [searchQuery, setSearchQuery] = useState(''); const [selectedItem, setSelectedItem] = useState(null); const [showCollectModal, setShowCollectModal] = useState(false); const [showProblemModal, setShowProblemModal] = useState(false); const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' | 'warning' } | null>(null); const [viewMode, setViewMode] = useState<'compact' | 'detailed'>('compact'); // Track which supplier groups are expanded const [expandedSuppliers, setExpandedSuppliers] = useState>(new Set()); useEffect(() => { if (!authLoading && !isLoggedIn) router.push('/login/'); }, [isLoggedIn, authLoading, router]); const loadItems = useCallback(async (showLoading = true) => { if (!isAuthenticated()) return; if (showLoading) setIsLoading(true); setError(''); try { const data = await getAssignedCollections(); setItems(data); setFilteredItems(data); // Auto-expand all suppliers on fresh load const allIds = new Set(); data.forEach(i => { if (i.expand?.supplier?.id) allIds.add(i.expand.supplier.id); }); setExpandedSuppliers(allIds); } catch (err) { console.error('Failed to load collections:', err); setError('Failed to load collections. Pull down to retry.'); } finally { setIsLoading(false); setIsRefreshing(false); } }, []); useEffect(() => { loadItems(); }, [loadItems]); useEffect(() => { let filtered = items; if (searchQuery) { const query = searchQuery.toLowerCase(); filtered = filtered.filter((i) => { const desc = i.description || ''; const supplier = i.expand?.supplier?.suppliername || ''; const orderNum = i.expand?.order?.order_number || ''; return ( desc.toLowerCase().includes(query) || supplier.toLowerCase().includes(query) || orderNum.toLowerCase().includes(query) ); }); } setFilteredItems(filtered); }, [items, searchQuery]); const handleRefresh = async () => { setIsRefreshing(true); await loadItems(false); }; const showToast = (message: string, type: 'success' | 'error' | 'warning' = 'success') => { setToast({ message, type }); setTimeout(() => setToast(null), 3000); }; const handleCollect = async (item: OrderItem, receivedQty: number, notes: string) => { try { await updateCollectionStatus(item.id, 'collected', { qty_collected: receivedQty, notes, collected_date: new Date().toISOString(), }); setItems((prev) => prev.filter((i) => i.id !== item.id)); setShowCollectModal(false); showToast('Collection completed successfully'); } catch (err) { showToast('Failed to update collection', 'error'); } }; const handleProblem = async (item: OrderItem, problemType: string, description: string) => { try { await reportProblem(item.id, problemType, description); setItems((prev) => prev.filter((i) => i.id !== item.id)); setShowProblemModal(false); showToast('Problem reported successfully', 'warning'); } catch (err) { showToast('Failed to report problem', 'error'); } }; const handleNavigate = (lat: number, lng: number, name: string) => { const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent); const url = isIOS ? `maps://maps.apple.com/?q=${encodeURIComponent(name)}&ll=${lat},${lng}` : `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}`; window.open(url, '_blank'); }; const toggleSupplier = (supplierId: string) => { setExpandedSuppliers(prev => { const next = new Set(prev); if (next.has(supplierId)) next.delete(supplierId); else next.add(supplierId); return next; }); }; const expandAll = () => { const allIds = new Set(); filteredItems.forEach(i => { if (i.expand?.supplier?.id) allIds.add(i.expand.supplier.id); }); setExpandedSuppliers(allIds); }; const collapseAll = () => setExpandedSuppliers(new Set()); // Group by supplier const groupedArray = useMemo(() => { const grouped = filteredItems.reduce((acc, item) => { const supplier = item.expand?.supplier; if (!supplier) return acc; const key = supplier.id; if (!acc[key]) acc[key] = { supplier, items: [] }; acc[key].items.push(item); return acc; }, {} as Record['supplier']; items: OrderItem[] }>); return Object.values(grouped).sort((a, b) => (a.supplier?.suppliername || '').localeCompare(b.supplier?.suppliername || '') ); }, [filteredItems]); if (authLoading) { return (

Loading...

); } if (!isLoggedIn) return null; return (
{/* Header */}

Collections

{user?.name || user?.email}

{/* Search bar */}
setSearchQuery(e.target.value)} placeholder="Search by item, supplier, order..." className="w-full pl-9 pr-9 py-2.5 bg-white/15 backdrop-blur-sm border border-white/20 rounded-xl text-sm text-white placeholder-white/50 focus:outline-none focus:ring-2 focus:ring-white/30 focus:bg-white/20 transition-all" /> {searchQuery && ( )}
{/* Stats + Controls bar */}
{items.length} item{items.length !== 1 ? 's' : ''} {groupedArray.length > 0 && ( {groupedArray.length} stop{groupedArray.length !== 1 ? 's' : ''} )}
{/* View toggle */}
{/* Content */}
{isLoading ? (
{[...Array(3)].map((_, i) => (
))}
) : error ? (

{error}

Check your connection and try again

) : groupedArray.length === 0 ? (

All caught up!

{searchQuery ? 'No collections match your search' : 'No assigned collections right now'}

) : (
{groupedArray.map(({ supplier, items: supplierItems }, groupIdx) => { const color = SUPPLIER_COLORS[groupIdx % SUPPLIER_COLORS.length]; const isExpanded = expandedSuppliers.has(supplier!.id); return ( toggleSupplier(supplier!.id)} onCollect={(item) => { setSelectedItem(item); setShowCollectModal(true); }} onProblem={(item) => { setSelectedItem(item); setShowProblemModal(true); }} onNavigate={handleNavigate} /> ); })}
)}
{/* Modals */} { setShowCollectModal(false); setSelectedItem(null); }} onConfirm={handleCollect} /> { setShowProblemModal(false); setSelectedItem(null); }} onConfirm={handleProblem} /> {/* Toast */} {toast && (
{toast.message}
)}
); }