mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-05 08:02:18 +00:00
206 lines
6.2 KiB
Svelte
206 lines
6.2 KiB
Svelte
<script lang="ts">
|
|
import { type DBSchema } from '$lib/stores'
|
|
import { MoreVertical, Plus, Table2 } from 'lucide-svelte'
|
|
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
|
import { ClearableInput, Drawer, DrawerContent } from './common'
|
|
import { sendUserToast } from '$lib/toast'
|
|
import { type ColumnDef } from './apps/components/display/dbtable/utils'
|
|
import DBTable from './DBTable.svelte'
|
|
import type { DbTableActionFactory, IDbTableOps } from './dbOps'
|
|
import DropdownV2 from './DropdownV2.svelte'
|
|
import ConfirmationModal from './common/confirmationModal/ConfirmationModal.svelte'
|
|
import Button from './common/button/Button.svelte'
|
|
import DbTableEditor, { type DBTableEditorProps } from './DBTableEditor.svelte'
|
|
|
|
type Props = {
|
|
dbSchema: DBSchema
|
|
dbSupportsSchemas: boolean
|
|
getColDefs: (tableKey: string) => Promise<ColumnDef[]>
|
|
dbTableOpsFactory: (params: { colDefs: ColumnDef[]; tableKey: string }) => IDbTableOps
|
|
dbTableActionsFactory?: DbTableActionFactory[]
|
|
refresh?: () => void
|
|
dbTableEditorPropsFactory?: (params: { selectedSchemaKey?: string }) => DBTableEditorProps
|
|
}
|
|
let {
|
|
dbSchema,
|
|
dbTableOpsFactory,
|
|
getColDefs,
|
|
dbTableActionsFactory,
|
|
refresh,
|
|
dbTableEditorPropsFactory,
|
|
dbSupportsSchemas
|
|
}: Props = $props()
|
|
|
|
let schemaKeys = $derived(Object.keys(dbSchema.schema))
|
|
let search = $state('')
|
|
let selected: {
|
|
schemaKey?: undefined | string
|
|
tableKey?: undefined | string
|
|
} = $state({})
|
|
|
|
$effect(() => {
|
|
if (!selected.schemaKey && schemaKeys.length) {
|
|
selected = {
|
|
schemaKey:
|
|
'public' in dbSchema.schema ? 'public' : 'dbo' in dbSchema.schema ? 'dbo' : schemaKeys[0]
|
|
}
|
|
}
|
|
})
|
|
|
|
let tableKeys = $derived.by(() => {
|
|
if (dbSchema.lang === 'graphql') {
|
|
sendUserToast('graphql not supported by DBExplorerTable', true)
|
|
return []
|
|
}
|
|
if (!selected.schemaKey) return []
|
|
return Object.keys(dbSchema.schema[selected.schemaKey])
|
|
})
|
|
|
|
$effect(() => {
|
|
if (tableKeys.length && !selected.tableKey) {
|
|
selected.tableKey = filteredTableKeys[0]
|
|
}
|
|
})
|
|
|
|
let filteredTableKeys = $derived.by(() => {
|
|
const l = tableKeys.filter((tk) => tk.includes(search))
|
|
l.sort()
|
|
return l
|
|
})
|
|
|
|
let tableKey = $derived(
|
|
dbSupportsSchemas && selected.schemaKey
|
|
? `${selected.schemaKey}.${selected.tableKey}`
|
|
: selected.tableKey
|
|
)
|
|
|
|
let askingForConfirmation:
|
|
| (ConfirmationModal['$$prop_def'] & { onConfirm: () => void })
|
|
| undefined = $state()
|
|
|
|
let dbTableEditorState: { open: boolean } = $state({ open: false })
|
|
|
|
let dbTableEditorProps = $derived(
|
|
dbTableEditorPropsFactory?.({ selectedSchemaKey: selected.schemaKey })
|
|
)
|
|
</script>
|
|
|
|
<Splitpanes>
|
|
<Pane size={24} class="relative flex flex-col">
|
|
<div class="mx-3 mt-3">
|
|
{#if dbSupportsSchemas}
|
|
<select
|
|
value={selected.schemaKey}
|
|
onchange={(e) => {
|
|
selected = { schemaKey: e.currentTarget.value }
|
|
}}
|
|
>
|
|
{#each schemaKeys as schemaKey}
|
|
<option value={schemaKey}>{schemaKey}</option>
|
|
{/each}
|
|
</select>
|
|
{/if}
|
|
<ClearableInput wrapperClass="mt-3" bind:value={search} placeholder="Search table..." />
|
|
</div>
|
|
<div class="overflow-x-clip overflow-y-auto relative mt-3 border-y flex-1">
|
|
{#each filteredTableKeys as tableKey}
|
|
<button
|
|
class={'w-full text-sm font-normal flex gap-2 items-center h-10 cursor-pointer pl-3 pr-1 ' +
|
|
(selected.tableKey === tableKey ? 'bg-gray-500/25' : 'hover:bg-gray-500/10')}
|
|
onclick={() => (selected.tableKey = tableKey)}
|
|
>
|
|
<Table2 class="text-primary shrink-0" size={16} />
|
|
<p class="truncate text-ellipsis grow text-left text-emphasis text-xs">{tableKey}</p>
|
|
{#if dbTableActionsFactory}
|
|
{@const dbTableActions = dbTableActionsFactory.map((f) =>
|
|
f({
|
|
tableKey: `${selected.schemaKey}.${tableKey}`,
|
|
refresh: refresh ?? (() => {})
|
|
})
|
|
)}
|
|
<DropdownV2
|
|
items={() =>
|
|
dbTableActions.map((tableAction) => ({
|
|
displayName: tableAction.displayName,
|
|
...(tableAction.icon ? { icon: tableAction.icon } : {}),
|
|
action: () =>
|
|
(askingForConfirmation = {
|
|
title: tableAction.confirmTitle ?? 'Are you sure ?',
|
|
confirmationText: tableAction.confirmBtnText ?? 'Confirm',
|
|
open: true,
|
|
onConfirm: async () => {
|
|
askingForConfirmation && (askingForConfirmation.loading = true)
|
|
try {
|
|
await tableAction.action()
|
|
tableAction.successText && sendUserToast(tableAction.successText)
|
|
} catch (e) {
|
|
let msg: string | undefined = (e as Error).message
|
|
if (typeof msg !== 'string') msg = e ? JSON.stringify(e) : undefined
|
|
sendUserToast(msg ?? 'Action failed!', true)
|
|
}
|
|
askingForConfirmation = undefined
|
|
}
|
|
})
|
|
}))}
|
|
class="w-fit"
|
|
>
|
|
<svelte:fragment slot="buttonReplacement">
|
|
<MoreVertical
|
|
size={8}
|
|
class="w-8 h-8 p-2 hover:bg-surface-hover cursor-pointer rounded-md"
|
|
/>
|
|
</svelte:fragment>
|
|
</DropdownV2>
|
|
{/if}
|
|
</button>
|
|
{/each}
|
|
</div>
|
|
<Button
|
|
on:click={() => (dbTableEditorState = { open: true })}
|
|
wrapperClasses="mx-2 my-2 text-sm"
|
|
startIcon={{ icon: Plus }}
|
|
>
|
|
New table
|
|
</Button>
|
|
</Pane>
|
|
<Pane class="p-3 pt-1">
|
|
{#if tableKey}
|
|
{#await getColDefs(tableKey) then colDefs}
|
|
{#if colDefs && colDefs?.length}
|
|
{@const dbTableOps = dbTableOpsFactory({ colDefs, tableKey })}
|
|
<DBTable {dbTableOps} />
|
|
{/if}
|
|
{/await}
|
|
{/if}
|
|
</Pane>
|
|
</Splitpanes>
|
|
|
|
<ConfirmationModal
|
|
{...askingForConfirmation ?? { confirmationText: '', title: '' }}
|
|
on:canceled={() => (askingForConfirmation = undefined)}
|
|
on:confirmed={askingForConfirmation?.onConfirm ?? (() => {})}
|
|
/>
|
|
|
|
{#if dbTableEditorProps}
|
|
<Drawer
|
|
size="600px"
|
|
open={dbTableEditorState.open}
|
|
on:close={() => (dbTableEditorState = { open: false })}
|
|
>
|
|
<DrawerContent
|
|
on:close={() => (dbTableEditorState = { open: false })}
|
|
title="Create a new table"
|
|
>
|
|
<DbTableEditor
|
|
{...dbTableEditorProps}
|
|
{dbSchema}
|
|
currentSchema={selected.schemaKey}
|
|
onConfirm={async (values) => {
|
|
await dbTableEditorProps.onConfirm(values)
|
|
dbTableEditorState = { open: false }
|
|
}}
|
|
/>
|
|
</DrawerContent>
|
|
</Drawer>
|
|
{/if}
|