-
-
-
Irrigation System
-
- Zone {currentZoneIndex + 1} of {zones.length}
-
-
+
+
+
+
Zone {currentZoneIndex + 1}/{zones.length}
+
{currentZone.name}
+
- {/* Progress Bar */}
-
+ {/* Progress */}
+
+ {zones.map((_, i) => (
+ ))}
+
+
+ {/* Drippers Counter */}
+
+
+ Drippers
+ {drippersFailed > 0 && (
+ {drippersFailed} failed
+ )}
+
+
+
+
+
{drippersWorking}
+
of {currentZone.defaultDrippers}
+
+
+
+
+
+ {/* Status Toggles - compact */}
+
+
+
+
+
+
+ {/* Issues input - only when problems */}
+ {!allGood && (
+
+ setIssues(e.target.value)}
+ placeholder="Describe issues..."
+ className="input w-full text-sm"
/>
-
+ )}
- {/* Main Content - Two Column on Desktop */}
-
- {/* Left Column - Drippers & Status */}
-
- {/* Zone Info Card */}
-
-
-
🚿
-
{currentZone.name}
-
{currentZone.defaultDrippers} drippers total
+ {/* Photo */}
+ {(isPhotoRequired || !allGood) && (
+
+
{ if (e.target.files?.[0]) setPhoto(URL.createObjectURL(e.target.files[0])); }} />
+ {photo ? (
+
+

+
-
- {/* Status Badge */}
-
- {allGood ?
:
}
- {allGood ? 'All Systems Operational' : 'Issues Detected'}
-
-
-
- {/* Drippers Counter */}
-
-
-
-
-
-
{drippersWorking}
- {drippersFailed > 0 && (
-
{drippersFailed} failed
- )}
-
-
-
-
-
-
- {/* Right Column - System Checks */}
-
- {/* System Status Toggles */}
-
-
-
-
-
-
-
-
-
- {/* Issues / Notes */}
- {!allGood && (
-
-
-
)}
-
- {/* Photo Upload */}
- {(isPhotoRequired || !allGood) && (
-
-
-
-
{
- if (e.target.files?.[0]) {
- setPhoto(URL.createObjectURL(e.target.files[0]));
- }
- }}
- />
-
- {photo ? (
-
-

-
-
- ) : (
-
- )}
-
- )}
-
- {/* Action Buttons */}
-
-
-
-
-
+ )}
+
+ {/* Action */}
+
);
}
-// Status Toggle Component
-function StatusToggle({
- label,
- value,
- onChange
-}: {
- label: string;
- value: boolean;
- onChange: (val: boolean) => void;
-}) {
+function StatusRow({ label, value, onChange }: { label: string; value: boolean; onChange: (v: boolean) => void }) {
return (
);
}
diff --git a/frontend/src/components/walkthrough/PlantHealthChecklist.tsx b/frontend/src/components/walkthrough/PlantHealthChecklist.tsx
index 7a3451d..b9daf82 100644
--- a/frontend/src/components/walkthrough/PlantHealthChecklist.tsx
+++ b/frontend/src/components/walkthrough/PlantHealthChecklist.tsx
@@ -1,5 +1,5 @@
import { useState } from 'react';
-import { ArrowLeft, ChevronRight, Camera, X, Check, Bug, Droplets, Utensils } from 'lucide-react';
+import { ArrowLeft, ChevronRight, Camera, X, Check, Bug } from 'lucide-react';
interface PlantHealthCheckData {
zoneName: string;
@@ -73,232 +73,149 @@ export default function PlantHealthChecklist({ onComplete, onBack, isPhotoRequir
}
};
- const healthOptions = [
- { value: 'GOOD' as const, label: 'Good', emoji: '😊', color: 'bg-success' },
- { value: 'FAIR' as const, label: 'Fair', emoji: '😐', color: 'bg-warning' },
- { value: 'NEEDS_ATTENTION' as const, label: 'Needs Attention', emoji: '😟', color: 'bg-destructive' },
- ];
-
return (
-
-
+
+
{/* Header */}
-
-
-
-
-
Plant Health Check
-
- Zone {currentZoneIndex + 1} of {zones.length}
-
-
+
+
+
+
Zone {currentZoneIndex + 1}/{zones.length}
+
{currentZone}
+
- {/* Progress Bar */}
-
+ {/* Progress */}
+
+ {zones.map((_, i) => (
+ ))}
+
+
+ {/* Health Status - compact segmented control */}
+
+
Overall Health
+
+ {(['GOOD', 'FAIR', 'NEEDS_ATTENTION'] as const).map(status => (
+
+ ))}
+
+
+
+ {/* Pest Check */}
+
+
+ {pestsObserved && (
+
+ setPestType(e.target.value)}
+ placeholder="Type of pest..."
+ className="input w-full text-sm"
+ />
+
+ )}
+
+
+ {/* Access Status */}
+
+ setWaterAccess(v ? 'OK' : 'ISSUES')}
+ />
+ setFoodAccess(v ? 'OK' : 'ISSUES')}
+ />
+
+
+ {/* Notes - only if issues */}
+ {hasIssues && (
+
+ setNotes(e.target.value)}
+ placeholder="Notes..."
+ className="input w-full text-sm"
/>
-
+ )}
- {/* Main Content - Two Column on Desktop */}
-
- {/* Left Column - Health Status */}
-
- {/* Zone Info */}
-
-
🌱
-
{currentZone}
-
Quick visual inspection
-
-
- {/* Overall Health */}
-
-
-
- {healthOptions.map((option) => (
-
- ))}
-
-
-
- {/* Pest Check */}
-
-
-
-
-
-
- {/* Right Column - Access & Notes */}
-
- {/* Access Status */}
-
-
-
-
setWaterAccess(val ? 'OK' : 'ISSUES')}
- />
- setFoodAccess(val ? 'OK' : 'ISSUES')}
- />
-
-
-
- {/* Notes */}
- {hasIssues && (
-
-
-
- )}
-
- {/* Photo Upload */}
-
-
-
- {/* Action Buttons */}
-
-
- Back
-
-
- {isLastZone ? 'Complete' : 'Next Zone'}
- {!isLastZone && }
-
-
+ )}
-
+ )}
+
+ {/* Action */}
+
+ {isLastZone ? 'Complete' : 'Next'}
+ {!isLastZone && }
+
);
}
-// Access Toggle Component
-function AccessToggle({
- icon: Icon,
- label,
- value,
- onChange
-}: {
- icon: typeof Droplets;
- label: string;
- value: boolean;
- onChange: (val: boolean) => void;
-}) {
+function StatusRow({ label, value, onChange }: { label: string; value: boolean; onChange: (v: boolean) => void }) {
return (
onChange(!value)}
- className={`w-full flex items-center justify-between p-4 rounded-md border transition-colors ${value
- ? 'bg-success-muted border-success/30'
- : 'bg-destructive-muted border-destructive/30'
- }`}
+ className="w-full flex items-center justify-between px-4 py-3 hover:bg-tertiary transition-colors"
>
-
-
-
{label}
+
{label}
+
+ {value ? : }
-
- {value ? '✓' : '✗'}
-
);
}
diff --git a/frontend/src/components/walkthrough/ReservoirChecklist.tsx b/frontend/src/components/walkthrough/ReservoirChecklist.tsx
index 7ca16a9..7b76571 100644
--- a/frontend/src/components/walkthrough/ReservoirChecklist.tsx
+++ b/frontend/src/components/walkthrough/ReservoirChecklist.tsx
@@ -1,5 +1,5 @@
import { useState } from 'react';
-import { ArrowLeft, ChevronRight, Camera, X, AlertCircle } from 'lucide-react';
+import { ArrowLeft, ChevronRight, Camera, X } from 'lucide-react';
interface Tank {
name: string;
@@ -75,164 +75,117 @@ export default function ReservoirChecklist({ onComplete, onBack, isPhotoRequired
const status = getStatus(levelPercent);
const statusConfig = {
- OK: { color: 'bg-success', text: 'Good', badge: 'badge-success' },
- LOW: { color: 'bg-warning', text: 'Low - Needs Refill', badge: 'badge-warning' },
- CRITICAL: { color: 'bg-destructive', text: 'Critical - Refill Now!', badge: 'badge-destructive' },
+ OK: { color: 'bg-success', text: 'Good' },
+ LOW: { color: 'bg-warning', text: 'Low' },
+ CRITICAL: { color: 'bg-destructive', text: 'Critical' },
}[status];
return (
-
-
+
+
{/* Header */}
-
-
-
-
-
-
-
Reservoir Checks
-
- Tank {currentTankIndex + 1} of {tanks.length}
-
-
+
+
+
+
+
+
Reservoir {currentTankIndex + 1}/{tanks.length}
+
{currentTank.name}
+
{currentTank.type}
+
- {/* Progress Bar */}
-
+ {/* Progress */}
+
+ {tanks.map((_, i) => (
+ ))}
+
+
+ {/* Level Indicator */}
+
+
+
+
+ {levelPercent}%
+
+
+
+
setLevelPercent(parseInt(e.target.value))}
+ className="w-full h-1.5 bg-tertiary rounded-full appearance-none cursor-pointer accent-accent"
+ />
+
+
+ {statusConfig.text}
- {/* Main Content - Two Column on Desktop */}
-
- {/* Left Column - Level Control */}
-
-
-
💧
-
{currentTank.name}
-
- {currentTank.type === 'VEG' ? 'Vegetative' : 'Flowering'} Tank
-
-
+ {/* Notes - compact */}
+
+ setNotes(e.target.value)}
+ placeholder="Notes (optional)"
+ className="input w-full text-sm"
+ />
+
- {/* Visual Level Indicator */}
-
-
-
-
- {levelPercent}%
-
-
-
-
- {/* Slider */}
+ {/* Photo - only show when needed */}
+ {(isPhotoRequired || status !== 'OK') && (
+
setLevelPercent(parseInt(e.target.value))}
- className="w-full h-2 bg-tertiary rounded-full appearance-none cursor-pointer accent-accent mb-4"
+ type="file"
+ id="photo-upload"
+ className="hidden"
+ accept="image/*"
+ capture="environment"
+ onChange={(e) => {
+ if (e.target.files?.[0]) {
+ setPhoto(URL.createObjectURL(e.target.files[0]));
+ }
+ }}
/>
-
- {/* Status Badge */}
-
- {statusConfig.text}
-
-
-
- {/* Right Column - Notes & Photo */}
-
- {/* Notes */}
-
-
- Notes (Optional)
-
-
-
- {/* Photo Upload */}
- {(isPhotoRequired || status !== 'OK') && (
-
-
- {isPhotoRequired ? 'Photo (Required)' : 'Photo (Recommended)'}
-
-
-
{
- if (e.target.files?.[0]) {
- setPhoto(URL.createObjectURL(e.target.files[0]));
- }
- }}
- />
-
- {photo ? (
-
-

-
setPhoto(null)}
- className="absolute top-2 right-2 p-1.5 bg-destructive text-white rounded-full shadow-lg"
- >
-
-
-
- ) : (
-
-
- Tap to capture
- Tank level photo
-
- )}
+ {photo ? (
+
+

+
setPhoto(null)}
+ className="absolute top-1 right-1 p-1 bg-black/50 rounded-full"
+ >
+
+
+ ) : (
+
+
+ {isPhotoRequired ? 'Photo required' : 'Add photo'}
+
)}
-
- {/* Action Buttons */}
-
-
- Back
-
-
- {isLastTank ? 'Complete' : 'Next Tank'}
- {!isLastTank && }
-
-
-
+ )}
+
+ {/* Actions */}
+
+ {isLastTank ? 'Complete' : 'Next'}
+ {!isLastTank && }
+
);
diff --git a/frontend/src/pages/DailyWalkthroughPage.tsx b/frontend/src/pages/DailyWalkthroughPage.tsx
index d11f820..5874976 100644
--- a/frontend/src/pages/DailyWalkthroughPage.tsx
+++ b/frontend/src/pages/DailyWalkthroughPage.tsx
@@ -10,7 +10,7 @@ import {
IrrigationCheckData,
PlantHealthCheckData
} from '../lib/walkthroughApi';
-import { ArrowLeft, Check, Loader2, AlertCircle, Droplets, Sprout, Bug } from 'lucide-react';
+import { ArrowLeft, Check, Loader2, AlertCircle, Droplets, Sprout, Bug, ChevronRight, Clock } from 'lucide-react';
import { useToast } from '../context/ToastContext';
type Step = 'start' | 'reservoir' | 'irrigation' | 'plant-health' | 'summary';
@@ -172,129 +172,80 @@ export default function DailyWalkthroughPage() {
// Summary Step
if (currentStep === 'summary') {
+ const totalChecks = reservoirChecks.length + irrigationChecks.length + plantHealthChecks.length;
+ const issues = [
+ ...reservoirChecks.filter(c => c.status !== 'OK'),
+ ...irrigationChecks.filter(c => !c.waterFlow || !c.nutrientsMixed || c.drippersWorking < c.drippersTotal),
+ ...plantHealthChecks.filter(c => c.healthStatus !== 'GOOD' || c.pestsObserved)
+ ].length;
+
return (
-
-
-
-
Review & Submit
-
Review your walkthrough before submitting
+
+
+ {/* Compact header */}
+
+
Review & Submit
- {/* Summary Grid - 1 col on mobile, 3 cols on desktop */}
-
- {/* Reservoir Summary */}
-
-
-
- Reservoirs ({reservoirChecks.length})
-
-
- {reservoirChecks.map((check, i) => (
-
- {check.tankName}
-
- {check.levelPercent}% — {check.status}
-
-
- ))}
- {reservoirChecks.length === 0 && (
-
No checks recorded
- )}
-
+ {/* Summary stats */}
+
+
+
+
+
0 ? 'text-warning' : 'text-success'}`}>{issues}
+
Issues
+
+
- {/* Irrigation Summary */}
-
-
-
- Irrigation ({irrigationChecks.length})
-
-
- {irrigationChecks.map((check, i) => (
-
-
- {check.zoneName}
-
- {check.drippersWorking}/{check.drippersTotal}
-
-
-
-
- {check.waterFlow ? '✓' : '✗'} Water
-
-
- {check.nutrientsMixed ? '✓' : '✗'} Nutrients
-
-
- {check.scheduleActive ? '✓' : '✗'} Schedule
-
-
-
- ))}
- {irrigationChecks.length === 0 && (
-
No checks recorded
- )}
-
-
-
- {/* Plant Health Summary */}
-
-
-
- Plant Health ({plantHealthChecks.length})
-
-
- {plantHealthChecks.map((check, i) => (
-
-
- {check.zoneName}
-
- {check.healthStatus}
-
-
- {check.pestsObserved && (
-
- 🐛 Pests: {check.pestType || 'Observed'}
-
- )}
-
- ))}
- {plantHealthChecks.length === 0 && (
-
No checks recorded
- )}
-
-
+ {/* Compact summary cards */}
+
+ c.status === 'OK') ? 'good' : 'warning'}
+ />
+ c.waterFlow && c.nutrientsMixed) ? 'good' : 'warning'}
+ />
+ c.healthStatus === 'GOOD' && !c.pestsObserved) ? 'good' : 'warning'}
+ />
{error && (
-
-
+
)}
-
+ {/* Actions */}
+
setCurrentStep('plant-health')}
disabled={isLoading}
- className="btn btn-secondary flex-1 h-12 md:h-14"
+ className="btn btn-secondary px-6"
>
-
+
Back
- {isLoading ? : }
- {isLoading ? 'Submitting...' : 'Submit Walkthrough'}
+ {isLoading ? : }
+ {isLoading ? 'Submitting...' : 'Complete Walkthrough'}
@@ -302,82 +253,85 @@ export default function DailyWalkthroughPage() {
);
}
- // Start Screen
- const steps = [
- { id: 'reservoir', title: 'Reservoir Checks', description: 'Check all veg and flower tank levels', icon: Droplets },
- { id: 'irrigation', title: 'Irrigation System', description: 'Verify drippers and water flow', icon: Sprout },
- { id: 'plant-health', title: 'Plant Health', description: 'Spot check for pests and health', icon: Bug },
- ];
+ // Start Screen - Refined
+ const today = new Date();
+ const greeting = today.getHours() < 12 ? 'Good morning' : today.getHours() < 17 ? 'Good afternoon' : 'Good evening';
return (
-
-
- {/* Header */}
-
-
Daily Walkthrough
-
- {new Date().toLocaleDateString('en-US', {
- weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
- })}
-
+
+
+ {/* Minimal header */}
+
+
{today.toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' })}
+
{greeting}
- {/* Main Content - Centered Card */}
-
-
-
☀️
-
Good Morning!
-
Ready to start your daily facility walkthrough?
+ {/* Task list - tight and intentional */}
+
+
+
Daily Walkthrough
+
+
+ ~15 min
+
- {/* Steps Preview */}
-
- {steps.map((step, index) => {
- const Icon = step.icon;
- return (
-
-
-
-
-
-
- {index + 1}. {step.title}
-
-
{step.description}
-
-
-
- );
- })}
+
+
+
+
{error && (
-
-
+
)}
-
- {isLoading ? : null}
- {isLoading ? 'Starting...' : 'Start Walkthrough'}
-
-
-
-
- 💡 Tip: This walkthrough typically takes 15–20 minutes.
- Have your device ready for photos.
-
+
+
+ {isLoading ? : 'Begin'}
+
);
}
+
+// Compact step row
+function StepRow({ icon: Icon, label }: { icon: typeof Droplets; label: string }) {
+ return (
+
+ );
+}
+
+// Summary card
+function SummaryCard({ icon: Icon, label, count, status }: {
+ icon: typeof Droplets;
+ label: string;
+ count: number;
+ status: 'good' | 'warning';
+}) {
+ return (
+
+
+
+
+
{count}
+
{label}
+
+ );
+}