mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-06 00:02:13 +00:00
feat(datatables): create tables and schemas from any data table in the tree
The New table / New schema rows now show under every data table, not only the one the manager is pointed at. Creating outside it switches to that data table first, and the request rides through the parent because the switch re-mounts the manager. Tree rows also read uniformly now — the background alone marks the current one — with the create rows set apart in secondary.
This commit is contained in:
@@ -47,6 +47,10 @@
|
||||
table: string
|
||||
}
|
||||
|
||||
/** A create started from a row of another data table. Switching data table
|
||||
* re-mounts this component, so the request has to travel through the parent. */
|
||||
export type PendingCreate = { kind: 'table'; schema: string } | { kind: 'schema' }
|
||||
|
||||
type Props = {
|
||||
dbType: DbType
|
||||
dbSchema: DBSchema
|
||||
@@ -66,6 +70,7 @@
|
||||
datatableTree?: DataTableTables[]
|
||||
datatableTreeLoading?: boolean
|
||||
onSelectDatatable?: (datatable: string) => void
|
||||
pendingCreate?: PendingCreate | undefined
|
||||
/** Row-menu actions on a data table, run against that row's data table. */
|
||||
onDatatableAction?: (datatable: string, action: DatatableRowAction) => void
|
||||
canManageDatatable?: boolean
|
||||
@@ -95,6 +100,7 @@
|
||||
datatableTree,
|
||||
datatableTreeLoading,
|
||||
onSelectDatatable,
|
||||
pendingCreate = $bindable(undefined),
|
||||
onDatatableAction,
|
||||
canManageDatatable = false,
|
||||
multiSelectMode = false,
|
||||
@@ -266,11 +272,6 @@
|
||||
: 'group-hover:opacity-0 ' + (current ? 'opacity-0 ' : 'opacity-100 ')) +
|
||||
(open ? '' : '-rotate-90')
|
||||
|
||||
/** Every row in the tree reads the same way: what you are looking at now is
|
||||
* emphasized, everything else recedes. */
|
||||
const rowText = (current: boolean) =>
|
||||
current ? 'text-primary font-semibold' : 'text-secondary font-normal'
|
||||
|
||||
/** Reveal a node, dropping a stale "closed" that would hide a new selection. */
|
||||
function reveal(dt: string | undefined, schemaKey?: string) {
|
||||
const next = new Map(expandOverrides)
|
||||
@@ -293,6 +294,40 @@
|
||||
selected = { schemaKey, tableKey }
|
||||
}
|
||||
|
||||
function startCreateTable(dt: string | undefined, schemaKey: string) {
|
||||
if (dt !== undefined && dt !== currentDatatable) {
|
||||
selectedSchemaKey = schemaKey
|
||||
pendingCreate = { kind: 'table', schema: schemaKey }
|
||||
onSelectDatatable?.(dt)
|
||||
return
|
||||
}
|
||||
selected = { schemaKey, tableKey: undefined }
|
||||
dbTableEditorState = { open: true }
|
||||
}
|
||||
|
||||
function startCreateSchema(dt: string | undefined) {
|
||||
if (dt !== undefined && dt !== currentDatatable) {
|
||||
pendingCreate = { kind: 'schema' }
|
||||
onSelectDatatable?.(dt)
|
||||
return
|
||||
}
|
||||
newSchemaDialogOpen = true
|
||||
}
|
||||
|
||||
// Finishes a create requested before the switch, now that this component is
|
||||
// mounted against the data table it targeted.
|
||||
$effect(() => {
|
||||
const req = pendingCreate
|
||||
if (!req || !schemaKeys.length) return
|
||||
pendingCreate = undefined
|
||||
if (req.kind === 'schema') {
|
||||
newSchemaDialogOpen = true
|
||||
} else if (schemaKeys.includes(req.schema)) {
|
||||
selected = { schemaKey: req.schema, tableKey: undefined }
|
||||
dbTableEditorState = { open: true }
|
||||
}
|
||||
})
|
||||
|
||||
let search = $state('')
|
||||
let selected: {
|
||||
schemaKey?: undefined | string
|
||||
@@ -415,8 +450,7 @@
|
||||
{@const dtOpen = isExpanded(root.datatable)}
|
||||
{#if root.datatable !== undefined}
|
||||
<button
|
||||
class={'group w-full text-sm flex gap-2 items-center h-8 cursor-pointer pl-3 pr-1 hover:bg-gray-500/10 ' +
|
||||
rowText(root.datatable === currentDatatable)}
|
||||
class="group w-full text-xs font-normal text-primary flex gap-2 items-center h-8 cursor-pointer pl-3 pr-1 hover:bg-gray-500/10"
|
||||
onclick={() => toggle(root.datatable)}
|
||||
>
|
||||
{#if multiSelectMode}
|
||||
@@ -484,12 +518,8 @@
|
||||
{@const indent = root.datatable !== undefined ? 'pl-7' : 'pl-3'}
|
||||
{#if dbSupportsSchemas}
|
||||
<button
|
||||
class={'group w-full text-sm flex gap-2 items-center h-8 cursor-pointer pr-1 hover:bg-gray-500/10 ' +
|
||||
indent +
|
||||
' ' +
|
||||
rowText(
|
||||
root.datatable === currentDatatable && sc.schemaKey === selected.schemaKey
|
||||
)}
|
||||
class={'group w-full text-xs font-normal text-primary flex gap-2 items-center h-8 cursor-pointer pr-1 hover:bg-gray-500/10 ' +
|
||||
indent}
|
||||
onclick={() => toggle(root.datatable, sc.schemaKey)}
|
||||
>
|
||||
{#if multiSelectMode}
|
||||
@@ -550,11 +580,9 @@
|
||||
selected.schemaKey === sc.schemaKey &&
|
||||
selected.tableKey === tableKey}
|
||||
<button
|
||||
class={'group w-full text-sm flex gap-2 items-center h-8 cursor-pointer pr-1 ' +
|
||||
class={'group w-full text-xs font-normal text-primary flex gap-2 items-center h-8 cursor-pointer pr-1 ' +
|
||||
tableIndent +
|
||||
' ' +
|
||||
rowText(isSelected) +
|
||||
' ' +
|
||||
(isSelected ? 'bg-surface-secondary' : 'hover:bg-surface-hover')}
|
||||
onclick={() => selectTable(root.datatable, sc.schemaKey, tableKey)}
|
||||
>
|
||||
@@ -630,26 +658,21 @@
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
{#if root.datatable === currentDatatable || root.datatable === undefined}
|
||||
<button
|
||||
class={'w-full text-sm font-normal flex gap-2 items-center h-8 cursor-pointer pr-1 hover:bg-gray-500/10 text-tertiary ' +
|
||||
tableIndent}
|
||||
onclick={() => {
|
||||
selected = { schemaKey: sc.schemaKey, tableKey: undefined }
|
||||
dbTableEditorState = { open: true }
|
||||
}}
|
||||
>
|
||||
<Plus class="shrink-0" size={14} />
|
||||
<span class="text-xs">New table</span>
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
class={'w-full text-xs font-normal flex gap-2 items-center h-8 cursor-pointer pr-1 hover:bg-gray-500/10 text-secondary ' +
|
||||
tableIndent}
|
||||
onclick={() => startCreateTable(root.datatable, sc.schemaKey)}
|
||||
>
|
||||
<Plus class="shrink-0" size={14} />
|
||||
<span class="text-xs">New table</span>
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if dbSupportsSchemas && (root.datatable === currentDatatable || root.datatable === undefined) && search.trim() === ''}
|
||||
{#if dbSupportsSchemas && search.trim() === ''}
|
||||
<button
|
||||
class={'w-full text-sm font-normal flex gap-2 items-center h-8 cursor-pointer pr-1 hover:bg-gray-500/10 text-tertiary ' +
|
||||
class={'w-full text-xs font-normal flex gap-2 items-center h-8 cursor-pointer pr-1 hover:bg-gray-500/10 text-secondary ' +
|
||||
(root.datatable !== undefined ? 'pl-7' : 'pl-3')}
|
||||
onclick={() => (newSchemaDialogOpen = true)}
|
||||
onclick={() => startCreateSchema(root.datatable)}
|
||||
>
|
||||
<Plus class="shrink-0" size={14} />
|
||||
<span class="text-xs">New schema</span>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
import type { DbInput } from './dbTypes'
|
||||
import { getDbSchemas, loadAllTablesMetaData } from './apps/components/display/dbtable/metadata'
|
||||
|
||||
import type { SelectedTable } from './DBManager.svelte'
|
||||
import type { PendingCreate, SelectedTable } from './DBManager.svelte'
|
||||
import { getDbFeatures } from './apps/components/display/dbtable/dbFeatures'
|
||||
import { resource } from 'runed'
|
||||
import ConfirmationModal from './common/confirmationModal/ConfirmationModal.svelte'
|
||||
@@ -43,6 +43,7 @@
|
||||
datatableTree?: DataTableTables[]
|
||||
datatableTreeLoading?: boolean
|
||||
onSelectDatatable?: (datatable: string) => void
|
||||
pendingCreate?: PendingCreate | undefined
|
||||
onDatatableAction?: (datatable: string, action: DatatableRowAction) => void
|
||||
canManageDatatable?: boolean
|
||||
/** Enable multi-select mode with checkboxes in sidebar */
|
||||
@@ -70,6 +71,7 @@
|
||||
datatableTree,
|
||||
datatableTreeLoading,
|
||||
onSelectDatatable,
|
||||
pendingCreate = $bindable(undefined),
|
||||
onDatatableAction,
|
||||
canManageDatatable,
|
||||
multiSelectMode = false,
|
||||
@@ -323,6 +325,7 @@
|
||||
{datatableTree}
|
||||
{datatableTreeLoading}
|
||||
{onSelectDatatable}
|
||||
bind:pendingCreate
|
||||
{onDatatableAction}
|
||||
{canManageDatatable}
|
||||
{onImport}
|
||||
|
||||
@@ -5,14 +5,9 @@
|
||||
import Drawer from './common/drawer/Drawer.svelte'
|
||||
import DrawerContent from './common/drawer/DrawerContent.svelte'
|
||||
import Select from './select/Select.svelte'
|
||||
import {
|
||||
ArrowLeft,
|
||||
Copy,
|
||||
Expand,
|
||||
Minimize,
|
||||
RefreshCcw,
|
||||
} from 'lucide-svelte'
|
||||
import { ArrowLeft, Copy, Expand, Minimize, RefreshCcw } from 'lucide-svelte'
|
||||
import DBManagerContent from './DBManagerContent.svelte'
|
||||
import type { PendingCreate } from './DBManager.svelte'
|
||||
import DataTableMigrationsButton from './workspaceSettings/DataTableMigrationsButton.svelte'
|
||||
import DataTablePermissionsButton from './workspaceSettings/DataTablePermissionsButton.svelte'
|
||||
import { resource } from 'runed'
|
||||
@@ -40,6 +35,10 @@
|
||||
// the editor that opened it (set via openDrawer), else the nav workspace.
|
||||
let ws = $derived(uriState.workspace ?? $workspaceStore)
|
||||
|
||||
// A create started on a data table other than the current one: survives the
|
||||
// re-mount the switch causes.
|
||||
let pendingCreate = $state<PendingCreate | undefined>(undefined)
|
||||
|
||||
// Every data table with its schemas and tables, in one call: this is what the
|
||||
// left pane's tree navigates, so it has to cover the data tables the user is
|
||||
// not currently on, not just the selected one.
|
||||
@@ -84,7 +83,6 @@
|
||||
uriState.selectedRole !== undefined))
|
||||
)
|
||||
|
||||
|
||||
// Settle the role before anything queries the data table: the schema and
|
||||
// metadata fetches run as whatever role the input carries, so leaving it unset
|
||||
// until the user touches the picker would send the first — and cached — round
|
||||
@@ -259,6 +257,7 @@
|
||||
datatableTree={uriState.isDatatableInput ? datatables.current : undefined}
|
||||
datatableTreeLoading={datatables.loading}
|
||||
onSelectDatatable={(dt) => (uriState.selectedDatatable = dt)}
|
||||
bind:pendingCreate
|
||||
canManageDatatable={!!($superadmin || $userStore?.is_admin)}
|
||||
onDatatableAction={runDatatableAction}
|
||||
bind:workerTag={() => workerTag.tag, (v) => (workerTag.tag = v)}
|
||||
@@ -268,8 +267,7 @@
|
||||
onImport={enableImportExport
|
||||
? (mode) => ((importDrawerOpen = true), (importBehavior = mode))
|
||||
: undefined}
|
||||
>
|
||||
</DBManagerContent>
|
||||
></DBManagerContent>
|
||||
{/key}
|
||||
{/if}
|
||||
{#snippet actions()}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import { ArrowLeft, Expand, Minimize, Plus, RefreshCcw } from 'lucide-svelte'
|
||||
import DBManagerContent from '../DBManagerContent.svelte'
|
||||
import type { DbInput } from '../dbTypes'
|
||||
import type { SelectedTable } from '../DBManager.svelte'
|
||||
import type { PendingCreate, SelectedTable } from '../DBManager.svelte'
|
||||
import { getRawAppOperatingWorkspace } from './rawAppWorkspace'
|
||||
import { useDbManagerTag } from '../dbManagerTag.svelte'
|
||||
import DbWorkerTagButton from '../DbWorkerTagButton.svelte'
|
||||
@@ -40,6 +40,9 @@
|
||||
// Multi-select mode: selected tables
|
||||
let selectedTables = $state<SelectedTable[]>([])
|
||||
|
||||
// Survives the re-mount a data table switch causes.
|
||||
let pendingCreate = $state<PendingCreate | undefined>(undefined)
|
||||
|
||||
// Selected schema/table from DBManager (for preview)
|
||||
let selectedSchemaKey = $state<string | undefined>(undefined)
|
||||
let selectedTableKey = $state<string | undefined>(undefined)
|
||||
@@ -181,6 +184,7 @@
|
||||
datatableTree={datatableTree.current}
|
||||
datatableTreeLoading={datatableTree.loading}
|
||||
onSelectDatatable={(dt) => (selectedDatatable = dt)}
|
||||
bind:pendingCreate
|
||||
/>
|
||||
{/key}
|
||||
{:else}
|
||||
|
||||
Reference in New Issue
Block a user