Skip to content

Unified /sql routing for workspace and dashboard surfaces with live view/edit modes #407

Description

@BorisTyshkevich

Goal

Serve the Workbench and its single workspace dashboard from the same /sql route, selected entirely by query parameters.

Canonical URLs:

/sql?ws=clickhouse_operations
/sql?ws=clickhouse_operations&surface=dashboard
/sql?ws=clickhouse_operations&surface=dashboard&mode=view

This issue depends on #406 for keyed multi-workspace persistence.

Product decisions

  • ws is the immutable human-readable workspace key.
  • Each workspace owns zero or one dashboard.
  • surface selects the application surface:
    • absent or workspace -> Workbench/editor;
    • dashboard -> Dashboard.
  • Dashboard edit mode is the default.
  • mode=view selects live read-only presentation mode.
  • mode=edit is accepted but canonicalized away.
  • Dashboard view mode reads the same live workspace dashboard as edit mode.
  • Durable detached dashboard snapshots are removed from the product model.
  • Workbench <-> Dashboard navigation happens in the same browser tab by default.
  • View <-> Edit changes presentation mode only and should not create noisy browser-history entries.
  • There is no migration requirement for old development URLs or detached-view data.

Route contract

Introduce one pure parser/normalizer for /sql state. Equivalent shape:

type SqlRoute =
  | {
      surface: 'workspace';
      workspaceKey: string | null;
    }
  | {
      surface: 'dashboard';
      workspaceKey: string | null;
      mode: 'edit' | 'view';
    };

Parsing rules:

  1. Read ws, surface, and mode from the current URL.
  2. Missing surface means workspace.
  3. surface=dashboard selects Dashboard.
  4. Unknown surface values normalize to workspace.
  5. mode=view is meaningful only on Dashboard.
  6. Missing, edit, or unknown Dashboard mode means edit.
  7. Remove irrelevant/invalid parameters when canonicalizing.
  8. Preserve unrelated application parameters that remain valid, especially OAuth callback parameters until the existing callback flow consumes them.

Canonicalization examples:

/sql?ws=x&surface=workspace          -> /sql?ws=x
/sql?ws=x&surface=dashboard&mode=edit -> /sql?ws=x&surface=dashboard
/sql?ws=x&mode=view                  -> /sql?ws=x
/sql?ws=x&surface=unknown            -> /sql?ws=x

Startup workspace resolution

After authentication/config setup:

Explicit workspace

/sql?ws=clickhouse_operations
  • Resolve exactly that key through the multi-workspace repository.
  • On success, set it as last-used and render the requested surface.
  • On failure, render a dedicated Workspace not found state.
  • Never silently substitute the last-used or another workspace.

No explicit workspace

/sql
  • Resolve the last-used workspace key.
  • If valid, canonicalize the URL to include it.
  • If no workspace exists, create a default workspace and canonicalize to /sql?ws=default or the actual collision-free generated key.
  • If the stored last-used key is stale, choose a deterministic existing workspace (alphabetically by display name/key is acceptable), update last-used, and canonicalize.

Surface rendering

Do not bootstrap two independent applications based on pathname.

The application should:

  1. resolve one workspace;
  2. parse the requested surface;
  3. construct/render only the required surface controller;
  4. switch surfaces without a full document reload where practical.

Workspace surface

  • Render normal editor/workbench UI.
  • Dashboard mode parameters are absent.

Dashboard edit surface

  • Load workspace.dashboard from the same workspace aggregate.
  • Register authoring capabilities: reorder, resize/layout changes, tile mutations, and persistence.
  • If dashboard === null, show an editable empty state with Create dashboard.
  • Merely visiting the route must not create a dashboard.

Dashboard view surface

  • Render the same current dashboard document and execute the same dashboard queries.
  • Do not expose or register mutation capabilities.
  • No drag/reorder/resize/delete handlers.
  • No workspace commit path reachable from view-mode controls or shortcuts.
  • If dashboard === null, show This workspace has no dashboard and execute nothing.
  • View mode is presentation-only, not authorization. The same local user may switch back to edit.

Navigation behavior

Workspace -> Dashboard

Use same-tab navigation and history.pushState():

/sql?ws=x
-> /sql?ws=x&surface=dashboard

