-
Notifications
You must be signed in to change notification settings - Fork 0
Fix sidebar workspace dropdown split, project fetching, and create-project dialog error handling #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -78,6 +78,7 @@ function DashboardLayout() { | |||||||||||||||||||
| const { user, hasWorkspace } = useRouteContext({ from: "/dashboard" }); | ||||||||||||||||||||
| const navigate = useNavigate(); | ||||||||||||||||||||
| const [profileOpen, setProfileOpen] = useState(false); | ||||||||||||||||||||
| const [workspaceOpen, setWorkspaceOpen] = useState(false); | ||||||||||||||||||||
| const orgQuery = useQuery(organizationQueryOptions()); | ||||||||||||||||||||
| const projectsQuery = useQuery({ | ||||||||||||||||||||
| ...projectsQueryOptions(), | ||||||||||||||||||||
|
|
@@ -88,11 +89,11 @@ function DashboardLayout() { | |||||||||||||||||||
|
|
||||||||||||||||||||
| const currentPath = typeof window === "undefined" | ||||||||||||||||||||
| ? "" | ||||||||||||||||||||
| : window.location.pathname; | ||||||||||||||||||||
| const currentSearch = typeof window === "undefined" | ||||||||||||||||||||
| ? new URLSearchParams() | ||||||||||||||||||||
| : new URLSearchParams(window.location.search); | ||||||||||||||||||||
| const currentProjectId = currentSearch.get("projectId") ?? ""; | ||||||||||||||||||||
| : window.location.pathname.replace(/\/$/, ""); | ||||||||||||||||||||
| const rawSearch = typeof window === "undefined" | ||||||||||||||||||||
| ? "" | ||||||||||||||||||||
| : window.location.search; | ||||||||||||||||||||
| const currentProjectId = new URLSearchParams(rawSearch).get("projectId") ?? ""; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||
| if (!hasWorkspace) { | ||||||||||||||||||||
|
|
@@ -131,18 +132,18 @@ function DashboardLayout() { | |||||||||||||||||||
| return; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const params = new URLSearchParams(currentSearch); | ||||||||||||||||||||
| const params = new URLSearchParams(rawSearch); | ||||||||||||||||||||
| params.set("projectId", preferredProjectId); | ||||||||||||||||||||
| if (currentPath === "/dashboard/analytics" && !params.get("range")) { | ||||||||||||||||||||
| params.set("range", "30d"); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| window.history.replaceState({}, "", `${currentPath}?${params.toString()}`); | ||||||||||||||||||||
| window.dispatchEvent(new PopStateEvent("popstate")); | ||||||||||||||||||||
| window.history.replaceState(window.history.state, "", `${currentPath}?${params.toString()}`); | ||||||||||||||||||||
| window.dispatchEvent(new PopStateEvent("popstate", { state: window.history.state })); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| }, [ | ||||||||||||||||||||
| currentPath, | ||||||||||||||||||||
| currentProjectId, | ||||||||||||||||||||
| currentSearch, | ||||||||||||||||||||
| rawSearch, | ||||||||||||||||||||
| hasWorkspace, | ||||||||||||||||||||
| navigate, | ||||||||||||||||||||
| organization, | ||||||||||||||||||||
|
|
@@ -154,7 +155,7 @@ function DashboardLayout() { | |||||||||||||||||||
| writeStoredProjectId(organization.id, projectId); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const params = new URLSearchParams(currentSearch); | ||||||||||||||||||||
| const params = new URLSearchParams(rawSearch); | ||||||||||||||||||||
| if (projectId) { | ||||||||||||||||||||
| params.set("projectId", projectId); | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
|
|
@@ -166,8 +167,8 @@ function DashboardLayout() { | |||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const query = params.toString(); | ||||||||||||||||||||
| window.history.replaceState({}, "", query ? `${currentPath}?${query}` : currentPath); | ||||||||||||||||||||
| window.dispatchEvent(new PopStateEvent("popstate")); | ||||||||||||||||||||
| window.history.replaceState(window.history.state, "", query ? `${currentPath}?${query}` : currentPath); | ||||||||||||||||||||
| window.dispatchEvent(new PopStateEvent("popstate", { state: window.history.state })); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| return ( | ||||||||||||||||||||
|
|
@@ -281,21 +282,58 @@ function DashboardLayout() { | |||||||||||||||||||
| </nav> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| <div className="relative border-t border-stone-200 pt-3 dark:border-stone-800"> | ||||||||||||||||||||
| <Link | ||||||||||||||||||||
| to="/dashboard/workspace" | ||||||||||||||||||||
| className="mb-3 flex w-full items-center gap-2.5 rounded-lg border border-stone-200 px-2.5 py-2 text-left text-sm transition hover:bg-stone-100 dark:border-stone-800 dark:hover:bg-stone-800" | ||||||||||||||||||||
| > | ||||||||||||||||||||
| <Settings className="h-4 w-4 text-stone-500 dark:text-stone-400" /> | ||||||||||||||||||||
| <div className="min-w-0 flex-1"> | ||||||||||||||||||||
| <p className="truncate font-medium text-stone-900 dark:text-stone-100"> | ||||||||||||||||||||
| {organization?.name ?? "Create workspace"} | ||||||||||||||||||||
| </p> | ||||||||||||||||||||
| <p className="truncate text-xs text-stone-500 dark:text-stone-400"> | ||||||||||||||||||||
| {organization?.slug ?? "Workspace required before creating a project"} | ||||||||||||||||||||
| </p> | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
| <ChevronDown className="h-4 w-4 shrink-0 text-stone-400" /> | ||||||||||||||||||||
| </Link> | ||||||||||||||||||||
| {workspaceOpen ? ( | ||||||||||||||||||||
| <> | ||||||||||||||||||||
| <div | ||||||||||||||||||||
| className="fixed inset-0 z-40" | ||||||||||||||||||||
| onClick={() => setWorkspaceOpen(false)} | ||||||||||||||||||||
| /> | ||||||||||||||||||||
| <div className="absolute bottom-full left-0 z-50 mb-2 w-full rounded-xl border border-stone-200 bg-white p-3 shadow-lg dark:border-stone-700 dark:bg-stone-900"> | ||||||||||||||||||||
|
||||||||||||||||||||
| <div className="absolute bottom-full left-0 z-50 mb-2 w-full rounded-xl border border-stone-200 bg-white p-3 shadow-lg dark:border-stone-700 dark:bg-stone-900"> | |
| <div | |
| className="absolute bottom-full left-0 z-50 mb-2 w-full rounded-xl border border-stone-200 bg-white p-3 shadow-lg dark:border-stone-700 dark:bg-stone-900" | |
| onKeyDown={(event) => { | |
| if (event.key === "Escape") { | |
| setWorkspaceOpen(false); | |
| } | |
| }} | |
| > |
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New workspace dropdown behavior isn’t covered by existing Playwright e2e tests (no tests reference the “Workspace menu” button / “Workspace settings” entry). Adding a regression test that opens the dropdown via the chevron button and verifies the “Workspace settings” link navigates correctly would help prevent future sidebar regressions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The create-project error-handling path (server throws -> error message shown + pending resets) isn’t exercised by automated tests. Since the repo already has Playwright e2e coverage, adding a test that forces createProjectServerFn to fail (or intercepts the network request) and asserts the dialog exits the “Creating…” state and displays the error would prevent regressions.