ca-grow-ops-manager/frontend/src/pages/WalkthroughSettingsPage.tsx
fullsizemalt e240ec7911 feat(phase2): Implement Phase 2 - Plant Touch Points & IPM
Added PlantTouchPoint and IPMSchedule models. Implemented touch-points and IPM controllers/routes. Updated frontend with Dashboard feed and IPM widgets.
2025-12-09 21:22:47 -08:00

154 lines
7.8 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Save, CheckSquare, Settings } from 'lucide-react';
import { settingsApi, WalkthroughSettings, PhotoRequirement } from '../lib/settingsApi';
const PHOTO_OPTIONS: { label: string; value: PhotoRequirement }[] = [
{ label: 'Always Required', value: 'REQUIRED' },
{ label: 'Optional', value: 'OPTIONAL' },
{ label: 'Weekly Only', value: 'WEEKLY' },
{ label: 'On Demand', value: 'ON_DEMAND' },
];
export default function WalkthroughSettingsPage() {
const [settings, setSettings] = useState<WalkthroughSettings | null>(null);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
loadSettings();
}, []);
const loadSettings = async () => {
try {
const data = await settingsApi.getWalkthrough();
setSettings(data);
} catch (e) {
console.error(e);
}
};
const handleUpdate = async (e: React.FormEvent) => {
e.preventDefault();
if (!settings) return;
setIsLoading(true);
try {
await settingsApi.updateWalkthrough(settings);
alert('Settings saved!');
} catch (e) {
console.error(e);
alert('Failed to save settings.');
} finally {
setIsLoading(false);
}
};
if (!settings) return <div>Loading...</div>;
return (
<div className="max-w-4xl mx-auto space-y-6 pb-20">
<header>
<h1 className="text-2xl font-bold text-slate-900 dark:text-white flex items-center gap-2">
<Settings className="text-emerald-600" />
Walkthrough Settings
</h1>
<p className="text-slate-500 dark:text-slate-400 text-sm">
Configure daily checklist requirements and photo rules.
</p>
</header>
<form onSubmit={handleUpdate} className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Enabled Sections */}
<div className="bg-white dark:bg-slate-800 p-6 rounded-xl border border-slate-200 dark:border-slate-700 space-y-4">
<h3 className="text-lg font-bold text-slate-900 dark:text-white flex items-center gap-2">
<CheckSquare size={18} /> Enabled Modules
</h3>
<div className="space-y-2">
<label className="flex items-center gap-3 p-3 bg-slate-50 dark:bg-slate-700/50 rounded-lg cursor-pointer">
<input
type="checkbox"
checked={settings.enableReservoirs}
onChange={e => setSettings({ ...settings, enableReservoirs: e.target.checked })}
className="w-5 h-5 rounded border-slate-300 text-emerald-600 focus:ring-emerald-500"
/>
<span className="font-medium text-slate-700 dark:text-slate-300">Reservoir Checks</span>
</label>
<label className="flex items-center gap-3 p-3 bg-slate-50 dark:bg-slate-700/50 rounded-lg cursor-pointer">
<input
type="checkbox"
checked={settings.enableIrrigation}
onChange={e => setSettings({ ...settings, enableIrrigation: e.target.checked })}
className="w-5 h-5 rounded border-slate-300 text-emerald-600 focus:ring-emerald-500"
/>
<span className="font-medium text-slate-700 dark:text-slate-300">Irrigation Checks</span>
</label>
<label className="flex items-center gap-3 p-3 bg-slate-50 dark:bg-slate-700/50 rounded-lg cursor-pointer">
<input
type="checkbox"
checked={settings.enablePlantHealth}
onChange={e => setSettings({ ...settings, enablePlantHealth: e.target.checked })}
className="w-5 h-5 rounded border-slate-300 text-emerald-600 focus:ring-emerald-500"
/>
<span className="font-medium text-slate-700 dark:text-slate-300">Plant Health Checks</span>
</label>
</div>
</div>
{/* Photo Requirements */}
<div className="bg-white dark:bg-slate-800 p-6 rounded-xl border border-slate-200 dark:border-slate-700 space-y-4">
<h3 className="text-lg font-bold text-slate-900 dark:text-white flex items-center gap-2">
<Save size={18} /> Photo Rules
</h3>
<div className="space-y-4">
<div>
<label className="block text-sm font-bold text-slate-700 dark:text-slate-300 mb-1">Reservoirs</label>
<select
value={settings.reservoirPhotos}
onChange={e => setSettings({ ...settings, reservoirPhotos: e.target.value as PhotoRequirement })}
className="w-full p-2 bg-slate-50 dark:bg-slate-700 rounded-lg border-none"
>
{PHOTO_OPTIONS.map(opt => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-bold text-slate-700 dark:text-slate-300 mb-1">Irrigation</label>
<select
value={settings.irrigationPhotos}
onChange={e => setSettings({ ...settings, irrigationPhotos: e.target.value as PhotoRequirement })}
className="w-full p-2 bg-slate-50 dark:bg-slate-700 rounded-lg border-none"
>
{PHOTO_OPTIONS.map(opt => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-bold text-slate-700 dark:text-slate-300 mb-1">Plant Health</label>
<select
value={settings.plantHealthPhotos}
onChange={e => setSettings({ ...settings, plantHealthPhotos: e.target.value as PhotoRequirement })}
className="w-full p-2 bg-slate-50 dark:bg-slate-700 rounded-lg border-none"
>
{PHOTO_OPTIONS.map(opt => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
))}
</select>
</div>
</div>
</div>
<div className="md:col-span-2">
<button
type="submit"
disabled={isLoading}
className="w-full md:w-auto px-8 py-3 bg-emerald-600 hover:bg-emerald-700 text-white font-bold rounded-xl flex items-center justify-center gap-2 transition-colors disabled:opacity-50"
>
{isLoading ? 'Saving...' : 'Save Configuration'}
</button>
</div>
</form>
</div>
);
}