- SUMMARY.md: 2 tasks, 6 files, commitsc398d6dand745f8a7- activeOffers in ProjectView/ClientView (public_name only, T-05-09/10 mitigated) - /admin/forecast 12-month RSC table complete
6.6 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 05-offer-system | 04 | ui |
|
|
|
|
|
|
|
|
|
~10 minutes | 2026-05-30 |
Phase 5 Plan 4: Client dashboard offer card + /admin/forecast revenue forecast page Summary
Client dashboard now shows active offers per project (public_name + cumulative service price + accepted_total), and /admin/forecast displays a 12-month server-side revenue projection spread from project_offers.accepted_total across duration_months.
Performance
- Duration: ~10 minutes
- Started: 2026-05-30T00:00:00Z
- Completed: 2026-05-30T00:00:00Z
- Tasks: 2
- Files modified: 6
Accomplishments
- Extended
ProjectViewandClientViewinterfaces withactiveOffersfield (public_name, cumulative_price, accepted_total only — never internal_name) - Created
forecast-queries.tswithgetRevenueForecast12Months()— builds 12 monthly buckets from current month and distributes each offer's accepted_total evenly across its duration_months window - Built
OffersSection.tsxclient component showing per-offer card with public name and prices — zero internal data exposed - Wired OffersSection into client-dashboard sidebar (conditionally rendered when offers exist), and mapped activeOffers through the projectViewToClientView adapter
- Delivered
/admin/forecastRSC page — table with month, projected revenue, proportional bar; totals row; Italian locale labels; empty-state message
Task Commits
Each task was committed atomically:
- Task 1: Extend client-view.ts + forecast-queries.ts -
c398d6d(feat) - Task 2: OffersSection + client dashboard wiring + /admin/forecast page -
745f8a7(feat)
Files Created/Modified
src/lib/client-view.ts— Added offer table imports, sql import; activeOffers to ProjectView and ClientView interfaces; projectOfferRows + cumulativePriceRows queries in getProjectView(); activeOffers in returnsrc/lib/forecast-queries.ts— New file: ForecastMonth type, getRevenueForecast12Months() with 12-bucket JS algorithmsrc/components/client/OffersSection.tsx— New RSC-compatible component: renders offer card per entry with public_name, cumulative_price, accepted_totalsrc/components/client-dashboard.tsx— Added OffersSection import; Offerte Attive sidebar block between Pagamenti and Documentisrc/app/client/[token]/page.tsx— Extended projectViewToClientView adapter to map activeOfferssrc/app/admin/forecast/page.tsx— New RSC page at /admin/forecast with table, bar column, totals row, empty state
Decisions Made
activeOffersreturnsundefined(not[]) when empty — allows simple truthy check in dashboard (view.activeOffers && view.activeOffers.length > 0)- Security comment in
client-view.tsoffer query explicitly namesinternal_nameas excluded — this is a documentation discipline from the existing payment amount comment pattern - Forecast uses
offer_micros.duration_monthsnot a hardcoded value — ties forecast accuracy directly to micro-offer configuration maxTotalfor bar width computed inside themapon each row iteration (idiomatic React, acceptable for 12 rows)
Deviations from Plan
None — plan executed exactly as written.
Issues Encountered
None.
Known Stubs
None — all data paths wire to live DB queries. OffersSection receives real project_offers data. getRevenueForecast12Months reads from production DB.
Threat Flags
No new trust boundaries beyond those in the plan's threat model. T-05-09 mitigated by explicit column selection (no internal_name in SELECT). T-05-10 mitigated by OffersSection receiving only public_name prop (confirmed by grep: 0 internal_name occurrences). T-05-12 mitigated by server-side RSC execution of getRevenueForecast12Months.
Self-Check: PASSED
src/lib/forecast-queries.tsexists: FOUND (c398d6d)src/components/client/OffersSection.tsxexists: FOUND (745f8a7)src/app/admin/forecast/page.tsxexists: FOUND (745f8a7)src/lib/client-view.tsactiveOffers count: 4 matches (interface×2 + query + return)grep "internal_name" src/components/client/OffersSection.tsx: 0 matchesgrep "internal_name" src/app/admin/forecast/page.tsx: 0 matchesnpx tsc --noEmit: PASSED (no errors)- Task 1 commit
c398d6d: FOUND - Task 2 commit
745f8a7: FOUND
Phase: 05-offer-system Completed: 2026-05-30