feat(web): project switcher and create dialog - #24
Open
marcorivm wants to merge 1 commit into
Open
Conversation
Slice 3b of docs/project-lifecycle.md — the UI on top of 3a's transport. A dropdown in the sidebar lists the projects the caller can reach, marks the active one, switches between them, and opens a dialog to create a new one. The list arrives already filtered by the API (listProjects mirrors canAccessProjectAsUser), so there is nothing to gate client-side: an empty list renders nothing at all. Two details that are easy to get wrong: Switching CLEARS the whole query cache, not just the project list. Query keys are scoped by getProjectId(), which is an `undefined` stub in this edition, so changing project does not change any key — cached agents, secrets and policy would otherwise be served for the previous project. router.refresh() follows because the cookie only reaches the server on the next request and most of the dashboard is server-rendered. useCurrentProjectId mirrors the server's resolution chain rather than guessing: the cookie wins when set, the session's default answers when it is not. The session endpoint deliberately reports the DEFAULT project (it resolves through findUserDefaultProject and ignores the header), which is exactly why the cookie has to take precedence. The cookie is read in an effect rather than during render since it lives on document — reading it while rendering would mismatch the server-rendered HTML. Creating switches to the new project, which is safe because the API seeds the creator as owner in the same write. Still no tests: apps/web has no vitest harness on 1.44.0 and adding one would collide with the config upstream ships in v1.45.0. Verified with pnpm check (lint + types + clippy + format) and a full web build.
marcorivm
force-pushed
the
feat/projects-switcher-ui
branch
from
August 8, 2026 19:34
88e1333 to
b21c323
Compare
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.
Slice 3b of #20, stacked on #23. 5 files, ~230 lines. The UI on top of 3a's transport.
A dropdown in the sidebar lists the projects you can reach, marks the active one, switches between them, and opens a dialog to create one. This is the slice that makes the whole stack visible: create → list → switch now works end to end.
Two details that are easy to get wrong
Switching clears the entire query cache, not just the project list.
Query keys are scoped by
getProjectId()— anundefinedstub in this edition (api-fetch.ts:5). So changing project does not change any query key, and cached agents, secrets and policy would keep being served for the previous project. Invalidating justprojects.list()would look right and be silently wrong.router.refresh()follows because the cookie only reaches the server on the next request and most of the dashboard is server-rendered.useCurrentProjectIdmirrors the server's resolution chain rather than guessing it. Cookie wins when set; the session's default answers when it isn't. That ordering isn't arbitrary — the session endpoint resolves throughfindUserDefaultProjectand ignoresX-Project-Id, so it always reports the default project, never the selected one. If the cookie didn't take precedence the switcher would show the wrong project the moment you switched.The cookie is read in an effect rather than during render, since it lives on
document— reading it while rendering would mismatch the server-rendered HTML.undefinedon first paint is correct and momentary.Smaller choices
ownerin the same write (feat(api): POST /v1/projects — create a project, owned by its creator #22), so it's immediately usable and manageable.projectNameSchemadeliberately permits duplicate names and the API disambiguates slugs silently. Validating against it would contradict the API.Tests
None — same deliberate gap as #23.
apps/webhas no vitest harness on the 1.44.0 base; upstream shipsapps/web/vitest.config.tsin v1.45.0, and adding one here would collide with that adoption.Verified instead with:
Once the v1.45.0 harness lands,
useCurrentProjectId's precedence and the cookie helpers are the first things worth covering.