import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { Plus, MoveRight, Sprout, Leaf, Flower, Archive, Bug, Search, Cloud, CloudOff, BarChart3, ShieldCheck, Activity } from 'lucide-react'; import { Batch, batchesApi } from '../lib/batchesApi'; import { useToast } from '../context/ToastContext'; import BatchTransitionModal from '../components/BatchTransitionModal'; import WeightLogModal from '../components/WeightLogModal'; import CreateTaskModal from '../components/tasks/CreateTaskModal'; import CreateBatchModal from '../components/CreateBatchModal'; import IPMScheduleModal from '../components/IPMScheduleModal'; import ScoutingModal from '../components/ipm/ScoutingModal'; import { DataTable, Column } from '../components/ui/DataTable'; import { Card } from '../components/ui/card'; import { cn } from '../lib/utils'; // --- Stage Badge with Industrial Styling --- function StageBadge({ stage }: { stage: string }) { const config: Record = { CLONE_IN: { label: 'Clone', color: 'bg-[var(--color-accent)]/10 text-[var(--color-accent)] border-blue-500/20', icon: Sprout }, VEGETATIVE: { label: 'Veg', color: 'bg-[var(--color-primary)]/10 text-[var(--color-primary)] border-emerald-500/20', icon: Leaf }, FLOWERING: { label: 'Flower', color: 'bg-purple-500/10 text-purple-500 border-purple-500/20', icon: Flower }, HARVEST: { label: 'Harvest', color: 'bg-[var(--color-warning)]/10 text-[var(--color-warning)] border-amber-500/20', icon: Archive }, DRYING: { label: 'Drying', color: 'bg-orange-500/10 text-orange-500 border-orange-500/20', icon: Archive }, CURING: { label: 'Curing', color: 'bg-stone-500/10 text-stone-500 border-stone-500/20', icon: Archive }, FINISHED: { label: 'Done', color: 'bg-slate-500/10 text-[var(--color-text-tertiary)] border-slate-500/20', icon: Archive }, }; const info = config[stage] || { label: stage, color: 'bg-gray-100 text-gray-500' }; const Icon = info.icon; return ( {Icon && } {info.label} ); } function MetrcBadge({ synced = true }: { synced?: boolean }) { if (synced) { return ( Sync ); } return ( Pend ); } export default function BatchesPage() { const { addToast } = useToast(); const navigate = useNavigate(); const [batches, setBatches] = useState([]); const [loading, setLoading] = useState(true); // Action States const [selectedBatch, setSelectedBatch] = useState(null); const [weightLogBatch, setWeightLogBatch] = useState(null); const [createTaskBatch, setCreateTaskBatch] = useState(null); const [ipmBatch, setIpmBatch] = useState(null); const [scoutingBatch, setScoutingBatch] = useState(null); const [isCreateOpen, setIsCreateOpen] = useState(false); useEffect(() => { fetchBatches(); }, []); const fetchBatches = async (showToast = false) => { setLoading(true); try { const data = await batchesApi.getAll(); setBatches(data); if (showToast) addToast('Batches refreshed', 'info'); } catch (e) { console.error(e); addToast('Failed to load batches', 'error'); } finally { setLoading(false); } }; const handlePullRefresh = async () => { await fetchBatches(true); }; // --- Table Configuration --- const columns: Column[] = [ { key: 'name', header: 'Batch', cell: (batch) => { const batchCode = batch.name.match(/B\d{3}/)?.[0] || batch.id.slice(0, 6).toUpperCase(); return (
{batchCode}
{batch.strain}
{/* Mobile-only subheading */}
{batch.plantCount} plants {batch.stage.replace('_', ' ').toLowerCase()}
); } }, { key: 'stage', header: 'Stage', hideOnMobile: true, cell: (batch) => }, { key: 'location', header: 'Location', hideOnMobile: true, cell: (batch) => ( {batch.room?.name?.replace('[DEMO] ', '') || 'Unassigned'} ) }, { key: 'plants', header: 'Plants', className: 'text-right', hideOnMobile: true, cell: (batch) => {batch.plantCount} }, { key: 'startDate', header: 'Age', className: 'text-right w-32', hideOnMobile: true, cell: (batch) => { const days = Math.floor((Date.now() - new Date(batch.startDate).getTime()) / (1000 * 60 * 60 * 24)); return (
Day {days} {new Date(batch.startDate).toLocaleDateString()}
); } }, { key: 'actions', header: '', className: 'w-[140px]', cell: (batch) => (
e.stopPropagation()}>
) } ]; // Calculate summary stats const vegCount = batches.filter(b => b.stage === 'VEGETATIVE').length; const flowerCount = batches.filter(b => b.stage === 'FLOWERING').length; const totalPlants = batches.reduce((acc, b) => acc + b.plantCount, 0); return (
{/* Page Header */}

Production Inventory

{batches.length} Active Batches • METRC Integrated
{/* KPI Strip */}

Total Batches

{batches.length}

In Flower

{flowerCount}

In Veg

{vegCount}

Total Plants

{totalPlants.toLocaleString()}

{/* Data Table */} navigate(`/batches/${batch.id}`)} /> {/* Modals */} {selectedBatch && ( setSelectedBatch(null)} onSuccess={() => { fetchBatches(); setSelectedBatch(null); }} /> )} {weightLogBatch && ( setWeightLogBatch(null)} onSuccess={() => fetchBatches()} /> )} {createTaskBatch && ( setCreateTaskBatch(null)} onSuccess={() => { setCreateTaskBatch(null); addToast('Task created!', 'success'); }} /> )} {ipmBatch && ( setIpmBatch(null)} /> )} {scoutingBatch && ( setScoutingBatch(null)} onSuccess={() => { fetchBatches(); addToast('Scouting report logged', 'success'); }} /> )} setIsCreateOpen(false)} onSuccess={() => { fetchBatches(); setIsCreateOpen(false); }} />
); }