feat(04-01): wire BrowserRouter + BottomTabBar + empty Lists surface

- App.tsx: BrowserRouter with /calendar, /lists, /lists/:listId routes; / redirects to /calendar
- BottomTabBar.tsx: fixed-bottom 56px tab bar with Calendar + Lists NavLinks, active accent
- AppNav.tsx: add Calendar/Lists NavLinks to desktop sidebar (≥768px)
- ListsIndex.tsx: full-height surface with isLoading/isError/empty state branches; "+ New List" FAB placeholder
- ListDetail.tsx: placeholder stub for /lists/:listId (Plan 04-04 fills in)
- listsStore.ts: Zustand UI-only store (activeTab, createListSheetOpen)
- listsClient.ts: fetchLists + List/ListItem types (initial; Plans 04-02/03 expand)
- Fix Wave-0 RED stubs: add vitest imports so stubs execute (todo) not error on import
- Fix CalendarShell.test.tsx: wrap renderWithClient in MemoryRouter (AppNav uses NavLink)
This commit is contained in:
Lucas Berger
2026-06-09 12:03:52 -04:00
parent 2f25b15949
commit c0088edf44
10 changed files with 563 additions and 3 deletions
+36
View File
@@ -0,0 +1,36 @@
/**
* Zustand UI-state store for the lists surface.
*
* Owns ONLY UI-shape state — no server data ever enters this store.
* Server state (lists, items) lives in TanStack Query.
*
* Convention: follows calendarStore.ts pattern — no persist middleware,
* no immer. State is ephemeral; lists UI state does not need persistence.
*
* State contract:
* activeTab — which bottom-tab is selected (drives tab active-state)
* createListSheetOpen — whether the "New List" sheet/dialog is open
*/
import { create } from 'zustand'
// ── Types ──────────────────────────────────────────────────────────────────
export interface ListsStore {
// UI-only state — no server data
activeTab: 'calendar' | 'lists'
createListSheetOpen: boolean
setActiveTab: (tab: 'calendar' | 'lists') => void
setCreateListSheetOpen: (open: boolean) => void
}
// ── Store ──────────────────────────────────────────────────────────────────
export const useListsStore = create<ListsStore>()((set) => ({
activeTab: 'calendar',
createListSheetOpen: false,
setActiveTab: (tab) => set({ activeTab: tab }),
setCreateListSheetOpen: (open) => set({ createListSheetOpen: open }),
}))