Skip to content

Fix sidebar workspace dropdown split, project fetching, and create-project dialog error handling#1

Open
Copilot wants to merge 1 commit intomainfrom
copilot/fix-dashboards-sidebar-button-dropdown
Open

Fix sidebar workspace dropdown split, project fetching, and create-project dialog error handling#1
Copilot wants to merge 1 commit intomainfrom
copilot/fix-dashboards-sidebar-button-dropdown

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 27, 2026

The sidebar workspace button had no actual dropdown — the entire element (including ChevronDown) was a single <Link> navigating to the workspace page. Project fetching on the dashboard was broken by a useEffect dependency bug and incorrect history.replaceState usage. The create-project dialog silently got stuck in a pending state on server errors.

Sidebar workspace: split button and dropdown

The workspace row is now split into two interactive zones within a shared bordered container:

  • Left (icon + name + slug) → <Link to="/dashboard/workspace">
  • Right (ChevronDown) → toggles a floating dropdown menu (mirrors the profile-menu pattern) with a "Workspace settings" entry

Project fetching: fix useEffect dependency and replaceState state loss

currentSearch was new URLSearchParams(window.location.search) computed inline — a new object reference on every render — causing useEffect to fire on every render. Replaced with rawSearch (plain string):

// Before — new object every render, effect runs on every render
const currentSearch = new URLSearchParams(window.location.search);

// After — stable string, effect only fires when search actually changes
const rawSearch = window.location.search;

replaceState was called with {} as the state argument, wiping TanStack Router's stored history state and breaking subsequent route reads. Fixed to preserve the existing state:

// Before — nukes router-internal history state
window.history.replaceState({}, "", newUrl);
window.dispatchEvent(new PopStateEvent("popstate"));

// After — preserves router state; event carries state too
window.history.replaceState(window.history.state, "", newUrl);
window.dispatchEvent(new PopStateEvent("popstate", { state: window.history.state }));

Path comparison now normalises trailing slashes (pathname.replace(/\/$/, "")) so /dashboard/ matches the same conditions as /dashboard.

Create-project dialog: add try/catch/finally

createProjectServerFn always re-throws on failure. Without a try/catch, setPending(false) was never reached, leaving the dialog stuck in "Creating…" with no visible error. Wrapped in try/catch/finallyfinally always resets pending state; catch surfaces the server error message.

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 27, 2026

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

Project Deployment Actions Updated (UTC)
mini-cms Ready Ready Preview, Comment Mar 28, 2026 10:54am
mini-cms-docs Ready Ready Preview, Comment Mar 28, 2026 10:54am

@lakubuDavid lakubuDavid marked this pull request as ready for review March 28, 2026 12:36
Copilot AI review requested due to automatic review settings March 28, 2026 12:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes several dashboard UX/state issues: adds a real workspace dropdown in the sidebar, stabilizes project-selection effects and preserves router history state when updating query params, and prevents the create-project dialog from getting stuck pending on server errors.

Changes:

  • Split the sidebar workspace row into a link area + chevron-triggered dropdown.
  • Fix dashboard project-selection useEffect dependencies and preserve window.history.state when calling replaceState/dispatching popstate.
  • Wrap create-project flow in try/catch/finally to always clear pending state and surface errors.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
apps/web/src/routes/dashboard/route.tsx Stabilizes URL/query handling and adds a workspace dropdown menu UI in the sidebar.
apps/web/src/routes/dashboard/index.tsx Improves create-project dialog reliability by handling server errors and always resetting pending state.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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">
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workspace dropdown backdrop only closes on click; there’s no keyboard handling (e.g., Escape) so keyboard users can get stuck with the menu open. Consider matching the pattern used in apps/web/src/routes/dashboard/api-keys.tsx:549-554 by adding an onKeyDown Escape handler (and/or a document keydown listener while open) so the menu can be dismissed without a pointer.

Suggested change
<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 uses AI. Check for mistakes.
Comment on lines +328 to +335
<button
type="button"
onClick={() => setWorkspaceOpen(!workspaceOpen)}
aria-label="Workspace menu"
className="flex items-center justify-center px-2.5 py-2 text-stone-400 transition hover:bg-stone-100 hover:text-stone-600 dark:hover:bg-stone-800 dark:hover:text-stone-300 border-l border-stone-200 dark:border-stone-800"
>
<ChevronDown className="h-4 w-4" />
</button>
Copy link

Copilot AI Mar 28, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +511 to 533
try {
const project = await createProjectServerFn({
data: { name, slug },
});

setPending(false);
if (!project) {
setError("Unable to create project. Please try again.");
return;
}

if (!project) {
setError("Unable to create project. Please try again.");
return;
setName("");
setSlug("");
setOpen(false);
props.onCreated();
} catch (cause) {
setError(
cause instanceof Error
? cause.message
: "Unable to create project. Please try again.",
);
} finally {
setPending(false);
}
Copy link

Copilot AI Mar 28, 2026

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.

Copilot uses AI. Check for mistakes.
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.

3 participants