Move UI to BoardUI with slate + salmon theme; add PDF plan export
- Adopt BoardUI (Pro init): theme tokens, Inter, .dark mode, Button, SegmentedControl, Meter, Skeleton and ThemeToggle; remap the app's color aliases onto BoardUI tokens and swap lucide for Remix icons - Retint BoardUI's neutral ramp to cool slate with a salmon accent; drop the dotted background; raise muted text contrast - Simplify the landing page and remove the overlap explorer - Planner: explicit "I have this" toggle on grocery lines, pantry panel above the menu with the staples list spelled out, sticky aisle header fix, and a "Download PDF" plan export (jsPDF, lazy-loaded) - Recipes: replace the family cabbage soup with Cade's Cabbage Soup (adds Bone-In Ham Steak to the catalog); remove Ham, Bean & Cabbage Soup - Remove unused BoardUI components, dependencies and exports Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b02c7a024b
commit
df5dd3a70e
56 changed files with 4749 additions and 788 deletions
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { ChevronDown, Search, X } from "lucide-react";
|
||||
import { RiArrowDownSLine as ChevronDown, RiSearchLine as Search, RiCloseLine as X } from "@remixicon/react";
|
||||
import { useMemo, useState, type Dispatch } from "react";
|
||||
import { CATALOG, CATALOG_BY_ID, PANTRY_STAPLE_IDS } from "@/app/lib/catalog";
|
||||
import { RECIPES } from "@/app/lib/recipes";
|
||||
|
|
@ -8,6 +8,9 @@ import type { PlanAction } from "@/app/lib/usePlanState";
|
|||
import type { Aisle, ItemId, ItemPreference, MealPlanState, StoreItem } from "@/app/types";
|
||||
import { RadioGroup, type RadioOption } from "./RadioGroup";
|
||||
|
||||
// Shown beside the staples toggle so people know exactly what it marks as $0.
|
||||
const STAPLE_NAMES = PANTRY_STAPLE_IDS.map((id) => CATALOG_BY_ID.get(id)?.name).filter(Boolean).join(", ");
|
||||
|
||||
interface IngredientPanelProps {
|
||||
state: MealPlanState;
|
||||
dispatch: Dispatch<PlanAction>;
|
||||
|
|
@ -63,7 +66,7 @@ export function IngredientPanel({ state, dispatch }: IngredientPanelProps) {
|
|||
const staplesAllOnHand = PANTRY_STAPLE_IDS.every((id) => state.onHandItems.includes(id));
|
||||
|
||||
return (
|
||||
<section aria-labelledby="ingredients-heading" className="rounded-xl border border-line bg-surface">
|
||||
<section aria-labelledby="ingredients-heading" className="rounded-3xl border border-border-button-default bg-surface">
|
||||
<details className="group">
|
||||
<summary className="flex cursor-pointer list-none items-center justify-between gap-4 p-4 sm:px-5 [&::-webkit-details-marker]:hidden">
|
||||
<div>
|
||||
|
|
@ -115,10 +118,11 @@ export function IngredientPanel({ state, dispatch }: IngredientPanelProps) {
|
|||
<RadioGroup name="Filter ingredients" options={FILTER_OPTIONS} value={filter} onChange={setFilter} size="xs" />
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-xs sm:text-sm">
|
||||
<div className="mt-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs sm:text-sm">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPreference(PANTRY_STAPLE_IDS, staplesAllOnHand ? "use" : "have")}
|
||||
aria-describedby="pantry-staples-list"
|
||||
className="min-h-8 font-medium text-brand-300 hover:underline"
|
||||
>
|
||||
{staplesAllOnHand ? "Unmark pantry staples" : "Mark basic pantry staples as on hand"}
|
||||
|
|
@ -132,6 +136,9 @@ export function IngredientPanel({ state, dispatch }: IngredientPanelProps) {
|
|||
Reset all to “Use”
|
||||
</button>
|
||||
)}
|
||||
<p id="pantry-staples-list" className="basis-full text-xs text-ink-muted">
|
||||
Staples: {STAPLE_NAMES}.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 max-h-[28rem] space-y-5 overflow-y-auto pr-1">
|
||||
|
|
@ -139,7 +146,8 @@ export function IngredientPanel({ state, dispatch }: IngredientPanelProps) {
|
|||
|
||||
{groups.map(([aisle, items]) => (
|
||||
<section key={aisle}>
|
||||
<div className="sticky top-0 z-10 flex items-center justify-between border-b border-line bg-surface py-1.5">
|
||||
{/* z-20: BoardUI segment buttons are z-10, so the sticky header must sit above them while rows scroll under it */}
|
||||
<div className="sticky top-0 z-20 flex items-center justify-between border-b border-line bg-surface py-1.5">
|
||||
<h3 className="font-mono text-xs font-semibold tracking-wider text-ink-muted uppercase">{aisle}</h3>
|
||||
<AisleBulkToggle itemIds={items.map((i) => i.id)} avoidedItems={state.avoidedItems} onSet={setPreference} />
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue