/* @component-map * App — Main container, tab navigation [app.jsx] * Dashboard — Plant dashboard with status indicators [components/Dashboard.jsx] * AddPlant — Form to add new plants [components/AddPlant.jsx] * PlantCard — Individual plant card with status [components/PlantCard.jsx] * PlantDetail — Detail/edit view for a plant [components/PlantDetail.jsx] * @end-component-map */ // DEPLOY_CONFIG: {"cron": [{"name": "daily_watering_check", "schedule": "0 9 * * *", "action": "event", "config": {"event_type": "check_plants_needing_watering"}}, {"name": "daily_watering_notifications", "schedule": "0 9 * * *", "action": "event", "config": {"event_type": "send_watering_notifications"}}, {"name": "weekly_care_summary_email", "schedule": "0 9 * * 1", "action": "event", "config": {"event_type": "send_weekly_care_summary_email"}}]} import { useMemo, useState } from 'react'; import { useCollection, useUpload, capturePhoto } from '@deplixo/sdk'; import { Dashboard } from './components/Dashboard.jsx'; import { AddPlant } from './components/AddPlant.jsx'; import { PlantDetail } from './components/PlantDetail.jsx'; // PROGRESS:sc_001:complete:Setting up plant care dashboard function App() { const [view, setView] = useState('dashboard'); const [selectedPlantId, setSelectedPlantId] = useState(null); const { items: careLogs, loading: careLogsLoading, add: addCareLog, update: updateCareLog, remove: removeCareLog, } = useCollection('care_logs', { personal: true }); const { items: plantPhotos, loading: plantPhotosLoading, add: addPlantPhoto, update: updatePlantPhoto, remove: removePlantPhoto, } = useCollection('plant_photos', { personal: true }); const { upload, uploading: uploadingPhoto } = useUpload(); const [photoBusyId, setPhotoBusyId] = useState(null); const handleViewPlant = (id) => { setSelectedPlantId(id); setView('detail'); }; const handleBack = () => { setSelectedPlantId(null); setView('dashboard'); }; const handleAddCareLog = async (entry) => { await addCareLog({ ...entry, id: crypto.randomUUID(), createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }); }; const handleUpdateCareLog = async (id, changes) => { await updateCareLog(id, { ...changes, updatedAt: new Date().toISOString(), }); }; const handleDeleteCareLog = async (id) => { await removeCareLog(id); }; const handleAddPlantPhoto = async (plantId, fileOrUrl) => { setPhotoBusyId(plantId); try { let url = fileOrUrl; let fileName = null; if (fileOrUrl instanceof File) { const result = await upload(fileOrUrl); url = result.url; fileName = fileOrUrl.name; } const now = new Date().toISOString(); await addPlantPhoto({ id: crypto.randomUUID(), plantId, url, fileName, createdAt: now, updatedAt: now, }); } finally { setPhotoBusyId(null); } }; const handleUpdatePlantPhoto = async (id, changes) => { await updatePlantPhoto(id, { ...changes, updatedAt: new Date().toISOString(), }); }; const handleDeletePlantPhoto = async (id) => { await removePlantPhoto(id); }; const selectedPlantLogs = useMemo(() => { if (!selectedPlantId) return []; return [...(careLogs || [])] .filter((log) => log.plantId === selectedPlantId) .sort((a, b) => new Date(b.createdAt || 0) - new Date(a.createdAt || 0)); }, [careLogs, selectedPlantId]); const selectedPlantPhotos = useMemo(() => { if (!selectedPlantId) return []; return [...(plantPhotos || [])] .filter((photo) => photo.plantId === selectedPlantId) .sort((a, b) => new Date(b.createdAt || 0) - new Date(a.createdAt || 0)); }, [plantPhotos, selectedPlantId]); const handleCapturePhoto = async (plantId) => { setPhotoBusyId(plantId); try { const result = await capturePhoto({ facing: 'environment' }); if (result?.url) { await handleAddPlantPhoto(plantId, result.url); } } finally { setPhotoBusyId(null); } }; return (
🌿

Plantiful

Your personal plant care companion

{view === 'dashboard' && ( setView('add')} onViewPlant={handleViewPlant} /> )} {view === 'add' && ( )} {view === 'detail' && ( )}
); } ReactDOM.createRoot(document.getElementById("root")).render();