import { useState, useEffect, useCallback, useContext, useRef } from "react"; import { AuthContext } from "./AuthContext"; import Login from "./Login"; import Register from "./Register"; // ─── Storage helpers ─── const STORAGE_KEY = "coffee-logbook-data"; const defaultData = { beans: [], brewLogs: [], lastSyncedAt: null }; const storage = { get: async (key) => { try { const val = localStorage.getItem(key); return val ? { value: val } : null; } catch { return null; } }, set: async (key, val) => { try { localStorage.setItem(key, val); } catch (e) { console.error("Storage set failed:", e); } } }; async function loadData() { try { const result = await storage.get(STORAGE_KEY); if (result) { const parsed = JSON.parse(result.value); return { beans: parsed.beans || [], brewLogs: parsed.brewLogs || [], lastSyncedAt: parsed.lastSyncedAt || null }; } return defaultData; } catch { return defaultData; } } async function saveData(data) { try { await storage.set(STORAGE_KEY, JSON.stringify(data)); } catch (e) { console.error("Save failed:", e); } } const uid = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 7); const ROAST_TYPES = ["Light", "Light-Medium", "Medium", "Medium-Dark", "Dark"]; const METHODS = ["pourover", "espresso", "coldbrew"]; const METHOD_LABELS = { pourover: "Pour Over", espresso: "Espresso", coldbrew: "Cold Brew" }; const METHOD_ICONS = { pourover: "☕", espresso: "⚡", coldbrew: "❄️" }; const METHOD_COLORS = { pourover: "#8B6914", espresso: "#5C3317", coldbrew: "#2F4F6F" }; const inputCls = "w-full px-3.5 py-3 border border-[#E8DFD3] rounded-lg bg-white text-sm text-[#2C1810] transition-colors outline-none focus:border-[#8B6914]"; const labelCls = "block text-[10px] font-semibold uppercase tracking-wider text-[#6B5744] mb-1.5"; // ─── Create Modal ─── function CreateModal({ onClose, onAddBean, onAddBrew }) { return (
e.stopPropagation()}>
What would you like to add?
); } // ─── Bean Form ─── function BeanForm({ onSave, onClose, initial }) { const [form, setForm] = useState(initial || { name: "", roastery: "", roastDate: "", roastType: "", image: "", tastingNotes: "" }); const set = (k, v) => setForm(p => ({ ...p, [k]: v })); const canSave = form.name.trim().length > 0; const handleImage = (e) => { const file = e.target.files?.[0]; if (!file) return; if (file.size > 2 * 1024 * 1024) { alert("Image must be under 2 MB"); return; } const reader = new FileReader(); reader.onload = () => set("image", reader.result); reader.readAsDataURL(file); }; return (
e.stopPropagation()}>
{initial ? "Edit Bean" : "Add Bean"}
set("name", e.target.value)} />
set("roastery", e.target.value)} />
set("roastDate", e.target.value)} />
{form.image ? ( <>Bean ) : (<>
📷
Tap to upload a photo
)}
Max 2 MB · JPG, PNG, or WebP