This keeps browser Back useful and avoids default concurrent editing between Workbench and Dashboard tabs.

Dashboard -> Workspace

Use history.pushState() or browser Back when appropriate:

/sql?ws=x&surface=dashboard
-> /sql?ws=x

View <-> Edit

Use history.replaceState():

/sql?ws=x&surface=dashboard
<-> /sql?ws=x&surface=dashboard&mode=view

The Back button should not alternate repeatedly between view and edit presentation states.

Switching workspace

Preserve the current surface and mode when the user selects another workspace:

/sql?ws=a&surface=dashboard&mode=view
-> /sql?ws=b&surface=dashboard&mode=view

Dirty-draft confirmation is owned by the workspace-management UI issue.

Remove old Dashboard route assumptions

Retire production dependence on pathname suffix routing:

  • /sql/dashboard standalone route;
  • isDashboardRoute(pathname) as the application surface discriminator;
  • configBase() logic that strips /dashboard;
  • dashboard-specific OAuth redirect URI behavior;
  • code paths that bootstrap Dashboard separately solely because of pathname.

The OAuth callback must still preserve ws, surface, and mode while stripping only OAuth callback parameters.

Remove detached snapshot/view infrastructure

The product no longer needs durable detached read-only snapshots.

Remove or retire:

  • detached dashboard view store;
  • one-time dashboard state handoff token/store;
  • session-bundle dashboard open source;
  • st dashboard transport parameter;
  • detached workspace materialization and retention policy;
  • Open for viewing… snapshot action;
  • mode discrimination by which store contains a workspace ID.

Do not remove the existing credential handoff mechanism merely because state handoff is removed if credential handoff is still used elsewhere. Re-evaluate its necessity after same-tab navigation becomes the default.

Dashboard header controls

On Dashboard surface show:

[Workspace | Dashboard] [View | Edit]

Exact visual form may follow existing header primitives.

  • Surface switch updates surface.
  • Mode switch is visible only on Dashboard.
  • View mode is clearly indicated.
  • Edit controls are not rendered in view mode.

Missing/deleted workspace behavior

If another tab deletes the workspace currently addressed by the URL:

  • the next load/refresh/mutation failure resolves to Workspace not found;
  • do not silently open another workspace;
  • execute no dashboard queries after the workspace is known missing.

Real-time cross-tab invalidation is a separate follow-up; this issue only requires correct behavior when the missing state is observed.

Tests

Add pure route tests and integration tests covering at least:

  1. Parse and canonicalize all three canonical URL forms.
  2. /sql resolves last-used workspace and rewrites URL.
  3. /sql creates a default workspace when none exist.
  4. Explicit missing ws shows not-found without fallback.
  5. Workspace surface renders for absent surface.
  6. Dashboard edit is default for surface=dashboard.
  7. mode=view renders read-only viewer with no mutation handlers.
  8. mode=edit canonicalizes away.
  9. Missing dashboard differs correctly in edit and view modes.
  10. Workspace/Dashboard same-tab navigation uses expected history behavior.
  11. View/Edit uses replaceState.
  12. OAuth callback cleanup retains ws, surface, and mode.
  13. Old detached stores/tokens are no longer required by the production route.
  14. Dashboard queries are not executed for missing workspace or missing dashboard in view mode.

Acceptance criteria

  • Workbench and Dashboard are both served from /sql.
  • The canonical Dashboard edit URL is /sql?ws=<key>&surface=dashboard.
  • The canonical live read-only URL is /sql?ws=<key>&surface=dashboard&mode=view.
  • Dashboard view and edit use the same canonical workspace/dashboard data.
  • Detached dashboard snapshots and one-time state handoff are removed.
  • Default navigation between Workbench and Dashboard stays in the same tab.
  • Explicit unknown workspace keys never fall back silently.

Non-goals

  • Security/authorization enforcement for view mode.
  • Multiple dashboards per workspace.
  • Frozen historical dashboard snapshots.
  • Cross-device bookmark portability when data is only local.
  • Cross-tab stale-write prevention; tracked separately.
  • Migration of old development URLs or IndexedDB records.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    inboxFiled mid-task; not yet triaged into the roadmap

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions