dashboards: keep the selection while editing, and let the form win

Clicking edit dropped ?selected= from the url, so the address bar forgot which dashboard
you were editing — and on mobile that is worse than cosmetic: DashboardsScreen uses
`selected` to decide whether to show the right-hand panel, which is where the form
renders, so tapping edit opened it out of sight.

It cleared the param because it had to. The form was only reachable through
DashboardPreviewEmpty, which the preview renders when nothing is selected, so an edit
could only be shown by first pretending nothing was selected. DashboardPreview now checks
creating/editing itself, before it resolves the selection, and the empty state loses its
duplicate of that check — having two of them is what made the first attempt at this look
safe when it removed the only route to the form.

The form is shown only for the dashboard actually selected (`editingId === selectedId`),
and picking a row clears creating/editing. Without both, an abandoned edit followed you
onto the next dashboard: the url said you were on B while the form still edited A. The
click handler covers the ordinary path and the guard covers back/forward and pasted
links.

Verified in the browser: D5, D7, D8, D9 — edit keeps the param and opens the form, save
hands back to the preview of what you edited, switching dashboards mid-edit abandons it
cleanly, and returning to the first one shows its preview rather than the stale form.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 08:00:14 +00:00
co-authored by Claude Opus 5
parent 67b91a7976
commit 4970e7e70f
2 changed files with 24 additions and 5 deletions
@@ -60,9 +60,13 @@ export const DashboardListApp = () => {
})
: dashboards;
// Keep ?selected= while editing: you are still on this dashboard, just in edit mode. DashboardPreview
// renders the form over the preview whenever `editing` is set, so there is nothing to clear it for —
// and on mobile `selected` is what opens the right-hand panel the form lives in (DashboardsScreen),
// so clearing it opened the form where you could not see it. Cancelling or refreshing now returns to
// the dashboard you were editing instead of an empty preview.
const handleEdit = (ev: React.MouseEvent, ws: DashboardDefinition) => {
ev.stopPropagation();
clearSelected();
setCreating(false);
setEditing(ws.id);
setName(ws.name);
@@ -140,6 +144,12 @@ export const DashboardListApp = () => {
>
<Link
to={isDashboardsPage ? `/dashboards?selected=${ws.id}` : `/dashboards/${ws.id}`}
// Picking a dashboard leaves edit/create mode — otherwise an abandoned edit would follow you
// onto the next one. Not preventDefault'd, so the link still navigates normally.
onClick={() => {
setCreating(false);
setEditing(null);
}}
className="flex flex-1 items-center gap-2.5 py-2 px-3 min-w-0 cursor-pointer"
>
<LayoutGrid className="h-4 w-4 shrink-0" />
@@ -446,16 +446,15 @@ const NewDashboardForm = () => {
// --- Empty State ---
// Only reached when nothing is selected AND nothing is being created or edited — DashboardPreview checks
// those first now, so this component no longer needs its own creating/editing branch.
const DashboardPreviewEmpty = () => {
const [creating, setCreating] = useGlobal<boolean>(CREATING_DASHBOARD_KEY, false);
const [editingId] = useGlobal<string | null>(EDITING_DASHBOARD_KEY, null);
const [, setCreating] = useGlobal<boolean>(CREATING_DASHBOARD_KEY, false);
const [, setName] = useGlobal<string>(NEW_DASH_NAME_KEY, '');
const [, setDescription] = useGlobal<string>(NEW_DASH_DESC_KEY, '');
const [, setTemplateIdx] = useGlobal<number>(NEW_DASH_TEMPLATE_KEY, 0);
const [, setFilePath] = useUserState<string>('files/currentPath', '/');
if (creating || editingId) return <NewDashboardForm />;
const handleCreate = () => {
setName(generateSlug());
setDescription('');
@@ -492,6 +491,16 @@ export const DashboardPreview = () => {
const [searchParams] = useSearchParams();
const selectedId = searchParams.get('selected');
const { value: dashboards } = useDashboardState<DashboardDefinition[]>('workspaces', []);
const [creating] = useGlobal<boolean>(CREATING_DASHBOARD_KEY, false);
const [editingId] = useGlobal<string | null>(EDITING_DASHBOARD_KEY, null);
// The form wins over the preview, but only for the dashboard actually selected. It used to be reachable
// only through the empty state, so editing had to clear ?selected= to get to it — which meant the URL
// forgot which dashboard you were editing, and on mobile it closed the very panel the form renders in.
// The `editingId === selectedId` guard is what stops an abandoned edit from showing over a different
// dashboard reached by back/forward or a pasted link (a click is already handled by the list).
if (creating || (editingId && editingId === selectedId)) return <NewDashboardForm />;
const dashboard = selectedId ? dashboards.find((ws) => ws.id === selectedId) : null;
if (!dashboard) return <DashboardPreviewEmpty />;