feat(frontend): /projects and /projects/new pages + first-time flow (#15) - #32
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Closes #15.
What
Phase 4, part two: a place to create projects, a place to see them all, and a first-run path for users who have neither.
/projects/new— name field with a live slug preview. The action derives the slug server-side (the preview is only a preview, the browser doesn't get to pick it), creates the project, writescurrentProjectID, and lands on/dashboard./projects— every project the user belongs to, with slug, role, creation date, and a marker on the current one. Clicking a row posts to/projects/switchwithredirectTo=/dashboard, so the session write and its membership re-check stay in one place./projects/new, which is exempt from the check and is the one protected page that renders without a current project. It sits after the existing stale-session correction so the cookie is cleaned up first.lib/slug.ts—slugify(), shared by the preview and the action. Output always satisfies the broker'sdata.ValidSlugregex or is empty, which the action rejects with a message rather than sending upstream.useProjects()— reads projects off the layout loader instead of re-fetching them per page, mirroringuseCsrfToken./projectsis reachable from the sidebar.Roles required a backend change
GET /projectsreturned bare projects, so "lists all user projects with correct roles" was unreachable from the frontend without an N+1 ofGET /projects/:id/members. Instead:data.UserProject(project + the caller's role), returned byGetProjectsForUser, which now zips the membership roles it was already fetching into the result.RPCServer.ListUserProjectsand the broker'sListProjectscarry the new type. Verified the embedded struct survives the gob hop and serializes flat as{id, name, slug, created_at, role}.Drive-by fix
POST /projectsleaked the raw Mongo duplicate-key error on a slug collision. It now mapsE11000to a 409 with "a project with that slug already exists", which is what the form shows. Matching on the string is the existing idiom here —net/rpcflattens errors, soerrors.Iscan't cross the wire (same as the last-owner guard).Notes for review
feat/multi-tenancy, notmain./dashboard,/keys, and/settingsstill read?projectId=from the query string, so they show their "select a project" empty state no matter what is in the session. Untouched by this PR — that migration is [Phase 4] Update API client and existing pages to use current project scope #17./projects*broker routes were undocumented since [Phase 3] Add project management routes to broker #13, so the internal routes table is filled in alongside the two rows this PR changes.Verification
npm run typecheck,oxlint,oxfmt --check, andnpm run buildclean.GetProjectsForUsernow asserts owner vs. member roles, and a newTestInsertProject_DuplicateSlugpins theE11000substring the 409 mapping depends on.TestProjectIsolation_*failed on one full integration run and passed on retry; they fail the same way on an untouchedfeat/multi-tenancy, so they are flaky here and unrelated.🤖 Generated with Claude Code