Skip to content

BigQuery Query Console Redesign โ€‹

Status: Draft Date: 2026-05-03 Scope: Admin portal โ€” /admin/analytics/bigquery/console

Goal โ€‹

Replace the current Query Console layout with a tabbed-rail / work-surface structure that makes the editor and results the primary work area, unifies reference content (schema, saved queries, history) into a single collapsible rail, and treats result tables as a first-class viewing experience instead of an afterthought.

Why โ€‹

The current console has four issues that compound:

  1. Layout fights itself. Schema browser and editor share a row, but results render under the editor in the same right-hand column โ€” so result tables get a narrow strip while the schema browser holds full vertical space.
  2. Wasted vertical space. Schema, editor, saved queries, and built-in reports all stack as full-width sections; running a saved query and seeing results requires significant scrolling.
  3. Flat hierarchy. Limit pills, section headers, cards, and results all carry equal visual weight.
  4. Results presentation is weak. Columns aren't sized to content, long UUIDs wrap into multiple lines of mush, no way to expand a row, no copy-CSV, no fullscreen.

Built-in reports also live on this page today but are moving to their own page (separate work).

Out of Scope โ€‹

  • Built-in reports section โ€” being removed from the console; lives on its own page (separate effort).
  • Reference Docs ยท Export Status ยท Query Console ยท Scheduled Reports ยท Data Retention sub-nav (BigQueryTabs) โ€” unchanged.
  • Backend / API changes for query execution. Existing runBqQueryRaw, estimateBqQueryRaw, saveQuery, deleteSavedQuery, schema-fetch endpoints stay as-is.
  • Authentication / authorization. No changes to admin role gating.

Layout โ€‹

A three-zone shell:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Toolbar:  โ–ถ Run   Estimate   Save   Format   Clear      [limits] โ›ถ โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Tabbed rail  โ”‚  Editor                                              โ”‚
โ”‚              โ”‚                                                      โ”‚
โ”‚ Schema       โ”‚  โ”€โ”€ drag handle โ”€โ”€ (resizes editor / results split)  โ”‚
โ”‚ Saved (n)    โ”‚                                                      โ”‚
โ”‚ History      โ”‚  Cost & job meta strip                               โ”‚
โ”‚              โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚ [searchโ€ฆ]    โ”‚  โ”‚ Results toolbar (filter ยท CSV ยท download ยท โ›ถ)  โ”‚  โ”‚
โ”‚              โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚ listโ€ฆ        โ”‚  โ”‚ Results table (resizable cols, click to expand)โ”‚  โ”‚
โ”‚              โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Toolbar โ€‹

