diff --git a/web-common/src/features/connectors/explorer/ConnectorExplorer.svelte b/web-common/src/features/connectors/explorer/ConnectorExplorer.svelte index fd0fffcc1c6..6aab5911a97 100644 --- a/web-common/src/features/connectors/explorer/ConnectorExplorer.svelte +++ b/web-common/src/features/connectors/explorer/ConnectorExplorer.svelte @@ -5,28 +5,49 @@ import { runtime } from "../../../runtime-client/runtime-store"; import ConnectorEntry from "./ConnectorEntry.svelte"; import type { ConnectorExplorerStore } from "./connector-explorer-store"; + import type { V1ConnectorDriver } from "../../../runtime-client"; export let store: ConnectorExplorerStore; export let olapOnly: boolean = false; + export let limitToConnector: string | undefined = undefined; + export let limitedConnectorDriver: V1ConnectorDriver | undefined = undefined; $: ({ instanceId } = $runtime); $: connectors = createRuntimeServiceAnalyzeConnectors(instanceId, { query: { + enabled: !!instanceId && !limitedConnectorDriver, // sort alphabetically select: (data) => { if (!data?.connectors) return; - const filtered = ( + let filtered = ( olapOnly ? data.connectors.filter((c) => c?.driver?.implementsOlap) : data.connectors ).sort((a, b) => (a?.name as string).localeCompare(b?.name as string)); + if (limitToConnector) { + filtered = filtered.filter((c) => c?.name === limitToConnector); + } + return { connectors: filtered }; }, }, }); $: ({ data, error } = $connectors); + $: connectorsData = limitedConnectorDriver + ? { + connectors: [ + { + name: + (limitToConnector as string) ?? + (limitedConnectorDriver.name as string) ?? + "", + driver: limitedConnectorDriver, + }, + ], + } + : data;
@@ -34,12 +55,12 @@ {error.message} - {:else if data?.connectors} - {#if data.connectors.length === 0} + {:else if connectorsData?.connectors} + {#if connectorsData.connectors.length === 0} No data found. Add data to get started! {:else}
    - {#each data.connectors as connector (connector.name)} + {#each connectorsData.connectors as connector (connector.name)} {/each}
diff --git a/web-common/src/features/connectors/explorer/TableEntry.svelte b/web-common/src/features/connectors/explorer/TableEntry.svelte index 37c1a653d64..4b4740ad8e4 100644 --- a/web-common/src/features/connectors/explorer/TableEntry.svelte +++ b/web-common/src/features/connectors/explorer/TableEntry.svelte @@ -29,6 +29,13 @@ $: expandedStore = store.getItem(connector, database, databaseSchema, table); $: showSchema = $expandedStore; + $: selectedStore = store.isSelected( + connector, + database, + databaseSchema, + table, + ); + $: isSelected = $selectedStore; const { allowContextMenu, allowNavigateToTable, allowShowSchema } = store; @@ -62,6 +69,7 @@
{#if allowShowSchema} + + +
+
diff --git a/web-common/src/features/sources/modal/AddDataForm.svelte b/web-common/src/features/sources/modal/AddDataForm.svelte index ee081dc8f34..b99abeec197 100644 --- a/web-common/src/features/sources/modal/AddDataForm.svelte +++ b/web-common/src/features/sources/modal/AddDataForm.svelte @@ -26,6 +26,9 @@ import { AddDataFormManager } from "./AddDataFormManager"; import { hasOnlyDsn } from "./utils"; import AddDataFormSection from "./AddDataFormSection.svelte"; + import AddDataExplorerStep from "./AddDataExplorerStep.svelte"; + import { goto } from "$app/navigation"; + import { cn } from "@rilldata/web-common/lib/shadcn"; export let connector: V1ConnectorDriver; export let formType: AddDataFormType; @@ -267,7 +270,9 @@ }); $: isClickhouse = connector.name === "clickhouse"; $: shouldShowSaveAnywayButton = - isConnectorForm && (showSaveAnyway || clickhouseShowSaveAnyway); + isConnectorForm && + (showSaveAnyway || clickhouseShowSaveAnyway) && + stepState.step !== "explorer"; $: saveAnywayLoading = isClickhouse ? clickhouseSubmitting && saveAnyway : submitting && saveAnyway; @@ -306,103 +311,132 @@
-
- {#if connector.name === "clickhouse"} - { - clickhouseError = error; - clickhouseErrorDetails = details; - }} - bind:formId={clickhouseFormId} - bind:isSubmitting={clickhouseSubmitting} - bind:isSubmitDisabled={clickhouseIsSubmitDisabled} - bind:connectorType={clickhouseConnectorType} - bind:connectionTab - bind:paramsForm={clickhouseParamsForm} - bind:dsnForm={clickhouseDsnForm} - bind:showSaveAnyway={clickhouseShowSaveAnyway} - /> - {:else if hasDsnFormOption} - - + {#if stepState.step === "explorer"} + formManager.handleBack(onBack)} + onModelCreated={async (path) => { + await goto(`/files${path}`); + onClose(); + }} + /> + {:else} +
+ {#if connector.name === "clickhouse"} + { + clickhouseError = error; + clickhouseErrorDetails = details; + }} + bind:formId={clickhouseFormId} + bind:isSubmitting={clickhouseSubmitting} + bind:isSubmitDisabled={clickhouseIsSubmitDisabled} + bind:connectorType={clickhouseConnectorType} + bind:connectionTab + bind:paramsForm={clickhouseParamsForm} + bind:dsnForm={clickhouseDsnForm} + bind:showSaveAnyway={clickhouseShowSaveAnyway} + /> + {:else if hasDsnFormOption} + + + + + + + + + + + + + {:else if isConnectorForm && connector.configProperties?.some((property) => property.key === "dsn")} + + + + + {:else if isMultiStepConnector} + {#if stepState.step === "connector"} + - - - + {:else} + - - - {:else if isConnectorForm && connector.configProperties?.some((property) => property.key === "dsn")} - - - - - {:else if isMultiStepConnector} - {#if stepState.step === "connector"} - - - - + {/if} {:else} - {/if} - {:else} - - - - {/if} -
+
- -
- +
+ + +
+ {#if shouldShowSaveAnywayButton} + + {/if} + + {#if isMultiStepConnector && stepState.step === "connector"} + + {/if} -
- {#if shouldShowSaveAnywayButton} - {/if} - - {#if isMultiStepConnector && stepState.step === "connector"} - - {/if} - - +
-
+ {/if}
-
- {#if dsnError || paramsError || clickhouseError} - + {#if dsnError || paramsError || clickhouseError} + + {/if} + + - {/if} - - - -
+ +
+ {/if} diff --git a/web-common/src/features/sources/modal/AddDataFormManager.ts b/web-common/src/features/sources/modal/AddDataFormManager.ts index e2d0f9c4c10..ccf3d7b4ba4 100644 --- a/web-common/src/features/sources/modal/AddDataFormManager.ts +++ b/web-common/src/features/sources/modal/AddDataFormManager.ts @@ -43,7 +43,7 @@ type SuperFormUpdateEvent = { // Shape of the step store for multi-step connectors type ConnectorStepState = { - step: "connector" | "source"; + step: "connector" | "source" | "explorer"; connectorConfig: Record | null; }; @@ -190,6 +190,11 @@ export class AddDataFormManager { const stepState = get(connectorStepStore) as ConnectorStepState; if (this.isMultiStepConnector && stepState.step === "source") { setStep("connector"); + } else if (this.isMultiStepConnector && stepState.step === "explorer") { + setStep("source"); + } else if (!this.isMultiStepConnector && stepState.step === "explorer") { + // For non-multi-step connectors, return to the prior step based on form type + setStep(this.isSourceForm ? "source" : "connector"); } else { onBack(); } @@ -254,6 +259,10 @@ export class AddDataFormManager { connector.name ?? "", ); const isConnectorForm = this.formType === "connector"; + const supportsTableExplorer = + Boolean((connector as any)?.implementsOlap) || + Boolean((connector as any)?.implementsSqlStore) || + Boolean((connector as any)?.implementsWarehouse); return async (event: { form: SuperValidated< @@ -281,7 +290,13 @@ export class AddDataFormManager { const stepState = get(connectorStepStore) as ConnectorStepState; if (isMultiStepConnector && stepState.step === "source") { await submitAddSourceForm(queryClient, connector, values); + // Advance to Step 3 (Table Explorer) only if supported; otherwise close + if (supportsTableExplorer) { + setStep("explorer"); + return; + } onClose(); + return; } else if (isMultiStepConnector && stepState.step === "connector") { await submitAddConnectorForm(queryClient, connector, values, false); setConnectorConfig(values); @@ -289,9 +304,17 @@ export class AddDataFormManager { return; } else if (this.formType === "source") { await submitAddSourceForm(queryClient, connector, values); + if (supportsTableExplorer) { + setStep("explorer"); + return; + } onClose(); } else { await submitAddConnectorForm(queryClient, connector, values, false); + if (supportsTableExplorer) { + setStep("explorer"); + return; + } onClose(); } } catch (e) { diff --git a/web-common/src/features/sources/modal/DataExplorerDialog.svelte b/web-common/src/features/sources/modal/DataExplorerDialog.svelte new file mode 100644 index 00000000000..8e6d5614a74 --- /dev/null +++ b/web-common/src/features/sources/modal/DataExplorerDialog.svelte @@ -0,0 +1,145 @@ + + +
+ + + + +
+
+

Data explorer

+

Pick a table to create a model

+
+ +
+ +
+
+
diff --git a/web-common/src/features/sources/modal/connectorStepStore.ts b/web-common/src/features/sources/modal/connectorStepStore.ts index 9ce2451f126..7fd4399a77b 100644 --- a/web-common/src/features/sources/modal/connectorStepStore.ts +++ b/web-common/src/features/sources/modal/connectorStepStore.ts @@ -1,6 +1,6 @@ import { writable } from "svelte/store"; -export type ConnectorStep = "connector" | "source"; +export type ConnectorStep = "connector" | "source" | "explorer"; export const connectorStepStore = writable<{ step: ConnectorStep; diff --git a/web-common/src/features/sources/modal/model-creation-utils.ts b/web-common/src/features/sources/modal/model-creation-utils.ts new file mode 100644 index 00000000000..ccd2bb2f574 --- /dev/null +++ b/web-common/src/features/sources/modal/model-creation-utils.ts @@ -0,0 +1,32 @@ +import type { QueryClient } from "@tanstack/svelte-query"; +import { + createSqlModelFromTable, + createYamlModelFromTable, +} from "../../connectors/code-utils"; + +export async function createModelFromExplorerSelection( + queryClient: QueryClient, + options: { + connector: string; + database: string; + schema: string; + table: string; + isModelingSupported: boolean; + }, +): Promise<[string, string]> { + return options.isModelingSupported + ? await createSqlModelFromTable( + queryClient, + options.connector, + options.database, + options.schema, + options.table, + ) + : await createYamlModelFromTable( + queryClient, + options.connector, + options.database, + options.schema, + options.table, + ); +}