From 7eb5335a888509db39d4a8d148e5a2865c037ad5 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Mon, 13 Apr 2026 18:05:24 +0200 Subject: [PATCH] docs: add phase 32 plan summaries Co-Authored-By: Claude Opus 4.6 (1M context) --- .../32-setup-sharing-system/32-01-SUMMARY.md | 42 ++++++++++++++++++ .../32-setup-sharing-system/32-02-SUMMARY.md | 43 +++++++++++++++++++ .../32-setup-sharing-system/32-03-SUMMARY.md | 33 ++++++++++++++ .../32-setup-sharing-system/32-04-SUMMARY.md | 33 ++++++++++++++ 4 files changed, 151 insertions(+) create mode 100644 .planning/phases/32-setup-sharing-system/32-01-SUMMARY.md create mode 100644 .planning/phases/32-setup-sharing-system/32-02-SUMMARY.md create mode 100644 .planning/phases/32-setup-sharing-system/32-03-SUMMARY.md create mode 100644 .planning/phases/32-setup-sharing-system/32-04-SUMMARY.md diff --git a/.planning/phases/32-setup-sharing-system/32-01-SUMMARY.md b/.planning/phases/32-setup-sharing-system/32-01-SUMMARY.md new file mode 100644 index 0000000..59a3f50 --- /dev/null +++ b/.planning/phases/32-setup-sharing-system/32-01-SUMMARY.md @@ -0,0 +1,42 @@ +# Plan 32-01 Summary: Schema Migration (isPublic -> visibility) + +**Status:** Complete +**Commit:** edc9793 + +## What was done + +1. **Schema changes** (`src/db/schema.ts`): + - Replaced `isPublic: boolean` with `visibility: text` (default "private") on setups table + - Added `shares` table with columns: id, setupId, token, permission, expiresAt, userId, createdAt, revokedAt + - Removed `boolean` import from drizzle-orm/pg-core + +2. **Migration** (`drizzle-pg/0005_true_green_goblin.sql`): + - Creates shares table with FK constraints + - Adds visibility column with data migration (`UPDATE setups SET visibility = 'public' WHERE is_public = true`) + - Drops is_public column + +3. **Full-stack isPublic -> visibility replacement** across 16 files: + - `src/shared/schemas.ts`: `z.enum(["private", "link", "public"])` replaces `z.boolean()` + - `src/server/services/setup.service.ts`: createSetup, getAllSetups, updateSetup + - `src/server/services/discovery.service.ts`: `eq(setups.visibility, "public")` + - `src/server/services/profile.service.ts`: Two occurrences updated + - `src/server/routes/account.ts`: Delete account reassignment query + - `src/client/hooks/useSetups.ts`: Types and mutation signatures + - `src/client/components/SetupCard.tsx`: Visibility badge (public=green, link=blue) + - `src/client/components/SetupsView.tsx`: Passes visibility prop + - `src/client/routes/setups/$setupId.tsx`: Temporary visibility badge with lock/link/globe icons + - `src/db/dev-seed.ts` and `src/db/dev-seed-data.ts`: Seed data updated + +4. **Tests updated** across 4 test files (46 tests pass): + - `tests/services/profile.service.test.ts` + - `tests/services/discovery.service.test.ts` + - `tests/routes/discovery.test.ts` + - `tests/routes/profiles.test.ts` + - `tests/helpers/db.ts`: Added shares to truncation list + +## Verification + +- `bun run lint`: Passes (0 errors) +- All affected tests pass (46/46) +- Zero isPublic/is_public references in src/ (except unrelated `isPublicRoute` in __root.tsx) +- Zero isPublic references in tests/ diff --git a/.planning/phases/32-setup-sharing-system/32-02-SUMMARY.md b/.planning/phases/32-setup-sharing-system/32-02-SUMMARY.md new file mode 100644 index 0000000..6692f67 --- /dev/null +++ b/.planning/phases/32-setup-sharing-system/32-02-SUMMARY.md @@ -0,0 +1,43 @@ +# Plan 32-02 Summary: Share Link Backend + +**Status:** Complete +**Commit:** da159d1 + +## What was done + +1. **Share service** (`src/server/services/share.service.ts`): + - `createShareLink`: 128-bit random base64url token, configurable expiration + - `getShareLinks`: Lists all shares for a setup (ownership verified) + - `revokeShareLink`: Sets revokedAt (ownership verified via join) + - `validateShareToken`: Returns setupId/permission, rejects expired/revoked/nonexistent + - `deactivateShareLinks`: Bulk revoke all active links for a setup + - `reactivateShareLinks`: Clears revokedAt on non-expired shares + +2. **Visibility transition side effects** (`src/server/services/setup.service.ts`): + - `updateSetup` now detects visibility transitions and calls deactivate/reactivate + - Uses dynamic import to avoid circular dependency + +3. **New function** `getSetupWithItemsById` for share-token-authorized access (no user/visibility check) + +4. **API routes** (added to `src/server/routes/setups.ts`): + - `POST /api/setups/:id/shares` — Create share link (auth required) + - `GET /api/setups/:id/shares` — List share links (auth required) + - `DELETE /api/setups/:id/shares/:shareId` — Revoke share link (auth required) + +5. **Public endpoints** (added to `src/server/index.ts`): + - `GET /api/shared/:token` — Access setup via share token (no auth) + - `GET /s/:token` — Short URL redirect to `/setups/:id?share=:token` + - Auth middleware skip for `/api/shared/` and rate limiting applied + +6. **Share schema** (`src/shared/schemas.ts`): + - `createShareLinkSchema` with `expiresInDays: 7 | 14 | 30 | null` + +7. **Tests** (`tests/services/share.service.test.ts`): + - 16 tests covering all service functions and visibility transitions + - All pass (62/62 across 5 affected test files) + +## Verification + +- `bun run lint`: Passes +- All share service tests pass (16/16) +- All affected tests pass (62/62 across 5 files) diff --git a/.planning/phases/32-setup-sharing-system/32-03-SUMMARY.md b/.planning/phases/32-setup-sharing-system/32-03-SUMMARY.md new file mode 100644 index 0000000..838c8f1 --- /dev/null +++ b/.planning/phases/32-setup-sharing-system/32-03-SUMMARY.md @@ -0,0 +1,33 @@ +# Plan 32-03 Summary: Share Modal UI + +**Status:** Complete +**Commit:** 7003e99 + +## What was done + +1. **Share hooks** (`src/client/hooks/useShares.ts`): + - `useShareLinks(setupId)` — Query hook for fetching share links + - `useCreateShareLink(setupId)` — Mutation with query invalidation + - `useRevokeShareLink(setupId)` — Mutation with query invalidation + +2. **ShareModal component** (`src/client/components/ShareModal.tsx`): + - Visibility picker with three options (private/link/public) — immediate API call on change + - Color-coded options: gray (private), blue (link), green (public) + - Share link creation with expiration dropdown (7/14/30 days, no expiration) + - Active links list with copy-to-clipboard and revoke actions + - Deactivation warning when links exist and switching to private + - Empty state "No share links yet" + - Escape key and overlay click to close + - Responsive: works on desktop and mobile + +3. **Setup detail page update** (`src/client/routes/setups/$setupId.tsx`): + - Replaced static visibility badge with interactive share button + - Desktop: "Share" text + visibility icon + - Mobile: Icon-only with 44px touch target + - ShareModal rendered with visibility change wired to `updateSetup.mutate` + +## Verification + +- `bun run lint`: Passes +- ShareModal follows existing modal patterns (overlay, escape key, z-50) +- Colors match UI-SPEC: gray-500/gray-50, blue-600/blue-50, green-700/green-50 diff --git a/.planning/phases/32-setup-sharing-system/32-04-SUMMARY.md b/.planning/phases/32-setup-sharing-system/32-04-SUMMARY.md new file mode 100644 index 0000000..8a21817 --- /dev/null +++ b/.planning/phases/32-setup-sharing-system/32-04-SUMMARY.md @@ -0,0 +1,33 @@ +# Plan 32-04 Summary: Shared Setup Viewer + +**Status:** Complete +**Commit:** 0b46eff + +## What was done + +1. **useSharedSetup hook** (`src/client/hooks/useSetups.ts`): + - Fetches `/api/shared/:token` with retry disabled (404 = invalid token) + - Returns same SetupWithItems type as other setup hooks + +2. **Route search params** (`src/client/routes/setups/$setupId.tsx`): + - Added `validateSearch` with Zod schema for `share` query param + - Three-way data source: share token > authenticated owner > public viewer + +3. **Shared setup banner**: + - Blue banner with link icon: "Shared setup" shown when share token present + - Positioned above the sticky header bar + +4. **Error state for invalid tokens**: + - Shows "Link not available" with link icon and descriptive text + - Renders instead of the normal page when shared fetch errors + +5. **Read-only mode**: + - `showOwnerControls` computed from `!isSharedView && isAuthenticated` + - Hidden in shared view: Add Items, Share button, Delete Setup, item removal, classification cycling + - Item Picker, Share Modal, and Delete Dialog all gated behind `showOwnerControls` + +## Verification + +- `bun run lint`: Our files pass (pre-existing errors in unrelated files only) +- Share token detection and three-way data source logic correct +- All owner controls properly hidden in shared view