Skip to content

feat(projects): project page, add/edit flow and navbar deep links from Figma - #321

Merged
nourshoreibah merged 6 commits into
mainfrom
feat/projects-figma
Aug 12, 2026
Merged

feat(projects): project page, add/edit flow and navbar deep links from Figma#321
nourshoreibah merged 6 commits into
mainfrom
feat/projects-figma

Conversation

@nourshoreibah

@nourshoreibah nourshoreibah commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Implements the project page panel from Figma — the detail page, the add/edit project modal and all their states — and points the sidebar's Projects dropdown straight at a project instead of routing through the list page.

Figma: BRANCH Designs, node 3550-34375

Backend

The detail page used to assemble one view out of several calls. It now reads from a single GET /projects/{id}/overview returning the project, its members and its expenditures together, so the page costs one round trip rather than N+1.

  • GET /projects is enriched with total_spent, member_count and is_active, so the list page stops recomputing them per card.
  • GET /projects/assignable-staff backs the staff picker.
  • POST /projects and PUT /projects/{id} accept a member roster; membership is synced inside a transaction so a partial failure cannot leave a project with the wrong staff.
  • Route matching guards on numeric ids, so /projects/assignable-staff can't be swallowed by the /projects/{id} pattern.
  • Validation covers name, date range, currency and member roles; openapi.yaml is updated to match.

Frontend

  • Project detail page: funding summary with spending donut, expenses table, staff list, edit entry point.
  • Add/Edit project modal with inline validation, date pickers and a staff picker.
  • Sidebar Projects dropdown deep-links into each project.
  • New shared pieces: Button, SectionHeading, SpendingDonut, FundingSummary, DatePickerField, StaffPicker, lib/format.ts.
  • useAnchoredPopover positions and dismisses portalled popovers. A popover left in normal flow gets clipped by the modal body's scroll container; the hook plus a portal is how the date and staff pickers avoid that.

Sizing follows Figma but is expressed responsively rather than as fixed pixels. ProjectCard uses a container query for its stat columns, since how much room they have depends on the card's own grid track rather than the viewport — a card can be narrow on a wide screen.

A project is now addressed by query param

/projects?id=1, not /projects/1.

/projects/[id] could never work on this deployment and was already broken before this branch. A static export emits one document per id returned by generateStaticParams, and project ids are database rows, so every real id resolved to no document: CloudFront fell back to another page's shell and the client router rendered not-found. The new navbar dropdown would have hit that on every use.

/projects?id=1 is a single prerendered document that reads the id at runtime, so deep links, refreshes and in-app navigation work with no CloudFront rules to keep in step. projectPath() in lib/routes.ts owns the URL shape. The navbar takes the active project as a prop rather than parsing the path, because reading the query param there would force a Suspense boundary onto every page that renders the rail.

Note this changes the URL shape for projects. The old form never resolved on the deployed site, so nothing working depends on it.

Test plan

  • Frontend tsc --noEmit clean; 313 tests pass across 31 suites
  • Projects lambda tsc --noEmit clean; 50 unit tests pass
  • Frontend eslint clean
  • Static export build: every route is ○ (Static), no dynamic route remains
  • Deep link, card click-through and project-to-project dropdown navigation verified against a plain static server with no rewrite rules
  • No horizontal overflow at 390px, 768px or 1440px
  • Preview API verified: /projects/{id}/overview, /auth/me, /projects/assignable-staff all 200
  • Reviewer: click a project from the navbar dropdown on the preview

Notes

  • Labelled no-review at creation as requested.
  • The 181px sidebar still dominates phone widths. Nothing overflows, but making it a drawer is app-wide furniture with no mobile design in Figma, so it is deliberately out of scope here.

…m Figma

Builds the project detail page and the add/edit project modal to the Figma
design, and lets the sidebar jump straight into a project rather than making
users pass through the list page first.

The detail page previously fanned out to several endpoints to assemble one
view. It now loads from a single GET /projects/{id}/overview, which returns the
project, its members and its expenditures together, so the page renders in one
round trip instead of N+1. GET /projects is enriched with total_spent,
member_count and is_active so the list page no longer recomputes them per card,
and GET /projects/assignable-staff backs the staff picker.

Membership edits are synced transactionally against the submitted roster, so a
partially applied update cannot leave a project with the wrong staff.

Shared additions worth reusing: useAnchoredPopover positions and dismisses
portalled popovers, which is what keeps the date and staff pickers from being
clipped by the modal's scroll container.

Co-authored-by: Cursor <cursoragent@cursor.com>
@nourshoreibah nourshoreibah added the no-review The PR review bot won't run label Aug 12, 2026
@nourshoreibah nourshoreibah added the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 ⏳ Creating preview environment… (logs)

@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment — ready ✅

Open: https://d3nmtjoh6ir9ym.cloudfront.net/pr-321/
API: https://j9stadrbx6.execute-api.us-east-2.amazonaws.com/prod

Shared RDS + Cognito (prod data); DB migrations are not applied here — if this PR adds a migration, endpoints using the new columns will fail until it merges. New commits update this environment in place — a note is posted here on each update. Remove the test-environment label or close the PR to tear it down.

The static export prerenders /projects/[id] for one placeholder id and
CloudFront serves that single shell for every project, so on a hard load or
deep link useParams reports the placeholder rather than the id in the address
bar and the page fetches the wrong project.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Test environment updated in place ✅ — Click here to open. updated for 26b2abc · logs

…h segment

/projects/[id] could not work on a static export. The export emits one document
per id returned by generateStaticParams, and project ids are database rows, so
every real id resolved to no document: CloudFront fell back to another page's
shell and the client router rendered not-found. Every project deep link 404'd,
including the ones the new navbar dropdown produces.

/projects?id=1 is a single prerendered document that reads the id at runtime, so
deep links, refreshes and in-app navigation all work with no CloudFront rules
and no placeholder shell to keep in step with the infrastructure.

The navbar now takes the active project as a prop rather than parsing it out of
the path; reading the query param there would force a Suspense boundary onto
every page that renders the rail.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Test environment updated in place ✅ — Click here to open. updated for eb267ad · logs

Local-only scaffolding that does not belong in the deployed app. Reverting it
also removes the NEXT_DISABLE_STATIC_EXPORT escape hatch in next.config, which
existed solely to let `next dev` tolerate the dynamic /projects/[id] route that
no longer exists.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Test environment updated in place ✅ — Click here to open. updated for a9bf093 · logs

Chakra's reset zeroes padding on bare elements and outranks unprefixed Tailwind utilities, so the header lost its side gutters when its inline padding became px-4/sm:px-8. The portalled staff and date popovers were also unclickable, since Chakra marks every other body child non-interactive while a dialog is open, and a flipped popover was placed by its estimated rather than real height, leaving it floating over the description field. Also drop 'Submit for Review' on the project modal: projects have no review step.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Test environment updated in place ✅ — Click here to open. updated for e9d32d9 · logs

@nourshoreibah
nourshoreibah merged commit 1955c22 into main Aug 12, 2026
18 checks passed
@nourshoreibah
nourshoreibah deleted the feat/projects-figma branch August 12, 2026 05:43
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-review The PR review bot won't run test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant