Files
simone 23cb057f0b docs(07): phase 7 planning complete — 2 plans (schema migration + admin crud)
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>
2026-06-11 06:17:29 +02:00

5.6 KiB

plan_id, phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, key-decisions, requirements-completed, metrics
plan_id phase plan subsystem tags dependency_graph tech_stack key_files key-decisions requirements-completed metrics
05-03 5 3 admin-ui
offer-system
admin
project-workspace
server-actions
rsc
tabs
requires provides affects
phase provides
05-01
project_offers
offer_micros tables and TypeScript types
phase provides
05-02
offer-catalog-admin-ui
offer-queries.ts
OffersTab
project-offer-assignment
getClientActiveOffers
client-active-offers-section
src/app/admin/projects
src/app/admin/clients
src/lib/admin-queries.ts
added patterns
useTransition + router.refresh pattern
onBlur inline save
parallel Promise.all data fetch extension
requireAdmin guard in server actions
created modified
src/components/admin/tabs/OffersTab.tsx
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
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
OFFER-04
duration completed tasks_completed files_modified
~15 minutes 2026-05-30 3 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