"use client"; import { AlertCircle, RotateCcw, ShoppingBag, X } from "lucide-react"; import { useCallback, useEffect, useMemo, useState, useTransition } from "react"; import { generateMealPlanAction } from "@/app/actions"; import { Cart, buildGroceryList } from "@/app/lib/grocery"; import { activeRecipeIds } from "@/app/lib/plan"; import { averageGroceryProvider } from "@/app/lib/pricing"; import { RECIPES } from "@/app/lib/recipes"; import { usePlanState } from "@/app/lib/usePlanState"; import type { FillMode } from "@/app/types"; import { BudgetMeter } from "./BudgetMeter"; import { ConfigPanel } from "./ConfigPanel"; import { GroceryListDrawer, GrocerySummaryPanel } from "./GroceryListDrawer"; import { IngredientPanel } from "./IngredientPanel"; import { MealGrid } from "./MealGrid"; import { MealSelectorModal } from "./MealSelectorModal"; import { Navbar } from "./Navbar"; import { RecipeDialog } from "./RecipeDialog"; interface UndoSnapshot { message: string; previousSlots: Record; } export default function PlannerDashboard() { const { state, dispatch, syncStatus } = usePlanState(); const [isGenerating, startGenerating] = useTransition(); const [notes, setNotes] = useState([]); const [openSlot, setOpenSlot] = useState(null); const [viewSlot, setViewSlot] = useState(null); const [groceriesOpen, setGroceriesOpen] = useState(false); const [undo, setUndo] = useState(null); // Auto-dismiss undo toast after 6 seconds useEffect(() => { if (!undo) return; const timer = window.setTimeout(() => setUndo(null), 6000); return () => window.clearTimeout(timer); }, [undo]); const summary = useMemo( () => buildGroceryList(activeRecipeIds(state), { householdSize: state.householdSize, onHandItems: state.onHandItems }), [state], ); const mealCosts = useMemo(() => { const emptyCart = new Cart({ householdSize: state.householdSize, onHandItems: state.onHandItems }); return new Map(RECIPES.map((r) => [r.id, emptyCart.marginal(r.id).eaten])); }, [state.householdSize, state.onHandItems]); function handleGenerate(fillMode: FillMode) { startGenerating(async () => { try { const result = await generateMealPlanAction({ state, fillMode, seed: Date.now() }); dispatch({ type: "replaceSlots", slots: result.slots }); setNotes(result.notes); setUndo(null); } catch { setNotes(["Couldn't reach the planner. Check your connection and try again."]); } }); } const handleRemoveSlot = useCallback( (key: string, recipeTitle: string) => { setUndo({ message: `Removed "${recipeTitle}"`, previousSlots: { ...state.slots } }); dispatch({ type: "setSlot", key, recipeId: null }); }, [dispatch, state.slots], ); const handleClearWeek = useCallback(() => { const count = activeRecipeIds(state).length; if (count === 0) return; setUndo({ message: `Cleared ${count} planned meal${count === 1 ? "" : "s"}`, previousSlots: { ...state.slots } }); dispatch({ type: "clearSlots" }); }, [dispatch, state]); const handleUndo = useCallback(() => { if (!undo) return; dispatch({ type: "replaceSlots", slots: undo.previousSlots }); setUndo(null); }, [dispatch, undo]); const closeSelector = useCallback(() => setOpenSlot(null), []); const closeRecipe = useCallback(() => setViewSlot(null), []); const closeGroceries = useCallback(() => setGroceriesOpen(false), []); const toggleOnHand = useCallback( (itemId: string, onHand: boolean) => dispatch({ type: "setItemPreference", itemIds: [itemId], preference: onHand ? "have" : "use" }), [dispatch], ); const mealCount = activeRecipeIds(state).length; return ( <>
{/* Left: compact control bar -> immediate MealGrid -> Pantry below */}
{notes.length > 0 && (
    {notes.map((note) => (
  • {note}
  • ))}
)}
{/* Right: persistent Market Receipt summary (desktop) */}
{/* Undo Toast for accidental meal removal or Clear */} {undo && (
{undo.message}
)} {/* Mobile: sticky bottom bar */}

{mealCount} meals planned, estimated checkout {summary.checkoutTotal.toFixed(2)} dollars

{ setViewSlot(null); setOpenSlot(key); }} onRemove={(key, title) => { handleRemoveSlot(key, title); setViewSlot(null); }} /> { if (openSlot) dispatch({ type: "setSlot", key: openSlot, recipeId }); setOpenSlot(null); }} /> ); }