diff --git a/frontend/src/lib/components/DBManager.svelte b/frontend/src/lib/components/DBManager.svelte index d3681ff0f3..e4634db726 100644 --- a/frontend/src/lib/components/DBManager.svelte +++ b/frontend/src/lib/components/DBManager.svelte @@ -8,6 +8,9 @@ Table2, Database as DatabaseIcon, Folder as FolderIcon, + History as HistoryIcon, + KeyRound as KeyRoundIcon, + Download as DownloadIcon, Trash2Icon, UploadIcon } from 'lucide-svelte' @@ -33,6 +36,7 @@ import type { DbFeatures } from './apps/components/display/dbtable/dbFeatures' import Star from './Star.svelte' import type { Asset, DataTableTables } from '$lib/gen' + import type { DatatableRowAction } from './dbTypes' import TextInput from './text_input/TextInput.svelte' /** Represents a selected table with its schema */ @@ -63,6 +67,9 @@ datatableTree?: DataTableTables[] datatableTreeLoading?: boolean onSelectDatatable?: (datatable: string) => void + /** Row-menu actions on a data table, run against that row's data table. */ + onDatatableAction?: (datatable: string, action: DatatableRowAction) => void + canManageDatatable?: boolean /** Enable multi-select mode with checkboxes in sidebar */ multiSelectMode?: boolean /** Selected tables in multi-select mode */ @@ -90,6 +97,8 @@ datatableTree, datatableTreeLoading, onSelectDatatable, + onDatatableAction, + canManageDatatable = false, multiSelectMode = false, selectedTables = $bindable([]), disabledTables = [], @@ -217,6 +226,10 @@ // overrides is what lets the current data table and selected schema — which // default to open — actually be folded; a plain "expanded" set could never // close them, since the default would keep winning. + // Schema-level permissions are not built yet; the drawer is the shell the row + // menu already opens onto. + let schemaPermissionsOpen = $state(false) + let expandOverrides = $state>(new Map()) const nodeKey = (dt: string | undefined, schemaKey?: string) => `${dt ?? ''}${schemaKey === undefined ? '' : `/${schemaKey}`}` @@ -240,6 +253,12 @@ expandOverrides = next } + // Row actions stay out of the way until you are on the row — or it is the one + // you are looking at, where the menu is part of the current context. + const rowActionsClass = (current: boolean) => + 'w-fit -mr-1 transition-opacity focus-within:opacity-100 group-hover:opacity-100 ' + + (current ? 'opacity-100' : 'opacity-0') + /** Every row in the tree reads the same way: what you are looking at now is * emphasized, everything else recedes. */ const rowText = (current: boolean) => @@ -557,12 +576,45 @@ {@const dtOpen = isExpanded(root.datatable)} {#if root.datatable !== undefined} - - {/if} {#if uriState.effectiveInput && ws} workerTag.tag, (v) => (workerTag.tag = v)} @@ -286,6 +301,22 @@ +{#if actionDatatable && ws} + + +{/if} + (exportDrawerOpen = false)}> {#if exportResult} diff --git a/frontend/src/lib/components/dbTypes.ts b/frontend/src/lib/components/dbTypes.ts index b3874b8f5d..4723877e31 100644 --- a/frontend/src/lib/components/dbTypes.ts +++ b/frontend/src/lib/components/dbTypes.ts @@ -26,3 +26,6 @@ export const dbTypes = [ 'duckdb' ] as const export const isDbType = (str?: string): str is DbType => !!str && dbTypes.includes(str as DbType) + +/** A row-menu action on a data table in the database manager's tree. */ +export type DatatableRowAction = 'migrations' | 'roles' | 'export' | 'import' diff --git a/frontend/src/lib/components/workspaceSettings/DataTableMigrationsButton.svelte b/frontend/src/lib/components/workspaceSettings/DataTableMigrationsButton.svelte index bb908a433c..3af5f8d0ab 100644 --- a/frontend/src/lib/components/workspaceSettings/DataTableMigrationsButton.svelte +++ b/frontend/src/lib/components/workspaceSettings/DataTableMigrationsButton.svelte @@ -106,6 +106,11 @@ // Open the list modal and the detail view for a specific migration. Used to // jump to a just-created migration from the "See migration" toast action. + /** Open the migrations list without the trigger button (tree menus drive it). */ + export function open() { + openList() + } + export async function openMigration(timestamp: number) { listOpen = true await loadMigrations() diff --git a/frontend/src/lib/components/workspaceSettings/DataTablePermissionsButton.svelte b/frontend/src/lib/components/workspaceSettings/DataTablePermissionsButton.svelte index fefe47656f..6bfa9f868a 100644 --- a/frontend/src/lib/components/workspaceSettings/DataTablePermissionsButton.svelte +++ b/frontend/src/lib/components/workspaceSettings/DataTablePermissionsButton.svelte @@ -31,11 +31,15 @@ let { workspace, datatable, - disabled = false + disabled = false, + hideTrigger = false }: { workspace: string datatable: string disabled?: boolean + /** Mount the drawer without its button, so a caller can drive it via `open()` + * (the database manager opens it from the tree's row menu). */ + hideTrigger?: boolean } = $props() const ADMIN_ROLE = 'admin' @@ -124,6 +128,12 @@ load() } + /** Open the drawer without the trigger button (the database manager's tree + * row menu drives it). Not named `open`: that is the drawer's own state. */ + export function openPermissions() { + openDrawer() + } + function addRole() { roles.push({ id: randomUUID(), name: '', tenants: [] }) } @@ -208,16 +218,18 @@ } -