11 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 20-fab-full-screen-catalog-search | 01 | execute | 1 |
|
true |
|
|
Purpose: Provides the data layer (tags API), state management (UIStore), and hooks that Plan 02's UI components will consume. Output: Working GET /api/tags endpoint, registered GET /api/global-items, extended UIStore, useTags hook, updated useGlobalItems hook with tag support.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/20-fab-full-screen-catalog-search/20-CONTEXT.md @.planning/phases/20-fab-full-screen-catalog-search/20-RESEARCH.md@src/server/index.ts @src/server/routes/global-items.ts @src/server/services/global-item.service.ts @src/db/schema.ts @src/client/stores/uiStore.ts @src/client/hooks/useGlobalItems.ts @src/client/lib/api.ts @tests/routes/global-items.test.ts @tests/helpers/db.ts
From src/db/schema.ts:
export const tags = pgTable("tags", {
id: serial("id").primaryKey(),
name: text("name").notNull().unique(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
From src/client/lib/api.ts:
export async function apiGet<T>(path: string): Promise<T>;
From src/client/stores/uiStore.ts (existing pattern):
export const useUIStore = create<UIState>((set) => ({ ... }));
2. Create `src/server/routes/tags.ts`:
- Single GET "/" handler that calls `getAllTags(db)` and returns `c.json(allTags)`.
- Export as `tagRoutes`.
- Follow the exact pattern from `src/server/routes/global-items.ts` (Hono + Env type).
3. Update `src/server/index.ts`:
- Add import: `import { globalItemRoutes } from "./routes/global-items.ts";`
- Add import: `import { tagRoutes } from "./routes/tags.ts";`
- Register both routes AFTER existing route registrations (before MCP block):
`app.route("/api/global-items", globalItemRoutes);`
`app.route("/api/tags", tagRoutes);`
- CRITICAL: The global-items route file EXISTS but is NOT registered (research pitfall #1). Must add it here.
- Add public route skip in auth middleware for tags: `if (c.req.path.startsWith("/api/tags") && c.req.method === "GET") return next();`
- Add public route skip for global-items: `if (c.req.path.startsWith("/api/global-items") && c.req.method === "GET") return next();`
4. Create `tests/services/tag.service.test.ts`:
- Use `createTestDb()` from `tests/helpers/db.ts`.
- Test: returns empty array when no tags.
- Test: returns all tags as `{ id, name }` after inserting test data.
- Seed tags using `db.insert(tags).values(...)`.
5. Create `tests/routes/tags.test.ts`:
- Follow pattern from `tests/routes/global-items.test.ts`.
- Use the app with test db injection.
- Test: GET /api/tags returns 200 with JSON array.
- Test: response contains expected tags after seeding.
bun test tests/services/tag.service.test.ts tests/routes/tags.test.ts tests/routes/global-items.test.ts
- File `src/server/services/tag.service.ts` exists and exports `getAllTags`
- File `src/server/routes/tags.ts` exists and exports `tagRoutes`
- `src/server/index.ts` contains `app.route("/api/tags", tagRoutes)`
- `src/server/index.ts` contains `app.route("/api/global-items", globalItemRoutes)`
- `src/server/index.ts` contains auth skip for `/api/tags` and `/api/global-items` GET requests
- All tag tests pass: `bun test tests/services/tag.service.test.ts tests/routes/tags.test.ts`
- Existing global-items tests still pass: `bun test tests/routes/global-items.test.ts`
GET /api/tags returns all tags. GET /api/global-items is registered and reachable. All tests green.
Task 2: UIStore extension + useTags hook + useGlobalItems tag support
src/client/stores/uiStore.ts,
src/client/hooks/useTags.ts,
src/client/hooks/useGlobalItems.ts
src/client/stores/uiStore.ts,
src/client/hooks/useGlobalItems.ts,
src/client/lib/api.ts,
.planning/phases/20-fab-full-screen-catalog-search/20-CONTEXT.md
1. Extend `src/client/stores/uiStore.ts` per D-21, D-22, D-23:
- Add to UIState interface:
```
fabMenuOpen: boolean;
openFabMenu: () => void;
closeFabMenu: () => void;
catalogSearchOpen: boolean;
catalogSearchMode: "collection" | "thread" | null;
openCatalogSearch: (mode: "collection" | "thread") => void;
closeCatalogSearch: () => void;
```
- Add to store implementation:
```
fabMenuOpen: false,
openFabMenu: () => set({ fabMenuOpen: true }),
closeFabMenu: () => set({ fabMenuOpen: false }),
catalogSearchOpen: false,
catalogSearchMode: null,
openCatalogSearch: (mode) => set({ catalogSearchOpen: true, catalogSearchMode: mode, fabMenuOpen: false }),
closeCatalogSearch: () => set({ catalogSearchOpen: false, catalogSearchMode: null }),
```
- Note: `openCatalogSearch` also closes the FAB menu (natural flow: tap FAB -> tap option -> menu closes, overlay opens).
2. Create `src/client/hooks/useTags.ts`:
- Export `useTags()` hook using `useQuery` from `@tanstack/react-query`.
- Query key: `["tags"]`.
- Query fn: `apiGet<Tag[]>("/api/tags")` where `Tag = { id: number; name: string }`.
- Set `staleTime: 5 * 60 * 1000` (tags change rarely, cache 5 min).
- Export the `Tag` interface as well.
3. Update `src/client/hooks/useGlobalItems.ts`:
- Modify `useGlobalItems` signature to accept optional `tags?: string[]` parameter.
- Build query string using `URLSearchParams`:
```
const params = new URLSearchParams();
if (query) params.set("q", query);
if (tags && tags.length > 0) params.set("tags", tags.join(","));
const qs = params.toString();
```
- Update query key to include tags: `["global-items", query ?? "", tags ?? []]`.
- Update query fn URL: `` `/api/global-items${qs ? `?${qs}` : ""}` ``.
- Keep all other exports (`useGlobalItem`, `useLinkItem`, `useUnlinkItem`) unchanged.
bun run lint && bun run build
- `src/client/stores/uiStore.ts` contains `fabMenuOpen` state field
- `src/client/stores/uiStore.ts` contains `catalogSearchOpen` state field
- `src/client/stores/uiStore.ts` contains `catalogSearchMode` state field
- `src/client/stores/uiStore.ts` contains `openCatalogSearch` action
- `src/client/stores/uiStore.ts` contains `closeCatalogSearch` action
- `src/client/hooks/useTags.ts` exists and exports `useTags`
- `src/client/hooks/useGlobalItems.ts` accepts `tags?: string[]` parameter
- `bun run lint` passes with no errors
- `bun run build` succeeds
UIStore has FAB menu + catalog search state. useTags hook fetches tags. useGlobalItems supports tag filtering. Lint and build pass.
- `bun test tests/services/tag.service.test.ts tests/routes/tags.test.ts tests/routes/global-items.test.ts` -- all green
- `bun run lint` -- no errors
- `bun run build` -- succeeds
- `bun test` -- full suite green (no regressions)
<success_criteria>
- GET /api/tags returns all tags as JSON array
- GET /api/global-items is registered and reachable (was missing from index.ts)
- UIStore has fabMenuOpen, catalogSearchOpen, catalogSearchMode state
- useTags hook works with staleTime caching
- useGlobalItems accepts optional tags parameter for filtering
- All existing tests pass (no regressions) </success_criteria>