diff --git a/frontend/src/lib/components/workspaceSettings/DataTableSettings.svelte b/frontend/src/lib/components/workspaceSettings/DataTableSettings.svelte new file mode 100644 index 0000000000..1cbc4c7c89 --- /dev/null +++ b/frontend/src/lib/components/workspaceSettings/DataTableSettings.svelte @@ -0,0 +1,181 @@ + + + + +
+
+
Data tables
+ + Store relational data out of the box. Interact with a fully managed PostgreSQL database + directly from the windmill SDK. + +
+
+ + + + + {#each tableHeadNames as name, i} + + {name} + {#if tableHeadTooltips[name]} + + {@html tableHeadTooltips[name]} + + {/if} + + {/each} + + + + {#if tempSettings.dataTables.length == 0} + + + No data table in this workspace yet + + + {/if} + {#each tempSettings.dataTables as dataTable, dataTableIndex} + + + + + +
+
+ {#if dataTable.database.resource_type === 'instance'} + + Use Windmill's PostgreSQL instance + + {/if} + (dataTable.database.resource_path = i)} + placeholder="PostgreSQL database name" + items={[] as { value: string; label: string }[]} + disabled={!$isCustomInstanceDbEnabled} + /> + {/if} +
+
+
+ + + + + removeDataTable(dataTableIndex)} /> + +
+ {/each} + + +
+ +
+
+
+ +
+ + diff --git a/frontend/src/lib/components/workspaceSettings/DucklakeSettings.svelte b/frontend/src/lib/components/workspaceSettings/DucklakeSettings.svelte index 1910bca0e5..86d69636f8 100644 --- a/frontend/src/lib/components/workspaceSettings/DucklakeSettings.svelte +++ b/frontend/src/lib/components/workspaceSettings/DucklakeSettings.svelte @@ -65,12 +65,11 @@ import { SettingService, WorkspaceService, type DucklakeInstanceCatalogDbStatus } from '$lib/gen' import { type GetSettingsResponse } from '$lib/gen' - import { superadmin, workspaceStore } from '$lib/stores' + import { workspaceStore } from '$lib/stores' import { sendUserToast } from '$lib/toast' import ExploreAssetButton from '../ExploreAssetButton.svelte' import DbManagerDrawer from '../DBManagerDrawer.svelte' import Tooltip from '../Tooltip.svelte' - import { isCloudHosted } from '$lib/cloud' import ConfirmationModal from '../common/confirmationModal/ConfirmationModal.svelte' import { createAsyncConfirmationModal } from '../common/confirmationModal/asyncConfirmationModal.svelte' import { clone } from '$lib/utils' @@ -81,6 +80,7 @@ import LoggedWizardResult, { firstEmptyStepIsError } from '../wizards/LoggedWizardResult.svelte' import { safeSelectItems } from '../select/utils.svelte' import { slide } from 'svelte/transition' + import { isCustomInstanceDbEnabled } from './utils.svelte' const DEFAULT_DUCKLAKE_CATALOG_NAME = 'ducklake_catalog' @@ -95,8 +95,6 @@ onSave: onSaveProp = undefined }: Props = $props() - let isInstanceCatalogEnabled = $derived($superadmin && !isCloudHosted()) - function onNewDucklake() { const name = ducklakeSettings.ducklakes.some((d) => d.name === 'main') ? `${random_adj()}_ducklake` @@ -104,8 +102,8 @@ ducklakeSettings.ducklakes.push({ name, catalog: { - resource_type: isInstanceCatalogEnabled ? 'instance' : 'postgresql', - resource_path: isInstanceCatalogEnabled ? DEFAULT_DUCKLAKE_CATALOG_NAME : undefined + resource_type: $isCustomInstanceDbEnabled ? 'instance' : 'postgresql', + resource_path: $isCustomInstanceDbEnabled ? DEFAULT_DUCKLAKE_CATALOG_NAME : undefined }, storage: { storage: undefined, @@ -135,7 +133,7 @@ async function onSave() { try { if ( - isInstanceCatalogEnabled && + $isCustomInstanceDbEnabled && ducklakeSettings.ducklakes.some( (d) => d.catalog.resource_type === 'instance' && @@ -223,7 +221,7 @@ {#if ducklakeSettings.ducklakes.length == 0} - + No ducklake in this workspace yet @@ -255,7 +253,7 @@ { value: 'instance', label: 'Instance', - subtitle: isInstanceCatalogEnabled ? undefined : 'Superadmin only' + subtitle: $isCustomInstanceDbEnabled ? undefined : 'Superadmin only' } ]} bind:value={ @@ -276,8 +274,6 @@ {:else} {@const status = @@ -289,7 +285,7 @@ onCreateItem={(i) => (ducklake.catalog.resource_path = i)} placeholder="PostgreSQL database name" items={safeSelectItems(Object.keys(instanceCatalogStatuses.value ?? {}))} - disabled={!isInstanceCatalogEnabled} + disabled={!$isCustomInstanceDbEnabled} /> { if (instanceCatalogSetupIsRunning) return @@ -516,7 +512,7 @@ }} loading={instanceCatalogSetupIsRunning} > - {#if !isInstanceCatalogEnabled} + {#if !$isCustomInstanceDbEnabled} Only superadmins can setup instance catalogs {:else if status?.success} Check again @@ -532,7 +528,7 @@ asset={{ kind: 'resource', path: 'INSTANCE_DUCKLAKE_CATALOG/' + dbname }} _resourceMetadata={{ resource_type: 'postgresql' }} {dbManagerDrawer} - disabled={!isInstanceCatalogEnabled} + disabled={!$isCustomInstanceDbEnabled} onClick={() => instanceCatalogPopover?.close()} /> {/if} diff --git a/frontend/src/lib/components/workspaceSettings/utils.svelte.ts b/frontend/src/lib/components/workspaceSettings/utils.svelte.ts new file mode 100644 index 0000000000..74f9b4a6fa --- /dev/null +++ b/frontend/src/lib/components/workspaceSettings/utils.svelte.ts @@ -0,0 +1,9 @@ +import { isCloudHosted } from '$lib/cloud' + +import { superadmin } from '$lib/stores' +import { derived } from 'svelte/store' + +export let isCustomInstanceDbEnabled = derived( + [superadmin], + (superadmin_) => superadmin_ && !isCloudHosted() +) diff --git a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte index 132b626a21..52e56d6db1 100644 --- a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte @@ -59,6 +59,7 @@ import UnsavedConfirmationModal from '$lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte' import TextInput from '$lib/components/text_input/TextInput.svelte' import CollapseLink from '$lib/components/CollapseLink.svelte' + import DataTableSettings from '$lib/components/workspaceSettings/DataTableSettings.svelte' let slackInitialPath: string = $state('') let slackScriptPath: string = $state('') @@ -131,6 +132,7 @@ | 'deploy_to' | 'error_handler' | 'ai' + | 'windmill_data_tables' | 'windmill_lfs' | 'git_sync' | 'default_app' @@ -339,7 +341,9 @@ if (!$workspaceStore) return try { - const config = await WorkspaceService.getWorkspaceSlackOauthConfig({ workspace: $workspaceStore }) + const config = await WorkspaceService.getWorkspaceSlackOauthConfig({ + workspace: $workspaceStore + }) useCustomSlackApp = !!config.slack_oauth_client_id slackOAuthClientId = config.slack_oauth_client_id || '' slackOAuthClientSecret = config.slack_oauth_client_secret || '' @@ -662,6 +666,13 @@ aiDescription="Windmill AI workspace settings" label="Windmill AI" /> + {#snippet workspaceConfig()} -
- {#if slackOAuthConfigLoaded} - -
-
Workspace specific Slack app
-
-
-
- Client ID: - {slackOAuthClientId} -
- +
+
+ Client ID: + {slackOAuthClientId} +
+ +
+
-
+ {:else} + + + + {#if useCustomSlackApp} +
+ + + + + +
+ Create a Slack app at{' '} + + Slack API + . Set the redirect URI to:{' '} + + {window.location.origin}{base}/oauth/callback_slack + +
+
+ +
+ +
+
+ {/if} + {/if}
- {:else} - - - - {#if useCustomSlackApp} -
- - - - - -
- Create a Slack app at{' '} - - Slack API - . Set the redirect URI to:{' '} - - {window.location.origin}{base}/oauth/callback_slack - -
-
- -
- -
-
- {/if} - {/if} - - {/snippet} - - {:else if slack_tabs === 'teams_commands'} + {/snippet} + + {:else if slack_tabs === 'teams_commands'} {#if !$enterpriseLicense}
@@ -1102,6 +1113,8 @@ initialMaxTokensPerModel = clone(maxTokensPerModel) }} /> + {:else if tab == 'windmill_data_tables'} + {:else if tab == 'windmill_lfs'}