diff --git a/frontend/src/pages/DailyWalkthroughPage.tsx b/frontend/src/pages/DailyWalkthroughPage.tsx index bf02b0b..03d96ac 100644 --- a/frontend/src/pages/DailyWalkthroughPage.tsx +++ b/frontend/src/pages/DailyWalkthroughPage.tsx @@ -2,11 +2,16 @@ import { useState, useEffect, useRef } from 'react'; import { useNavigate, Link } from 'react-router-dom'; import { settingsApi, WalkthroughSettings } from '../lib/settingsApi'; import { walkthroughApi, ReservoirCheckData, IrrigationCheckData, PlantHealthCheckData, Walkthrough } from '../lib/walkthroughApi'; +import { documentsApi, Document } from '../lib/documentsApi'; import { - Check, Loader2, Droplets, Sprout, Bug, - Camera, X, Minus, Plus, ChevronDown, ChevronUp, Upload, CheckCircle, Clock + Check, Loader2, Droplets, Sprout, Bug, ArrowLeft, + Camera, X, Minus, Plus, ChevronDown, ChevronUp, Upload, CheckCircle2, Clock, + FileText, AlertTriangle, Thermometer, Wind, Leaf, BookOpen, CircleCheck } from 'lucide-react'; import { useToast } from '../context/ToastContext'; +import { Card } from '../components/ui/card'; +import { cn } from '../lib/utils'; +import { motion, AnimatePresence } from 'framer-motion'; // Tank/Zone configs const TANKS = [ @@ -34,6 +39,7 @@ export default function DailyWalkthroughPage() { const [isLoading, setIsLoading] = useState(true); const [settings, setSettings] = useState(null); const [todaysWalkthrough, setTodaysWalkthrough] = useState(null); + const [relatedSOPs, setRelatedSOPs] = useState([]); // All check states const [reservoirChecks, setReservoirChecks] = useState>({}); @@ -51,12 +57,14 @@ export default function DailyWalkthroughPage() { const loadData = async () => { setIsLoading(true); try { - const [settingsData, todayData] = await Promise.all([ + const [settingsData, todayData, sops] = await Promise.all([ settingsApi.getWalkthrough().catch(() => null), - walkthroughApi.getToday().catch(() => null) + walkthroughApi.getToday().catch(() => null), + documentsApi.getDocuments({ type: 'CHECKLIST', status: 'APPROVED' }).catch(() => []) ]); setSettings(settingsData); setTodaysWalkthrough(todayData); + setRelatedSOPs(sops); } catch (error) { console.error('Failed to load walkthrough data:', error); } finally { @@ -111,13 +119,17 @@ export default function DailyWalkthroughPage() { const isComplete = totalChecks === requiredChecks; const progressPercent = Math.round((totalChecks / requiredChecks) * 100); + // Determine shift based on time + const currentHour = new Date().getHours(); + const shift = currentHour < 12 ? 'AM' : currentHour < 18 ? 'PM' : 'NIGHT'; + // Loading state if (isLoading) { return ( -
+
- -

Loading...

+ +

