From dfaab1dfcb624ea23bc6eed556df4ba6a3f30ae1 Mon Sep 17 00:00:00 2001 From: Nico Date: Wed, 28 Jan 2026 01:06:19 -0800 Subject: [PATCH] add handling of no stores and fix app naming --- frontend/index.html | 23 ++++---- frontend/src/pages/GroceryList.jsx | 71 +++++++++++++++++++++-- frontend/src/pages/Manage.jsx | 11 +++- frontend/src/styles/pages/GroceryList.css | 25 ++++++++ 4 files changed, 113 insertions(+), 17 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 6e33df9..285654b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,12 +1,15 @@ - - - - Costco Grocery List - - -
- - - + + + + + Grocery App + + + +
+ + + + \ No newline at end of file diff --git a/frontend/src/pages/GroceryList.jsx b/frontend/src/pages/GroceryList.jsx index 34cd4bd..f49786d 100644 --- a/frontend/src/pages/GroceryList.jsx +++ b/frontend/src/pages/GroceryList.jsx @@ -1,4 +1,5 @@ import { useCallback, useContext, useEffect, useMemo, useState } from "react"; +import { useNavigate } from "react-router-dom"; import { addItem, getClassification, @@ -30,13 +31,16 @@ import { findSimilarItems } from "../utils/stringSimilarity"; export default function GroceryList() { + const pageTitle = "Grocery List"; const { role: systemRole } = useContext(AuthContext); const { activeHousehold } = useContext(HouseholdContext); - const { activeStore } = useContext(StoreContext); + const { activeStore, stores, loading: storeLoading } = useContext(StoreContext); const { settings } = useContext(SettingsContext); + const navigate = useNavigate(); // Get household role for permissions const householdRole = activeHousehold?.role; + const isHouseholdAdmin = householdRole === "admin"; // === State === // const [items, setItems] = useState([]); @@ -494,13 +498,68 @@ export default function GroceryList() { }; - if (!activeHousehold || !activeStore) { + if (!activeHousehold) { return (
-

Costco Grocery List

+

{pageTitle}

- {!activeHousehold ? 'Loading households...' : 'Loading stores...'} + Loading households... +

+
+
+ ); + } + + if (storeLoading) { + return ( +
+
+

{pageTitle}

+

+ Loading stores... +

+
+
+ ); + } + + if (!storeLoading && stores.length === 0) { + return ( +
+
+

{pageTitle}

+
+

No stores found

+

+ This household doesn’t have any stores yet. +

+ {isHouseholdAdmin ? ( + + ) : ( +

+ Please notify a household admin to add a store. +

+ )} +
+
+
+ ); + } + + if (!activeStore) { + return ( +
+
+

{pageTitle}

+ +

+ Loading stores...

@@ -511,7 +570,7 @@ export default function GroceryList() { return (
-

Costco Grocery List

+

{pageTitle}

Loading grocery list...

@@ -523,7 +582,7 @@ export default function GroceryList() { return (
-

Costco Grocery List

+

{pageTitle}

diff --git a/frontend/src/pages/Manage.jsx b/frontend/src/pages/Manage.jsx index 5e55947..6406dc2 100644 --- a/frontend/src/pages/Manage.jsx +++ b/frontend/src/pages/Manage.jsx @@ -1,4 +1,5 @@ -import { useContext, useState } from "react"; +import { useContext, useEffect, useState } from "react"; +import { useSearchParams } from "react-router-dom"; import ManageHousehold from "../components/manage/ManageHousehold"; import ManageStores from "../components/manage/ManageStores"; import { HouseholdContext } from "../context/HouseholdContext"; @@ -7,6 +8,14 @@ import "../styles/pages/Manage.css"; export default function Manage() { const { activeHousehold } = useContext(HouseholdContext); const [activeTab, setActiveTab] = useState("household"); + const [searchParams] = useSearchParams(); + + useEffect(() => { + const tab = searchParams.get("tab"); + if (tab === "household" || tab === "stores") { + setActiveTab(tab); + } + }, [searchParams]); if (!activeHousehold) { return ( diff --git a/frontend/src/styles/pages/GroceryList.css b/frontend/src/styles/pages/GroceryList.css index 7a0b64b..cd0a15b 100644 --- a/frontend/src/styles/pages/GroceryList.css +++ b/frontend/src/styles/pages/GroceryList.css @@ -64,6 +64,31 @@ padding-top: var(--spacing-md); } +/* Empty State */ +.glist-empty-state { + margin: var(--spacing-xl) auto; + padding: var(--spacing-lg); + text-align: center; + display: flex; + flex-direction: column; + gap: var(--spacing-md); + max-width: 32rem; + border: var(--border-width-thin) dashed var(--color-border-medium); + border-radius: var(--border-radius-lg); + background: var(--color-bg-surface); +} + +.glist-empty-title { + font-size: var(--font-size-lg); + color: var(--color-text-primary); + margin: 0; +} + +.glist-empty-text { + color: var(--color-text-secondary); + margin: 0; +} + .glist-section-header .glist-section-title { margin: 0; border: none;