Single horizontal bar at the top of the work surface.

  • Run (primary, accent-colored). Keyboard: Cmd/Ctrl+Enter.
  • Estimate โ€” runs dry-run against current SQL.
  • Save โ€” opens the existing save-query inline form.
  • Format (ghost) โ€” formats the SQL in the editor via the sql-formatter package (BigQuery dialect).
  • Clear (ghost) โ€” empties the editor (with undo via the editor's own history).
  • Limits pill โ€” collapses the three pills (SELECT only, 1 GB, 5k rows) into one compact pill (SELECT only ยท 1 GB ยท 5k rows); full text on hover via title attribute.
  • Fullscreen toggle (right side) โ€” CSS-only: hides the admin left sidebar and BigQueryTabs page tabs so the console fills the browser viewport. Browser chrome / URL bar stay. Esc exits. State is local to the component โ€” does not persist across navigation.

Tabbed Rail (left) โ€‹

Single rail with three tabs. Width is resizable via a drag handle on the right edge; width persists per admin in localStorage.

Schema tab โ€” current SchemaBrowser content. One filter input that searches across datasets / tables / columns. Click a column or table to insert at cursor in the editor.

Saved tab โ€” current SavedQueriesPanel content, with the count badge in the tab label. Each entry shows name, SQL preview, author, timestamp, and Load/Delete actions. "Load into editor" replaces editor contents.

History tab โ€” new. Last 25 successful runs by the current admin, stored in localStorage (key: bq-console:history:<adminId>). Each entry: SQL snippet, timestamp, scan size, runtime. Click to load into editor. Cleared via a "Clear history" link in the tab header.

The rail is collapsible to a thin icon strip via a chevron in the rail header; collapsed state persists.

Work Surface โ€‹

Editor. Existing RawSqlEditor component, retained as-is for SQL input. Drag handle along its bottom edge resizes the editor / results split; ratio persists.

Cost & job meta strip. Persistent strip between editor and results showing the most recent estimate (โœ“ Last estimate: 711 KB ยท $0.000003) and the most recent job result (Job: 1.2s ยท 10 rows ยท cache miss). Errors render here inline (red text + icon) instead of in a separate ErrorCard below the results.

Results.

  • Results toolbar: row-filter input (client-side substring filter across all columns), Copy CSV, Download CSV, Fullscreen (toggles same fullscreen as the toolbar button).
  • Results table: existing QueryResultTable extended with column resize (drag column-edge; widths persist per query name when applicable), and row-click to expand. The expand panel renders the row as a key/value list with full untruncated values (handles long UUIDs cleanly without wide columns).
  • Empty state: "Run a query to see results" with a hint about Cmd/Ctrl+Enter.
  • Loading state: skeleton rows + a subtle pulsing accent on the Run button.

Component Breakdown โ€‹

The current BigQueryWorkspace.jsx is ~1530 lines and combines export status, retention, scheduled reports, query console, schema browser, saved queries, raw editor, and result rendering. Touching it for this redesign without splitting will compound the problem.

Refactor as part of this work โ€” only the Console-related pieces:

apps/admin/src/admin/analytics/
โ”œโ”€โ”€ BigQueryWorkspace.jsx          # router shell, kept (other panels stay)
โ”œโ”€โ”€ console/
โ”‚   โ”œโ”€โ”€ ConsolePanel.jsx           # orchestrator; replaces in-file ConsolePanel
โ”‚   โ”œโ”€โ”€ ConsoleToolbar.jsx         # Run / Estimate / Save / Format / Clear / limits / fullscreen
โ”‚   โ”œโ”€โ”€ ConsoleRail.jsx            # tabbed rail (Schema ยท Saved ยท History)
โ”‚   โ”œโ”€โ”€ ConsoleEditor.jsx          # wraps RawSqlEditor + the resize handle
โ”‚   โ”œโ”€โ”€ ConsoleMetaStrip.jsx       # estimate / job meta / inline errors
โ”‚   โ”œโ”€โ”€ ConsoleResults.jsx         # results toolbar + table + row-expand
โ”‚   โ”œโ”€โ”€ HistoryPanel.jsx           # rail's history tab
โ”‚   โ”œโ”€โ”€ useConsoleState.js         # editor SQL, last estimate, last job, error
โ”‚   โ”œโ”€โ”€ useConsoleHistory.js       # localStorage history hook
โ”‚   โ””โ”€โ”€ useConsolePersistence.js   # rail width/collapsed, editor split ratio
โ””โ”€โ”€ (existing files for ExportStatus / Schedule / Retention untouched)

SchemaBrowser, SavedQueriesPanel, RawSqlEditor, QueryResultTable, ErrorCard, ResultMetaFooter, SectionHeader, formatBytes, and structuredError are all only used by the Console flow today (verified: ExportStatusPanel, ScheduledReportsPanel, and DataRetentionPanel don't reference them). They move into apps/admin/src/admin/analytics/console/ and are deleted from BigQueryWorkspace.jsx. The orchestrator (ConsolePanel) is the only new top-level component the router renders for the console route.

useConsoleState is the single source of truth the toolbar, editor, meta strip, and results all read/write through. No prop-drilling chains.

Persistence โ€‹

Per-admin localStorage keys (namespaced by admin id):

  • bq-console:rail:width โ€” pixel width of the rail
  • bq-console:rail:collapsed โ€” boolean
  • bq-console:rail:active-tab โ€” 'schema' | 'saved' | 'history'
  • bq-console:editor:height โ€” pixel height of the editor pane
  • bq-console:history โ€” array of { sql, ts, scanBytes, durationMs }, capped at 25
  • bq-console:column-widths:<queryFingerprint> โ€” optional, per-query column widths

A query "fingerprint" is a hash of the SQL text after format-normalization โ€” column widths only persist for queries that re-run with stable shape.

Keyboard โ€‹

  • Cmd/Ctrl+Enter โ€” Run
  • Cmd/Ctrl+S โ€” Save (opens existing save form)
  • Cmd/Ctrl+E โ€” Estimate
  • Cmd/Ctrl+K โ€” focus rail search input
  • Esc โ€” collapse rail (when not in an input)
  • Esc (in fullscreen) โ€” exit fullscreen

Error Handling โ€‹

  • Query errors (BigQuery rejection, timeout, scan-cap exceeded): render in the cost/job meta strip as inline error text with the existing structuredError shape.
  • Schema fetch failure: existing behavior preserved โ€” error card inside the Schema tab.
  • Saved-query fetch failure: error card inside the Saved tab; rest of the console remains functional.
  • History corruption (bad JSON in localStorage): silently reset and continue.
  • Network offline: Run/Estimate buttons disable; toolbar shows a small "offline" pill. Rail tabs (Schema, Saved, History) remain functional for any data already cached, but show a stale-data hint where applicable.

Testing โ€‹

Existing tests in apps/admin/src/admin/analytics/__tests__/ for the BigQuery flow continue to pass. New tests:

  • ConsolePanel.test.jsx โ€” rail tab switching; toolbar wiring; meta strip updates after estimate/run; row expand toggles.
  • useConsoleHistory.test.js โ€” capacity cap; corruption recovery; ordering.
  • useConsolePersistence.test.js โ€” width/height persistence; reset to defaults when keys missing.
  • Storybook story for ConsolePanel covering: empty / loading / results / error / fullscreen states.

Visual regression isn't currently set up for this area; not adding it as part of this work.

Migration / Rollout โ€‹

Single PR. No feature flag โ€” admin-only surface, low blast radius, instant rollback by revert. Manual smoke check:

  1. Schema browser inserts at cursor as before.
  2. Saved queries load into editor as before.
  3. Estimate and Run produce the same response shapes.
  4. Errors render readably.
  5. History entries appear after a run and survive a page reload.
  6. Resize handles persist across reloads.

Decisions Made โ€‹

These started as open questions and are now resolved:

  • History storage โ€” localStorage per admin. Saved queries already use Firestore (adminSavedQueries), but history is per-device ephemera (recent runs by the current admin) and doesn't justify Firestore writes on every successful run. Revisit if admins request cross-device history.
  • SQL formatting โ€” Ships with sql-formatter (BigQuery dialect). Adds the Format button as a real feature in v1, not a follow-up. Bundle cost (~25 KB gz) is acceptable for an admin-only surface.
  • Fullscreen โ€” CSS-only toggle. Hides the admin left sidebar and BigQueryTabs page tabs; console fills the browser viewport. Matches the BigQuery web UI pattern. Esc exits.

New Dependency โ€‹

  • sql-formatter (npm) โ€” added to apps/admin for the Format button. BigQuery dialect.

Built with VitePress