From 1001320b3620672254b3285bf6c332b431c3affb Mon Sep 17 00:00:00 2001 From: Simone Cavalli Date: Sat, 30 May 2026 16:39:21 +0200 Subject: [PATCH] =?UTF-8?q?docs(05-03):=20complete=20plan=203=20=E2=80=94?= =?UTF-8?q?=20OffersTab=20+=20project=20offer=20assignment=20+=20client=20?= =?UTF-8?q?active=20offers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../phases/05-offer-system/05-03-SUMMARY.md | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 .planning/phases/05-offer-system/05-03-SUMMARY.md diff --git a/.planning/phases/05-offer-system/05-03-SUMMARY.md b/.planning/phases/05-offer-system/05-03-SUMMARY.md new file mode 100644 index 0000000..21b8813 --- /dev/null +++ b/.planning/phases/05-offer-system/05-03-SUMMARY.md @@ -0,0 +1,114 @@ +--- +plan_id: 05-03 +phase: 5 +plan: 3 +subsystem: admin-ui +tags: [offer-system, admin, project-workspace, server-actions, rsc, tabs] +dependency_graph: + requires: + - phase: 05-01 + provides: [project_offers, offer_micros tables and TypeScript types] + - phase: 05-02 + provides: [offer-catalog-admin-ui, offer-queries.ts] + provides: [OffersTab, project-offer-assignment, getClientActiveOffers, client-active-offers-section] + affects: [src/app/admin/projects, src/app/admin/clients, src/lib/admin-queries.ts] +tech_stack: + added: [] + patterns: [useTransition + router.refresh pattern, onBlur inline save, parallel Promise.all data fetch extension, requireAdmin guard in server actions] +key_files: + created: + - src/components/admin/tabs/OffersTab.tsx + modified: + - src/lib/admin-queries.ts + - src/app/admin/projects/project-actions.ts + - src/app/admin/projects/[id]/page.tsx + - src/app/admin/clients/[id]/page.tsx +key-decisions: + - "OffersTab uses useTransition + router.refresh() (Pattern 3) consistent with ServiceCheckboxList from Plan 02" + - "accepted_total inline edit uses onBlur save to avoid accidental submissions during typing" + - "getClientActiveOffers selects only public_name, never internal_name — enforced by explicit column selection and comment" + - "getProjectFullDetail extended via additional queries in existing Promise.all — no separate function needed" + - "assignOfferToProject inserts with nanoid default (schema $defaultFn) — no explicit id generation in action" +requirements-completed: [OFFER-04] +metrics: + duration: ~15 minutes + completed: 2026-05-30 + tasks_completed: 3 + files_modified: 5 +--- + +# Phase 5 Plan 3: Project offer assignment — OffersTab in project workspace Summary + +**OffersTab client component for assigning micro-offers to projects with inline accepted_total editing, plus active-offers summary section on the client detail page showing public_name per project.** + +## Performance + +- **Duration:** ~15 minutes +- **Started:** 2026-05-30T00:00:00Z +- **Completed:** 2026-05-30T00:00:00Z +- **Tasks:** 3 +- **Files modified:** 5 + +## Accomplishments + +- Offerte tab added to project workspace at /admin/projects/[id] — admin can assign micro-offers, set accepted_total, and remove assignments +- Three new server actions (assignOfferToProject, removeProjectOffer, updateProjectOfferTotal) all guarded by requireAdmin() and revalidating /admin/forecast +- getClientActiveOffers() query added to admin-queries.ts showing public_name + project name at /admin/clients/[id] + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Extend getProjectFullDetail + add project offer server actions** - `b3f781b` (feat) +2. **Task 2: OffersTab component + project workspace page update** - `0c09d44` (feat) +3. **Task 3: Add getClientActiveOffers query + client detail page active offers summary** - `20f4fd8` (feat) + +## Files Created/Modified + +- `src/components/admin/tabs/OffersTab.tsx` - Client component with assign form, offer list, inline accepted_total editing, and remove button +- `src/lib/admin-queries.ts` - Added ProjectOfferWithMicro type, extended ProjectFullDetail, added project offer queries to getProjectFullDetail(), added ClientActiveOfferSummary type and getClientActiveOffers() function +- `src/app/admin/projects/project-actions.ts` - Added assignOfferToProject, removeProjectOffer, updateProjectOfferTotal server actions with Zod validation +- `src/app/admin/projects/[id]/page.tsx` - Added OffersTab import, destructured projectOffers/availableMicros, added Offerte tab trigger and content +- `src/app/admin/clients/[id]/page.tsx` - Added getClientActiveOffers parallel fetch, added Offerte Attive section at bottom + +## Decisions Made + +- Used `useTransition + router.refresh()` pattern consistent with the rest of the admin UI (established in Plan 02) +- `onBlur` for accepted_total inline save avoids mid-typing submissions while still feeling responsive +- `getClientActiveOffers` uses explicit `.select({ public_name: offer_micros.public_name })` with a comment — never internal_name — to enforce the CLAUDE.md architecture constraint (quote_items / offer internals not exposed to client) +- Extended the existing `Promise.all` in `getProjectFullDetail` rather than adding a separate query — keeps the function's parallel structure intact + +## Deviations from Plan + +None — plan executed exactly as written. All three tasks followed the plan's action specifications without modification. + +## Issues Encountered + +None. + +## Known Stubs + +None — all offer data is wired to live DB queries. OffersTab shows real project_offers rows from the database. getClientActiveOffers returns real data from the production DB. + +## Threat Flags + +No new trust boundaries beyond those already in the plan's threat model (T-05-06, T-05-07, T-05-08). All three mutating server actions call requireAdmin() as their first operation. OffersTab renders micro_internal_name only under /admin/projects/* which is Auth.js session-guarded. + +## Self-Check + +- `src/components/admin/tabs/OffersTab.tsx` exists: FOUND +- `src/lib/admin-queries.ts` — projectOffers field in ProjectFullDetail: FOUND +- `src/lib/admin-queries.ts` — getClientActiveOffers exported: FOUND +- `src/app/admin/projects/project-actions.ts` — 3 offer actions: FOUND +- `src/app/admin/projects/[id]/page.tsx` — OffersTab import + usage: FOUND (2 matches) +- `src/app/admin/clients/[id]/page.tsx` — activeOffers render: FOUND (4 matches) +- Task 1 commit b3f781b: FOUND +- Task 2 commit 0c09d44: FOUND +- Task 3 commit 20f4fd8: FOUND +- npx tsc --noEmit: PASSED + +## Self-Check: PASSED + +--- +*Phase: 05-offer-system* +*Completed: 2026-05-30*