// Enhanced stores that sync with both localStorage and Airtable import { writable } from 'svelte/store'; import { browser } from '$app/environment'; import { plantsApi, wateringLogsApi } from './api'; import type { FlowerPot, WateringLogEntry } from './stores'; // Enhanced Flower Pots Store with Airtable sync function createFlowerPotsStore() { const initialValue: FlowerPot[] = []; const { subscribe, set, update } = writable(initialValue); // Load from localStorage first, then sync with Airtable if (browser) { const stored = localStorage.getItem('flower-pots'); if (stored) { try { const parsed = JSON.parse(stored); const pots = parsed.map((pot: any) => ({ ...pot, lastWatered: new Date(pot.lastWatered), nextWatering: new Date(pot.nextWatering) })); set(pots); } catch (e) { console.error('Failed to parse stored flower pots:', e); } } // Sync with Airtable in background plantsApi.getAll().then(airtablePots => { if (airtablePots.length > 0) { set(airtablePots); localStorage.setItem('flower-pots', JSON.stringify(airtablePots)); } }).catch(err => { console.warn('Failed to sync plants with Airtable:', err); }); } return { subscribe, add: async (pot: Omit) => { const newPot: FlowerPot = { ...pot, id: crypto.randomUUID(), nextWatering: new Date(pot.lastWatered.getTime() + pot.wateringFrequency * 24 * 60 * 60 * 1000) }; // Update local state immediately update(pots => { const newPots = [...pots, newPot]; if (browser) { localStorage.setItem('flower-pots', JSON.stringify(newPots)); } return newPots; }); // Sync with Airtable try { await plantsApi.create(newPot); } catch (err) { console.error('Failed to sync new plant with Airtable:', err); } }, remove: async (id: string) => { // Update local state immediately update(pots => { const newPots = pots.filter(pot => pot.id !== id); if (browser) { localStorage.setItem('flower-pots', JSON.stringify(newPots)); } return newPots; }); // Sync with Airtable try { await plantsApi.delete(id); } catch (err) { console.error('Failed to delete plant from Airtable:', err); } }, water: async (id: string) => { update(pots => { const newPots = pots.map(pot => { if (pot.id === id) { const now = new Date(); // Add entry to watering log wateringLogAirtable.add({ potId: pot.id, potName: pot.name, wateredAt: now }); return { ...pot, lastWatered: now, nextWatering: new Date(now.getTime() + pot.wateringFrequency * 24 * 60 * 60 * 1000) }; } return pot; }); if (browser) { localStorage.setItem('flower-pots', JSON.stringify(newPots)); } return newPots; }); // Sync with Airtable try { const updatedPot = await plantsApi.update(id, { lastWatered: new Date() }); // Update local state with Airtable response update(pots => { return pots.map(pot => pot.id === id ? updatedPot : pot); }); } catch (err) { console.error('Failed to sync watered plant with Airtable:', err); } }, update: async (id: string, updates: Partial>) => { update(pots => { const newPots = pots.map(pot => { if (pot.id === id) { const updated = { ...pot, ...updates }; if (updates.lastWatered || updates.wateringFrequency) { updated.nextWatering = new Date( (updates.lastWatered || pot.lastWatered).getTime() + (updates.wateringFrequency || pot.wateringFrequency) * 24 * 60 * 60 * 1000 ); } return updated; } return pot; }); if (browser) { localStorage.setItem('flower-pots', JSON.stringify(newPots)); } return newPots; }); // Sync with Airtable try { await plantsApi.update(id, updates); } catch (err) { console.error('Failed to sync updated plant with Airtable:', err); } }, export: () => { let currentPots: FlowerPot[] = []; subscribe(value => currentPots = value)(); return currentPots; }, import: async (importedPots: Omit[], merge: boolean = false) => { update(currentPots => { let newPots: FlowerPot[]; if (merge) { const existingNames = new Set(currentPots.map(pot => pot.name.toLowerCase())); const potsToAdd = importedPots .filter(pot => !existingNames.has(pot.name.toLowerCase())) .map(pot => ({ ...pot, nextWatering: new Date(pot.lastWatered.getTime() + pot.wateringFrequency * 24 * 60 * 60 * 1000) })); newPots = [...currentPots, ...potsToAdd]; } else { newPots = importedPots.map(pot => ({ ...pot, nextWatering: new Date(pot.lastWatered.getTime() + pot.wateringFrequency * 24 * 60 * 60 * 1000) })); } if (browser) { localStorage.setItem('flower-pots', JSON.stringify(newPots)); } return newPots; }); // Sync with Airtable try { for (const pot of importedPots) { await plantsApi.create(pot as FlowerPot); } } catch (err) { console.error('Failed to sync imported plants with Airtable:', err); } }, sync: async () => { try { const airtablePots = await plantsApi.getAll(); set(airtablePots); if (browser) { localStorage.setItem('flower-pots', JSON.stringify(airtablePots)); } } catch (err) { console.error('Failed to sync plants with Airtable:', err); } } }; } // Enhanced Watering Log Store with Airtable sync function createWateringLogStore() { const initialValue: WateringLogEntry[] = []; const { subscribe, set, update } = writable(initialValue); // Load from localStorage first, then sync with Airtable if (browser) { const stored = localStorage.getItem('watering-log'); if (stored) { try { const parsed = JSON.parse(stored); const logs = parsed.map((log: any) => ({ ...log, wateredAt: new Date(log.wateredAt) })); set(logs); } catch (e) { console.error('Failed to parse stored watering log:', e); } } // Sync with Airtable in background wateringLogsApi.getAll().then(airtableLogs => { if (airtableLogs.length > 0) { set(airtableLogs); localStorage.setItem('watering-log', JSON.stringify(airtableLogs)); } }).catch(err => { console.warn('Failed to sync watering logs with Airtable:', err); }); } return { subscribe, add: async (entry: Omit) => { const newEntry: WateringLogEntry = { ...entry, id: crypto.randomUUID() }; // Update local state immediately update(logs => { const newLogs = [...logs, newEntry]; if (browser) { localStorage.setItem('watering-log', JSON.stringify(newLogs)); } return newLogs; }); // Sync with Airtable try { await wateringLogsApi.create(newEntry); } catch (err) { console.error('Failed to sync new watering log with Airtable:', err); } }, remove: async (id: string) => { // Update local state immediately update(logs => { const newLogs = logs.filter(log => log.id !== id); if (browser) { localStorage.setItem('watering-log', JSON.stringify(newLogs)); } return newLogs; }); // Sync with Airtable try { await wateringLogsApi.delete(id); } catch (err) { console.error('Failed to delete watering log from Airtable:', err); } }, getRecent: (limit: number = 10) => { let logs: WateringLogEntry[] = []; subscribe(value => logs = value)(); return logs .sort((a, b) => b.wateredAt.getTime() - a.wateredAt.getTime()) .slice(0, limit); }, sync: async () => { try { const airtableLogs = await wateringLogsApi.getAll(); set(airtableLogs); if (browser) { localStorage.setItem('watering-log', JSON.stringify(airtableLogs)); } } catch (err) { console.error('Failed to sync watering logs with Airtable:', err); } } }; } // Export enhanced stores export const flowerPotsAirtable = createFlowerPotsStore(); export const wateringLogAirtable = createWateringLogStore(); // Re-export original stores for backward compatibility export { flowerPots, wateringLog, upcomingWatering, dashboardLayout, theme, auth, generateMockPlantData } from './stores';