Loading walkthrough...

); @@ -127,36 +139,42 @@ export default function DailyWalkthroughPage() { if (todaysWalkthrough?.status === 'COMPLETED' && !walkthroughId) { const completedTime = new Date(todaysWalkthrough.endTime || todaysWalkthrough.startTime); return ( -
-
-
- +
+ +
+
-

Walkthrough Complete!

-

+

+ Walkthrough Complete +

+

{new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' })}

-
-
- - + +
+ + Completed at {completedTime.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}
-

+

by {todaysWalkthrough.user?.name || 'Unknown'}

-
- + + Back to Dashboard -
+
); } @@ -164,25 +182,31 @@ export default function DailyWalkthroughPage() { // In-progress walkthrough from earlier if (todaysWalkthrough?.status === 'IN_PROGRESS' && !walkthroughId) { return ( -
-
-
- +
+ +
+
-

Walkthrough In Progress

-

+

+ In Progress +

+

Started at {new Date(todaysWalkthrough.startTime).toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}

- + ← Back to Dashboard -
+
); } @@ -190,70 +214,154 @@ export default function DailyWalkthroughPage() { // Pre-start view if (!walkthroughId) { return ( -
-
-
- +
+ + {/* Header */} +
+
-

Daily Walkthrough

-

- {new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' })} -

+

+ Daily Walkthrough +

+
+ + {new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' })} + + + {shift} Shift + +
+ + {/* Checklist Preview */} + +

Today's Checklist

+
+
+ + Reservoir Checks + {TANKS.length} tanks +
+
+ + Irrigation Checks + {ZONES.length} zones +
+
+ + Plant Health Checks + {HEALTH_ZONES.length} zones +
+
+ {relatedSOPs.length > 0 && ( +
+
+ + {relatedSOPs.length} related SOPs available +
+
+ )} +
+ - + ← Back to Dashboard -
+
); } return ( -
+
+ {/* Sticky Header */} +
+
+
+
+ + + +
+

+ Daily Walkthrough +

+
+ + {new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} + + + {shift} + +
+
+
- {/* Header with Progress */} -
-
-
-

Daily Walkthrough

-

- {new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' })} -

+ {/* Progress Ring */} +
+
+ {progressPercent}% +

{totalChecks}/{requiredChecks}

+
+
+ + + + + {isComplete && ( +
+ +
+ )} +
+
-
- {progressPercent}% -

{totalChecks}/{requiredChecks} checks

-
-
- {/* Progress bar */} -
-
- {/* Sections */} -
- {/* Reservoirs Section */} + {/* Main Content */} +
+ {/* Sections */} {settings?.enableReservoirs !== false && ( toggleSection('reservoirs')} > -
+
{TANKS.map(tank => ( )} - {/* Irrigation Section */} {settings?.enableIrrigation !== false && ( toggleSection('irrigation')} > -
+
{ZONES.map(zone => ( )} - {/* Plant Health Section */} {settings?.enablePlantHealth !== false && ( toggleSection('plantHealth')} > -
+
{HEALTH_ZONES.map(zone => ( {/* Fixed Submit Button */} -
-
+
+
- {expanded &&
{children}
} -
+ + {expanded && ( + +
{children}
+
+ )} +
+ ); } @@ -388,8 +529,6 @@ function PhotoCapture({ const handleFile = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { - // For now, create a local preview URL - // In production, this would upload to server const url = URL.createObjectURL(file); onCapture(url); } @@ -397,27 +536,27 @@ function PhotoCapture({ if (photoUrl) { return ( -
- Captured +
+ Captured
); } return ( -
+
+
); } return ( -
+
- {tank.name} - + {tank.name} + {tank.type}
{/* Level Slider */} -
-
+
+
@@ -504,10 +649,10 @@ function ReservoirRow({ max="100" value={level} onChange={(e) => setLevel(parseInt(e.target.value))} - className="w-full h-2 bg-subtle rounded-full appearance-none cursor-pointer" + className="w-full h-2 bg-slate-700 rounded-full appearance-none cursor-pointer accent-emerald-500" />
- + {level}%
@@ -518,21 +663,19 @@ function ReservoirRow({ value={notes} onChange={(e) => setNotes(e.target.value)} placeholder="Notes (optional)" - className="w-full px-3 py-2 bg-primary border border-subtle rounded-lg text-sm" + className="w-full px-4 py-3 bg-slate-900 border border-slate-700 rounded-xl text-sm text-white placeholder:text-slate-500 focus:border-emerald-500/50 focus:outline-none" /> - {/* Photo Capture */} - {/* Save Button */} -
); } -// Irrigation Row - Enhanced with photo +// Irrigation Row function IrrigationRow({ zone, data, onChange }: { @@ -563,82 +706,80 @@ function IrrigationRow({ if (!editing && data) { const issues = !data.waterFlow || !data.nutrientsMixed || data.drippersWorking < data.drippersTotal; return ( -
-
-
+
+
+
- {zone.name} -
- {data.drippersWorking}/{data.drippersTotal} drippers + {zone.name} +
+ {data.drippersWorking}/{data.drippersTotal} drippers
- +
); } return ( -
+
- {zone.name} + {zone.name}
{/* Dripper Count */} -
+
- {working} - /{zone.drippers} -

drippers working

+ {working} + /{zone.drippers} +

drippers working

{/* Checkboxes */}
-
- {/* Photo Capture */} - {/* Save Button */} -
); } -// Plant Health Row - Enhanced with photo +// Plant Health Row function PlantHealthRow({ zoneName, data, onChange }: { @@ -668,31 +809,37 @@ function PlantHealthRow({ if (!editing && data) { return ( -
-
-
+
+
+
- {zoneName} -
- + {zoneName} +
+ {data.healthStatus} - {data.pestsObserved && 🐛} - {data.issuePhotoUrl && } + {data.pestsObserved && 🐛} + {data.issuePhotoUrl && }
- +
); } return ( -
+
- {zoneName} + {zoneName}
{/* Health Status Buttons */} @@ -701,27 +848,29 @@ function PlantHealthRow({ ))}
{/* Pests Checkbox */} -
diff --git a/frontend/src/pages/WalkthroughSettingsPage.tsx b/frontend/src/pages/WalkthroughSettingsPage.tsx index 9b1db7d..63b7a51 100644 --- a/frontend/src/pages/WalkthroughSettingsPage.tsx +++ b/frontend/src/pages/WalkthroughSettingsPage.tsx @@ -1,8 +1,12 @@ import { useState, useEffect } from 'react'; -import { Save, CheckSquare, Settings, Camera, Loader2 } from 'lucide-react'; +import { Link } from 'react-router-dom'; +import { Save, CheckSquare, Settings, Camera, Loader2, ArrowLeft, BookOpen, FileText, Droplets, Sprout, Bug } from 'lucide-react'; import { settingsApi, WalkthroughSettings, PhotoRequirement } from '../lib/settingsApi'; -import { PageHeader } from '../components/ui/LinearPrimitives'; +import { documentsApi, Document } from '../lib/documentsApi'; +import { Card } from '../components/ui/card'; import { useToast } from '../context/ToastContext'; +import { cn } from '../lib/utils'; +import { motion } from 'framer-motion'; const PHOTO_OPTIONS: { label: string; value: PhotoRequirement }[] = [ { label: 'Always Required', value: 'REQUIRED' }, @@ -16,6 +20,7 @@ export default function WalkthroughSettingsPage() { const [settings, setSettings] = useState(null); const [isLoading, setIsLoading] = useState(false); const [isSaving, setIsSaving] = useState(false); + const [checklists, setChecklists] = useState([]); useEffect(() => { loadSettings(); @@ -24,8 +29,12 @@ export default function WalkthroughSettingsPage() { const loadSettings = async () => { setIsLoading(true); try { - const data = await settingsApi.getWalkthrough(); + const [data, docs] = await Promise.all([ + settingsApi.getWalkthrough(), + documentsApi.getDocuments({ type: 'CHECKLIST', status: 'APPROVED' }).catch(() => []) + ]); setSettings(data); + setChecklists(docs); } catch (e) { console.error(e); addToast('Failed to load settings', 'error'); @@ -52,143 +61,224 @@ export default function WalkthroughSettingsPage() { if (isLoading || !settings) { return ( -
- -
- +
+
+ +

Loading settings...

); } // Toggle switch component - const Toggle = ({ checked, onChange, label }: { checked: boolean; onChange: () => void; label: string }) => ( -