From 7570349f2e5aaf93a6473125b9ccb2b0d5e9ff3d Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Wed, 26 Aug 2026 00:02:57 +0200 Subject: [PATCH] feat(datatables): switch a data table's role from its row badge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The badge is now a button with a chevron, opening the select dropdown over the roles the caller may use — the dropdown alone, not Select's own input. Picking one reconnects the manager as that role, switching data table first when the badge belongs to another one. --- frontend/src/lib/components/DBManager.svelte | 31 ++++++---- .../lib/components/DBManagerContent.svelte | 3 + .../src/lib/components/DBManagerDrawer.svelte | 5 ++ .../lib/components/DatatableRoleBadge.svelte | 57 +++++++++++++++++++ .../raw_apps/RawAppDataTableDrawer.svelte | 8 ++- 5 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 frontend/src/lib/components/DatatableRoleBadge.svelte diff --git a/frontend/src/lib/components/DBManager.svelte b/frontend/src/lib/components/DBManager.svelte index 1f35622dfb..54c9f412bb 100644 --- a/frontend/src/lib/components/DBManager.svelte +++ b/frontend/src/lib/components/DBManager.svelte @@ -34,7 +34,7 @@ import { capitalize, onlyAlphaNumAndUnderscore, pluralize } from '$lib/utils' import type { DbFeatures } from './apps/components/display/dbtable/dbFeatures' import Star from './Star.svelte' - import Badge from './common/badge/Badge.svelte' + import DatatableRoleBadge from './DatatableRoleBadge.svelte' import type { Asset, DataTableTables } from '$lib/gen' import { ADMIN_DATATABLE_ROLE, type DatatableRowAction } from './dbTypes' import TextInput from './text_input/TextInput.svelte' @@ -79,6 +79,8 @@ onSelectDatatable?: (datatable: string) => void /** Role the manager is connected as, when it is not the default one. */ currentRole?: string + /** Switch a data table's role from its row badge. */ + onSelectRole?: (datatable: string, role: string) => void pendingAction?: PendingRowAction | undefined /** Row-menu actions on a data table, run against that row's data table. */ onDatatableAction?: (datatable: string, action: DatatableRowAction) => void @@ -110,6 +112,7 @@ datatableTreeLoading, onSelectDatatable, currentRole, + onSelectRole, pendingAction = $bindable(undefined), onDatatableAction, canManageDatatable = false, @@ -211,16 +214,18 @@ return datatableTree?.find((d) => d.datatable_name === datatable)?.error } - /** The role a data table row is reached through, shown as a badge. Left out - * where naming it says nothing: a data table without permissions, or one whose - * single usable role is already the implicit `admin`. */ - function roleOf(datatable: string): string | undefined { + /** The role a data table row is reached through, and what it can be switched + * to. Absent where naming the role says nothing: a data table without + * permissions, or one whose single usable role is already the implicit + * `admin`. */ + function roleOf(datatable: string): { role: string; roles: string[] } | undefined { const entry = datatableTree?.find((d) => d.datatable_name === datatable) - const usable = entry?.usable_roles ?? [] - if (usable.length === 0 || (usable.length === 1 && usable[0] === ADMIN_DATATABLE_ROLE)) { + const roles = entry?.usable_roles ?? [] + if (roles.length === 0 || (roles.length === 1 && roles[0] === ADMIN_DATATABLE_ROLE)) { return undefined } - return (datatable === currentDatatable ? currentRole : undefined) ?? entry?.default_role + const role = (datatable === currentDatatable ? currentRole : undefined) ?? entry?.default_role + return role ? { role, roles } : undefined } const matchesSearch = (t: string) => t.toLowerCase().includes(search.trim().toLowerCase()) @@ -504,6 +509,7 @@ {@const dtOpen = isExpanded(root.datatable)} {#if root.datatable !== undefined} {@const hasMenu = !multiSelectMode && onDatatableAction !== undefined} + {@const roleInfo = roleOf(root.datatable)} + btnEl!.getBoundingClientRect())} + onSelectValue={(item) => { + open = false + if (item.value !== role) onSelect(item.value) + }} + /> + diff --git a/frontend/src/lib/components/raw_apps/RawAppDataTableDrawer.svelte b/frontend/src/lib/components/raw_apps/RawAppDataTableDrawer.svelte index 1c52530b5f..1261b9e814 100644 --- a/frontend/src/lib/components/raw_apps/RawAppDataTableDrawer.svelte +++ b/frontend/src/lib/components/raw_apps/RawAppDataTableDrawer.svelte @@ -30,6 +30,8 @@ let open = $state(false) let selectedDatatable = $state(undefined) + /** Role the query editor connects as; undefined means the data table's default. */ + let selectedRole = $state(undefined) // For DB manager let dbManagerContent: DBManagerContent | undefined = $state() @@ -125,6 +127,7 @@ type: 'database' as const, resourceType: 'postgresql' as const, resourcePath: `datatable://${selectedDatatable}`, + role: selectedRole, specificSchema: selectedSchemaKey, specificTable: selectedTableKey } @@ -169,7 +172,7 @@ noPadding > {#if dbInput && opWs} - {#key selectedDatatable} + {#key `${selectedDatatable}~${selectedRole ?? ''}`} (selectedDatatable = dt)} + onSelectDatatable={(dt) => ((selectedDatatable = dt), (selectedRole = undefined))} + onSelectRole={(dt, role) => ((selectedDatatable = dt), (selectedRole = role))} bind:pendingAction /> {/key}