feat(projects): project page, add/edit flow and navbar deep links from Figma - #321
Merged
Conversation
…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>
Contributor
|
🌿 ⏳ Creating preview environment… (logs) |
Contributor
🌿 Preview environment — ready ✅Open: https://d3nmtjoh6ir9ym.cloudfront.net/pr-321/ 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 |
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>
Contributor
…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>
Contributor
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>
Contributor
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>
Contributor
Contributor
|
🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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}/overviewreturning the project, its members and its expenditures together, so the page costs one round trip rather than N+1.GET /projectsis enriched withtotal_spent,member_countandis_active, so the list page stops recomputing them per card.GET /projects/assignable-staffbacks the staff picker.POST /projectsandPUT /projects/{id}accept a member roster; membership is synced inside a transaction so a partial failure cannot leave a project with the wrong staff./projects/assignable-staffcan't be swallowed by the/projects/{id}pattern.openapi.yamlis updated to match.Frontend
Button,SectionHeading,SpendingDonut,FundingSummary,DatePickerField,StaffPicker,lib/format.ts.useAnchoredPopoverpositions 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.
ProjectCarduses 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 bygenerateStaticParams, 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=1is 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()inlib/routes.tsowns 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
tsc --noEmitclean; 313 tests pass across 31 suitestsc --noEmitclean; 50 unit tests pass○ (Static), no dynamic route remains/projects/{id}/overview,/auth/me,/projects/assignable-staffall 200Notes
no-reviewat creation as requested.