Skip to content

feat(frontend): currentProjectID in session + sidebar project switcher (#14) - #31

Merged
jpricardo merged 1 commit into
feat/multi-tenancyfrom
feat/session-project-switcher
Aug 19, 2026
Merged

feat(frontend): currentProjectID in session + sidebar project switcher (#14)#31
jpricardo merged 1 commit into
feat/multi-tenancyfrom
feat/session-project-switcher

Conversation

@jpricardo

Copy link
Copy Markdown
Owner

Closes #14.

What

Phase 4, part one: the session now remembers which project the user is working in, and the sidebar gets a switcher to change it.

  • SessionSessionData gains currentProjectID, plus a getCurrentProjectID(request) helper.
  • Layout loader — fetches the user's projects via GET /projects, drops the stored id when the project is gone or the user lost access to it, auto-selects the first reachable project, and passes user, csrfToken, projects, and currentProject down. When it re-points the session it redirects to the same URL, so child loaders run against the corrected cookie instead of the stale one they were handed.
  • Sidebar — static header replaced by a project dropdown (active project, full list with a check on the current one, and a "New project" entry pointing at /projects/new, which lands in [Phase 4] Add /projects/new and /projects pages; implement first-time flow #15). With no projects it collapses to a single "New project" call to action.
  • /projects/switch — POST-only action. Validates CSRF, re-checks membership server-side (the id comes from the browser), writes the session, and redirects back to the originating page so every loader revalidates. A switch to a project the user can no longer reach leaves the session untouched and redirects back, where the layout re-points it.
  • API client — adds Project and getProjects().

Drive-by fix

/events returned a 500 whenever the SDK key was rejected — it is the one page still reading through the public SDK route rather than the internal API, so it had none of the noProject guards its siblings got in Phase 3. It now shows the same empty state as the other pages, and a rejected key renders an alert over an empty table instead of taking the page down. /events/:id and the delete action had the identical crash path and are guarded too.

Routing /events through the internal project-scoped API is #17 and needs a new broker route, so it is not attempted here.

Notes for review

Verification

npm run typecheck, oxlint, and oxfmt --check are clean on every touched file. Verified live against the local Docker stack: /events no longer 500s with no active project.

@vercel

vercel Bot commented Aug 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
logwolf-docs Ready Ready Preview Aug 19, 2026 12:13am

@jpricardo
jpricardo merged commit 2318f7d into feat/multi-tenancy Aug 19, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants