23cb057f0b
Wave 1: 07-01-PLAN.md — Unified services table (audit trail, backfill, validation) Wave 2: 07-02-PLAN.md — Admin catalog CRUD rewire (preserve historical references) Ready for execution: /gsd-execute-phase 7 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
115 lines
5.6 KiB
Markdown
115 lines
5.6 KiB
Markdown
---
|
|
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*
|