From 8ec95e5358f062e338a8e4bb2d8340286c711a2d Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 11:49:14 +0100 Subject: [PATCH 01/19] fix integrations management view stylesheet usage --- .../deepnote/integrations/integrationWebview.ts | 11 ----------- src/webviews/webview-side/integrations/index.tsx | 1 + 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/notebooks/deepnote/integrations/integrationWebview.ts b/src/notebooks/deepnote/integrations/integrationWebview.ts index 99d6b1239e..aa63eeb1be 100644 --- a/src/notebooks/deepnote/integrations/integrationWebview.ts +++ b/src/notebooks/deepnote/integrations/integrationWebview.ts @@ -590,16 +590,6 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { 'index.js' ) ); - const styleUri = webview.asWebviewUri( - Uri.joinPath( - this.extensionContext.extensionUri, - 'dist', - 'webviews', - 'webview-side', - 'integrations', - 'integrations.css' - ) - ); const codiconUri = webview.asWebviewUri( Uri.joinPath( this.extensionContext.extensionUri, @@ -619,7 +609,6 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { - Deepnote Integrations diff --git a/src/webviews/webview-side/integrations/index.tsx b/src/webviews/webview-side/integrations/index.tsx index 5c6e3166f5..c4fc3f7ece 100644 --- a/src/webviews/webview-side/integrations/index.tsx +++ b/src/webviews/webview-side/integrations/index.tsx @@ -6,6 +6,7 @@ import { detectBaseTheme } from '../react-common/themeDetector'; import { IntegrationPanel } from './IntegrationPanel'; import '../common/index.css'; +import './integrations.css'; // This special function talks to vscode from a web panel declare function acquireVsCodeApi(): IVsCodeApi; From fff1e717f2d05fabb0b2289884c97ac7ff243e3c Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 11:51:28 +0100 Subject: [PATCH 02/19] add new integration creating cards to integration management --- src/messageTypes.ts | 2 + .../integrations/integrationWebview.ts | 13 ++ src/platform/common/utils/localize.ts | 2 + .../integrations/IntegrationPanel.tsx | 16 +++ .../integrations/IntegrationTypeSelector.tsx | 126 ++++++++++++++++++ .../integrations/integrations.css | 60 +++++++++ 6 files changed, 219 insertions(+) create mode 100644 src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx diff --git a/src/messageTypes.ts b/src/messageTypes.ts index 53b36b44cb..18d20fb019 100644 --- a/src/messageTypes.ts +++ b/src/messageTypes.ts @@ -179,6 +179,8 @@ export type LocalizedMessages = { integrationsConfigureTitle: string; integrationsCancel: string; integrationsSave: string; + integrationsAddNewIntegration: string; + integrationsDatabase: string; // Integration type labels integrationsPostgresTypeLabel: string; integrationsBigQueryTypeLabel: string; diff --git a/src/notebooks/deepnote/integrations/integrationWebview.ts b/src/notebooks/deepnote/integrations/integrationWebview.ts index aa63eeb1be..9c296446da 100644 --- a/src/notebooks/deepnote/integrations/integrationWebview.ts +++ b/src/notebooks/deepnote/integrations/integrationWebview.ts @@ -127,6 +127,8 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { integrationsConfirmResetMessage: localize.Integrations.confirmResetMessage, integrationsConfirmResetDetails: localize.Integrations.confirmResetDetails, integrationsConfigureTitle: localize.Integrations.configureTitle, + integrationsAddNewIntegration: localize.Integrations.addNewIntegration, + integrationsDatabase: localize.Integrations.database, integrationsPostgresTypeLabel: localize.Integrations.postgresTypeLabel, integrationsBigQueryTypeLabel: localize.Integrations.bigQueryTypeLabel, integrationsSnowflakeTypeLabel: localize.Integrations.snowflakeTypeLabel, @@ -464,9 +466,20 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { // Update local state const integration = this.integrations.get(integrationId); if (integration) { + // Existing integration - update it integration.config = config; integration.status = IntegrationStatus.Connected; + integration.integrationName = config.name; + integration.integrationType = config.type; this.integrations.set(integrationId, integration); + } else { + // New integration - add it to the map + this.integrations.set(integrationId, { + config, + status: IntegrationStatus.Connected, + integrationName: config.name, + integrationType: config.type + }); } // Update the project's integrations list diff --git a/src/platform/common/utils/localize.ts b/src/platform/common/utils/localize.ts index 6296b4b2eb..00aa43d446 100644 --- a/src/platform/common/utils/localize.ts +++ b/src/platform/common/utils/localize.ts @@ -828,6 +828,8 @@ export namespace Integrations { export const configureTitle = l10n.t('Configure Integration: {0}'); export const cancel = l10n.t('Cancel'); export const save = l10n.t('Save'); + export const addNewIntegration = l10n.t('Add New Integration'); + export const database = l10n.t('Database'); export const requiredField = l10n.t('*'); export const optionalField = l10n.t('(optional)'); export const unnamedIntegration = (id: string) => l10n.t('Unnamed Integration ({0})', id); diff --git a/src/webviews/webview-side/integrations/IntegrationPanel.tsx b/src/webviews/webview-side/integrations/IntegrationPanel.tsx index e493f30ab5..5255e89c91 100644 --- a/src/webviews/webview-side/integrations/IntegrationPanel.tsx +++ b/src/webviews/webview-side/integrations/IntegrationPanel.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { IVsCodeApi } from '../react-common/postOffice'; import { getLocString, storeLocStrings } from '../react-common/locReactSide'; import { IntegrationList } from './IntegrationList'; +import { IntegrationTypeSelector } from './IntegrationTypeSelector'; import { ConfigurationForm } from './ConfigurationForm'; import { ConfigurableDatabaseIntegrationConfig, @@ -143,6 +144,19 @@ export const IntegrationPanel: React.FC = ({ baseTheme, const handleCancel = () => { setSelectedIntegrationId(null); setSelectedConfig(null); + setSelectedIntegrationDefaultName(undefined); + setSelectedIntegrationType(undefined); + }; + + const handleSelectIntegrationType = (type: ConfigurableDatabaseIntegrationType) => { + // Generate a new UUID for the integration + const newId = crypto.randomUUID(); + + // Set up the form for creating a new integration + setSelectedIntegrationId(newId); + setSelectedConfig(null); + setSelectedIntegrationDefaultName(undefined); + setSelectedIntegrationType(type); }; return ( @@ -153,6 +167,8 @@ export const IntegrationPanel: React.FC = ({ baseTheme, + + {selectedIntegrationId && selectedIntegrationType && ( void; +} + +interface IntegrationTypeInfo { + type: ConfigurableDatabaseIntegrationType; + label: string; + icon: string; +} + +const INTEGRATION_TYPES: IntegrationTypeInfo[] = [ + { + type: 'pgsql', + label: 'PostgreSQL', + icon: '🐘' + }, + { + type: 'mysql', + label: 'MySQL', + icon: '🐬' + }, + { + type: 'mariadb', + label: 'MariaDB', + icon: '🦭' + }, + { + type: 'mongodb', + label: 'MongoDB', + icon: '🍃' + }, + { + type: 'sql-server', + label: 'Microsoft SQL Server', + icon: '🗄️' + }, + { + type: 'big-query', + label: 'Google BigQuery', + icon: '📊' + }, + { + type: 'snowflake', + label: 'Snowflake', + icon: '❄️' + }, + { + type: 'alloydb', + label: 'Google AlloyDB', + icon: '🔷' + }, + { + type: 'spanner', + label: 'Google Cloud Spanner', + icon: '🔧' + }, + { + type: 'materialize', + label: 'Materialize', + icon: '⚡' + }, + { + type: 'clickhouse', + label: 'ClickHouse', + icon: '🏠' + }, + { + type: 'athena', + label: 'Amazon Athena', + icon: '🏛️' + }, + { + type: 'redshift', + label: 'Amazon Redshift', + icon: '🔴' + }, + { + type: 'databricks', + label: 'Databricks', + icon: '🧱' + }, + { + type: 'dremio', + label: 'Dremio', + icon: '🚀' + }, + { + type: 'mindsdb', + label: 'MindsDB', + icon: '🧠' + }, + { + type: 'trino', + label: 'Trino', + icon: '⚙️' + } +]; + +export const IntegrationTypeSelector: React.FC = ({ onSelectType }) => { + return ( +
+

{getLocString('integrationsAddNewIntegration', 'Add New Integration')}

+
+ {INTEGRATION_TYPES.map((integrationInfo) => ( + + ))} +
+
+ ); +}; + diff --git a/src/webviews/webview-side/integrations/integrations.css b/src/webviews/webview-side/integrations/integrations.css index a550f6054f..95456bf8f4 100644 --- a/src/webviews/webview-side/integrations/integrations.css +++ b/src/webviews/webview-side/integrations/integrations.css @@ -1,5 +1,7 @@ .integration-panel { padding: 20px; + max-width: 640px; + margin: 0 auto; color: var(--vscode-foreground); font-family: var(--vscode-font-family); font-size: var(--vscode-font-size); @@ -263,3 +265,61 @@ form { .form-actions button { flex: 1; } + +/* Integration type selector */ +.integration-type-selector { + margin-top: 32px; + padding-top: 32px; + border-top: 1px solid var(--vscode-panel-border); +} + +.integration-type-selector h2 { + margin-top: 0; + margin-bottom: 16px; + font-size: 1.2em; + font-weight: 600; +} + +.integration-type-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; +} + +.integration-type-card { + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; + padding: 12px 16px; + background-color: var(--vscode-editor-background); + border: 1px solid var(--vscode-panel-border); + border-radius: 4px; + cursor: pointer; + text-align: left; + transition: + background-color 0.2s, + border-color 0.2s; +} + +.integration-type-card:hover { + background-color: var(--vscode-toolbar-hoverBackground); + border-color: var(--vscode-focusBorder); +} + +.integration-type-icon { + font-size: 24px; + line-height: 1; + flex-shrink: 0; +} + +.integration-type-label { + font-weight: 400; + font-size: 13px; + flex: 1; + color: var(--vscode-foreground); +} + +.integration-type-category { + display: none; +} From 67d2557f44a51bd42f531e60dd62b4470eea0753 Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 11:58:00 +0100 Subject: [PATCH 03/19] refactor: replace method with map --- .../deepnote/sqlCellStatusBarProvider.ts | 63 +++++++------------ 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/src/notebooks/deepnote/sqlCellStatusBarProvider.ts b/src/notebooks/deepnote/sqlCellStatusBarProvider.ts index b7fade350f..f47b0fcf90 100644 --- a/src/notebooks/deepnote/sqlCellStatusBarProvider.ts +++ b/src/notebooks/deepnote/sqlCellStatusBarProvider.ts @@ -36,6 +36,26 @@ interface LocalQuickPickItem extends QuickPickItem { id: string; } +const integrationTypeLabels: Record = { + alloydb: l10n.t('AlloyDB'), + athena: l10n.t('Amazon Athena'), + 'big-query': l10n.t('BigQuery'), + clickhouse: l10n.t('ClickHouse'), + databricks: l10n.t('Databricks'), + dremio: l10n.t('Dremio'), + mariadb: l10n.t('MariaDB'), + materialize: l10n.t('Materialize'), + mindsdb: l10n.t('MindsDB'), + mongodb: l10n.t('MongoDB'), + mysql: l10n.t('MySQL'), + pgsql: l10n.t('PostgreSQL'), + redshift: l10n.t('Amazon Redshift'), + snowflake: l10n.t('Snowflake'), + spanner: l10n.t('Google Cloud Spanner'), + 'sql-server': l10n.t('SQL Server'), + trino: l10n.t('Trino') +}; + /** * Provides status bar items for SQL cells showing the integration name and variable name */ @@ -354,7 +374,7 @@ export class SqlCellStatusBarProvider implements NotebookCellStatusBarItemProvid const typeLabel = integrationType && (databaseIntegrationTypes as readonly string[]).includes(integrationType) - ? this.getIntegrationTypeLabel(integrationType) + ? integrationTypeLabels[integrationType] ?? integrationType : projectIntegration.type; const item: LocalQuickPickItem = { @@ -437,45 +457,4 @@ export class SqlCellStatusBarProvider implements NotebookCellStatusBarItemProvid // Trigger status bar update this._onDidChangeCellStatusBarItems.fire(); } - - private getIntegrationTypeLabel(type: ConfigurableDatabaseIntegrationType): string { - switch (type) { - case 'alloydb': - return l10n.t('AlloyDB'); - case 'athena': - return l10n.t('Amazon Athena'); - case 'big-query': - return l10n.t('BigQuery'); - case 'clickhouse': - return l10n.t('ClickHouse'); - case 'databricks': - return l10n.t('Databricks'); - case 'dremio': - return l10n.t('Dremio'); - case 'mariadb': - return l10n.t('MariaDB'); - case 'materialize': - return l10n.t('Materialize'); - case 'mindsdb': - return l10n.t('MindsDB'); - case 'mongodb': - return l10n.t('MongoDB'); - case 'mysql': - return l10n.t('MySQL'); - case 'pgsql': - return l10n.t('PostgreSQL'); - case 'redshift': - return l10n.t('Amazon Redshift'); - case 'snowflake': - return l10n.t('Snowflake'); - case 'spanner': - return l10n.t('Google Cloud Spanner'); - case 'sql-server': - return l10n.t('SQL Server'); - case 'trino': - return l10n.t('Trino'); - default: - return String(type); - } - } } From af1f1519c5be0289a3f7ce9b4b366cac247544b1 Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 11:59:33 +0100 Subject: [PATCH 04/19] replace integration configuration form title to include integration type --- .../integrations/ConfigurationForm.tsx | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/webviews/webview-side/integrations/ConfigurationForm.tsx b/src/webviews/webview-side/integrations/ConfigurationForm.tsx index 62e4fc94b8..c6f9035ce6 100644 --- a/src/webviews/webview-side/integrations/ConfigurationForm.tsx +++ b/src/webviews/webview-side/integrations/ConfigurationForm.tsx @@ -19,6 +19,27 @@ import { SQLServerForm } from './SQLServerForm'; import { TrinoForm } from './TrinoForm'; import { ConfigurableDatabaseIntegrationConfig, ConfigurableDatabaseIntegrationType } from './types'; +// Localized labels for integration types (duplicated from sqlCellStatusBarProvider.ts due to import restrictions) +const integrationTypeLabels: Record = { + alloydb: 'AlloyDB', + athena: 'Amazon Athena', + 'big-query': 'BigQuery', + clickhouse: 'ClickHouse', + databricks: 'Databricks', + dremio: 'Dremio', + mariadb: 'MariaDB', + materialize: 'Materialize', + mindsdb: 'MindsDB', + mongodb: 'MongoDB', + mysql: 'MySQL', + pgsql: 'PostgreSQL', + redshift: 'Amazon Redshift', + snowflake: 'Snowflake', + spanner: 'Google Cloud Spanner', + 'sql-server': 'SQL Server', + trino: 'Trino' +}; + export interface IConfigurationFormProps { integrationId: string; existingConfig: ConfigurableDatabaseIntegrationConfig | null; @@ -36,10 +57,8 @@ export const ConfigurationForm: React.FC = ({ onSave, onCancel }) => { - const title = getLocString('integrationsConfigureTitle', 'Configure Integration: {0}').replace( - '{0}', - integrationId - ); + const typeLabel = integrationTypeLabels[integrationType] || integrationType; + const title = getLocString('integrationsConfigureTitle', '{0} integration').replace('{0}', typeLabel); return (
From 21cf5d2e3e73c6b9ea9554d1712b16420d2ce977 Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 12:13:50 +0100 Subject: [PATCH 05/19] replace default integration name placeholder --- src/messageTypes.ts | 1 + .../integrations/integrationWebview.ts | 1 + src/platform/common/utils/localize.ts | 1 + .../webview-side/integrations/AlloyDBForm.tsx | 7 ++-- .../webview-side/integrations/AthenaForm.tsx | 7 ++-- .../integrations/BigQueryForm.tsx | 5 ++- .../integrations/ClickHouseForm.tsx | 7 ++-- .../integrations/ConfigurationForm.tsx | 22 +----------- .../integrations/DatabricksForm.tsx | 7 ++-- .../webview-side/integrations/DremioForm.tsx | 7 ++-- .../webview-side/integrations/MariaDBForm.tsx | 7 ++-- .../integrations/MaterializeForm.tsx | 7 ++-- .../webview-side/integrations/MindsDBForm.tsx | 7 ++-- .../webview-side/integrations/MongoDBForm.tsx | 7 ++-- .../webview-side/integrations/MySQLForm.tsx | 7 ++-- .../integrations/PostgresForm.tsx | 7 ++-- .../integrations/RedshiftForm.tsx | 7 ++-- .../integrations/SQLServerForm.tsx | 7 ++-- .../integrations/SnowflakeForm.tsx | 7 ++-- .../webview-side/integrations/SpannerForm.tsx | 7 ++-- .../webview-side/integrations/TrinoForm.tsx | 7 ++-- .../integrations/integrationUtils.ts | 34 +++++++++++++++++++ 22 files changed, 88 insertions(+), 88 deletions(-) create mode 100644 src/webviews/webview-side/integrations/integrationUtils.ts diff --git a/src/messageTypes.ts b/src/messageTypes.ts index 18d20fb019..053539219a 100644 --- a/src/messageTypes.ts +++ b/src/messageTypes.ts @@ -444,6 +444,7 @@ export type LocalizedMessages = { integrationsRequiredField: string; integrationsOptionalField: string; integrationsUnnamedIntegration: string; + integrationsDefaultName: string; integrationsUnsupportedIntegrationType: string; // Select input settings strings selectInputSettingsTitle: string; diff --git a/src/notebooks/deepnote/integrations/integrationWebview.ts b/src/notebooks/deepnote/integrations/integrationWebview.ts index 9c296446da..4ee04cc298 100644 --- a/src/notebooks/deepnote/integrations/integrationWebview.ts +++ b/src/notebooks/deepnote/integrations/integrationWebview.ts @@ -375,6 +375,7 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { integrationsCaCertificateText: localize.Integrations.caCertificateText, integrationsCaCertificateTextPlaceholder: localize.Integrations.caCertificateTextPlaceholder, integrationsUnnamedIntegration: localize.Integrations.unnamedIntegration('{0}'), + integrationsDefaultName: localize.Integrations.defaultName('{0}'), integrationsUnsupportedIntegrationType: localize.Integrations.unsupportedIntegrationType('{0}') }; diff --git a/src/platform/common/utils/localize.ts b/src/platform/common/utils/localize.ts index 00aa43d446..6e4d82c2cc 100644 --- a/src/platform/common/utils/localize.ts +++ b/src/platform/common/utils/localize.ts @@ -833,6 +833,7 @@ export namespace Integrations { export const requiredField = l10n.t('*'); export const optionalField = l10n.t('(optional)'); export const unnamedIntegration = (id: string) => l10n.t('Unnamed Integration ({0})', id); + export const defaultName = (type: string) => l10n.t('My {0} integration', type); export const unsupportedIntegrationType = (type: string) => l10n.t('Unsupported integration type: {0}', type); // Integration type labels diff --git a/src/webviews/webview-side/integrations/AlloyDBForm.tsx b/src/webviews/webview-side/integrations/AlloyDBForm.tsx index 02e7721356..921c7a72b5 100644 --- a/src/webviews/webview-side/integrations/AlloyDBForm.tsx +++ b/src/webviews/webview-side/integrations/AlloyDBForm.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; import { CaCertificateFields } from './CaCertificateFields'; +import { getDefaultIntegrationName } from './integrationUtils'; export interface IAlloyDBFormProps { integrationId: string; @@ -16,11 +17,9 @@ function createEmptyAlloyDBConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('alloydb')).trim(), type: 'alloydb', metadata: { host: '', diff --git a/src/webviews/webview-side/integrations/AthenaForm.tsx b/src/webviews/webview-side/integrations/AthenaForm.tsx index 13f10297db..9a31007928 100644 --- a/src/webviews/webview-side/integrations/AthenaForm.tsx +++ b/src/webviews/webview-side/integrations/AthenaForm.tsx @@ -1,16 +1,15 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; +import { getDefaultIntegrationName } from './integrationUtils'; function createEmptyAthenaConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('athena')).trim(), type: 'athena', metadata: { access_key_id: '', diff --git a/src/webviews/webview-side/integrations/BigQueryForm.tsx b/src/webviews/webview-side/integrations/BigQueryForm.tsx index b535e71aff..b4ae2b2087 100644 --- a/src/webviews/webview-side/integrations/BigQueryForm.tsx +++ b/src/webviews/webview-side/integrations/BigQueryForm.tsx @@ -1,15 +1,14 @@ import * as React from 'react'; import { format, getLocString } from '../react-common/locReactSide'; import { BigQueryAuthMethods, DatabaseIntegrationConfig } from '@deepnote/database-integrations'; +import { getDefaultIntegrationName } from './integrationUtils'; type BigQueryConfig = Extract; function createEmptyBigQueryConfig(params: { id: string; name?: string }): BigQueryConfig { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('big-query')).trim(), type: 'big-query', metadata: { authMethod: BigQueryAuthMethods.ServiceAccount, diff --git a/src/webviews/webview-side/integrations/ClickHouseForm.tsx b/src/webviews/webview-side/integrations/ClickHouseForm.tsx index 587f06ebd8..1c47ea5c07 100644 --- a/src/webviews/webview-side/integrations/ClickHouseForm.tsx +++ b/src/webviews/webview-side/integrations/ClickHouseForm.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; import { CaCertificateFields } from './CaCertificateFields'; +import { getDefaultIntegrationName } from './integrationUtils'; export interface IClickHouseFormProps { integrationId: string; @@ -16,11 +17,9 @@ function createEmptyClickHouseConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('clickhouse')).trim(), type: 'clickhouse', metadata: { host: '', diff --git a/src/webviews/webview-side/integrations/ConfigurationForm.tsx b/src/webviews/webview-side/integrations/ConfigurationForm.tsx index c6f9035ce6..dc79fd3d72 100644 --- a/src/webviews/webview-side/integrations/ConfigurationForm.tsx +++ b/src/webviews/webview-side/integrations/ConfigurationForm.tsx @@ -18,27 +18,7 @@ import { SpannerForm } from './SpannerForm'; import { SQLServerForm } from './SQLServerForm'; import { TrinoForm } from './TrinoForm'; import { ConfigurableDatabaseIntegrationConfig, ConfigurableDatabaseIntegrationType } from './types'; - -// Localized labels for integration types (duplicated from sqlCellStatusBarProvider.ts due to import restrictions) -const integrationTypeLabels: Record = { - alloydb: 'AlloyDB', - athena: 'Amazon Athena', - 'big-query': 'BigQuery', - clickhouse: 'ClickHouse', - databricks: 'Databricks', - dremio: 'Dremio', - mariadb: 'MariaDB', - materialize: 'Materialize', - mindsdb: 'MindsDB', - mongodb: 'MongoDB', - mysql: 'MySQL', - pgsql: 'PostgreSQL', - redshift: 'Amazon Redshift', - snowflake: 'Snowflake', - spanner: 'Google Cloud Spanner', - 'sql-server': 'SQL Server', - trino: 'Trino' -}; +import { integrationTypeLabels } from './integrationUtils'; export interface IConfigurationFormProps { integrationId: string; diff --git a/src/webviews/webview-side/integrations/DatabricksForm.tsx b/src/webviews/webview-side/integrations/DatabricksForm.tsx index e590de0864..f5e82d91d5 100644 --- a/src/webviews/webview-side/integrations/DatabricksForm.tsx +++ b/src/webviews/webview-side/integrations/DatabricksForm.tsx @@ -1,17 +1,16 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; +import { getDefaultIntegrationName } from './integrationUtils'; function createEmptyDatabricksConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('databricks')).trim(), type: 'databricks', metadata: { host: '', diff --git a/src/webviews/webview-side/integrations/DremioForm.tsx b/src/webviews/webview-side/integrations/DremioForm.tsx index 3483819afd..4daa5f41c7 100644 --- a/src/webviews/webview-side/integrations/DremioForm.tsx +++ b/src/webviews/webview-side/integrations/DremioForm.tsx @@ -1,17 +1,16 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; +import { getDefaultIntegrationName } from './integrationUtils'; function createEmptyDremioConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('dremio')).trim(), type: 'dremio', metadata: { host: '', diff --git a/src/webviews/webview-side/integrations/MariaDBForm.tsx b/src/webviews/webview-side/integrations/MariaDBForm.tsx index 0f3ded1372..dad2e20e0f 100644 --- a/src/webviews/webview-side/integrations/MariaDBForm.tsx +++ b/src/webviews/webview-side/integrations/MariaDBForm.tsx @@ -1,18 +1,17 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; import { CaCertificateFields } from './CaCertificateFields'; +import { getDefaultIntegrationName } from './integrationUtils'; function createEmptyMariaDBConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('mariadb')).trim(), type: 'mariadb', metadata: { host: '', diff --git a/src/webviews/webview-side/integrations/MaterializeForm.tsx b/src/webviews/webview-side/integrations/MaterializeForm.tsx index 031ccc5d08..647502fe7d 100644 --- a/src/webviews/webview-side/integrations/MaterializeForm.tsx +++ b/src/webviews/webview-side/integrations/MaterializeForm.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; import { CaCertificateFields } from './CaCertificateFields'; +import { getDefaultIntegrationName } from './integrationUtils'; export interface IMaterializeFormProps { integrationId: string; @@ -16,11 +17,9 @@ function createEmptyMaterializeConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('materialize')).trim(), type: 'materialize', metadata: { host: '', diff --git a/src/webviews/webview-side/integrations/MindsDBForm.tsx b/src/webviews/webview-side/integrations/MindsDBForm.tsx index 928724305c..3cf18259eb 100644 --- a/src/webviews/webview-side/integrations/MindsDBForm.tsx +++ b/src/webviews/webview-side/integrations/MindsDBForm.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; import { CaCertificateFields } from './CaCertificateFields'; +import { getDefaultIntegrationName } from './integrationUtils'; export interface IMindsDBFormProps { integrationId: string; @@ -16,11 +17,9 @@ function createEmptyMindsDBConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('mindsdb')).trim(), type: 'mindsdb', metadata: { host: '', diff --git a/src/webviews/webview-side/integrations/MongoDBForm.tsx b/src/webviews/webview-side/integrations/MongoDBForm.tsx index e169aaa6dd..2690154300 100644 --- a/src/webviews/webview-side/integrations/MongoDBForm.tsx +++ b/src/webviews/webview-side/integrations/MongoDBForm.tsx @@ -1,18 +1,17 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; import { CaCertificateFields } from './CaCertificateFields'; +import { getDefaultIntegrationName } from './integrationUtils'; function createEmptyMongoDBConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('mongodb')).trim(), type: 'mongodb', metadata: { connection_string: '' diff --git a/src/webviews/webview-side/integrations/MySQLForm.tsx b/src/webviews/webview-side/integrations/MySQLForm.tsx index 37903bcba9..ed22a16b84 100644 --- a/src/webviews/webview-side/integrations/MySQLForm.tsx +++ b/src/webviews/webview-side/integrations/MySQLForm.tsx @@ -1,18 +1,17 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; import { CaCertificateFields } from './CaCertificateFields'; +import { getDefaultIntegrationName } from './integrationUtils'; function createEmptyMySQLConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('mysql')).trim(), type: 'mysql', metadata: { host: '', diff --git a/src/webviews/webview-side/integrations/PostgresForm.tsx b/src/webviews/webview-side/integrations/PostgresForm.tsx index b6b12efab8..4cc4a6d4ae 100644 --- a/src/webviews/webview-side/integrations/PostgresForm.tsx +++ b/src/webviews/webview-side/integrations/PostgresForm.tsx @@ -1,18 +1,17 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; import { CaCertificateFields } from './CaCertificateFields'; +import { getDefaultIntegrationName } from './integrationUtils'; function createEmptyPostgresConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('pgsql')).trim(), type: 'pgsql', metadata: { host: '', diff --git a/src/webviews/webview-side/integrations/RedshiftForm.tsx b/src/webviews/webview-side/integrations/RedshiftForm.tsx index 0cd07d6910..6864e2730d 100644 --- a/src/webviews/webview-side/integrations/RedshiftForm.tsx +++ b/src/webviews/webview-side/integrations/RedshiftForm.tsx @@ -1,17 +1,16 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; import { CaCertificateFields } from './CaCertificateFields'; +import { getDefaultIntegrationName } from './integrationUtils'; type RedshiftConfig = Extract; function createEmptyRedshiftConfig(params: { id: string; name?: string }): RedshiftConfig { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('redshift')).trim(), type: 'redshift', metadata: { authMethod: 'username-and-password', diff --git a/src/webviews/webview-side/integrations/SQLServerForm.tsx b/src/webviews/webview-side/integrations/SQLServerForm.tsx index ad36470724..a21fdac394 100644 --- a/src/webviews/webview-side/integrations/SQLServerForm.tsx +++ b/src/webviews/webview-side/integrations/SQLServerForm.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; +import { getDefaultIntegrationName } from './integrationUtils'; export interface ISQLServerFormProps { integrationId: string; @@ -15,11 +16,9 @@ function createEmptySQLServerConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('sql-server')).trim(), type: 'sql-server', metadata: { host: '', diff --git a/src/webviews/webview-side/integrations/SnowflakeForm.tsx b/src/webviews/webview-side/integrations/SnowflakeForm.tsx index 637cf381ea..e13c32bf1d 100644 --- a/src/webviews/webview-side/integrations/SnowflakeForm.tsx +++ b/src/webviews/webview-side/integrations/SnowflakeForm.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig, SnowflakeAuthMethods } from '@deepnote/database-integrations'; +import { getDefaultIntegrationName } from './integrationUtils'; type SnowflakeConfig = Extract; type SnowflakeAuthMethod = SnowflakeConfig['metadata']['authMethod']; @@ -12,11 +13,9 @@ function isSupportedSnowflakeAuthMethod(authMethod: SnowflakeAuthMethod): boolea } function createEmptySnowflakeConfig(params: { id: string; name?: string }): SnowflakeConfig { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('snowflake')).trim(), type: 'snowflake', metadata: { authMethod: SnowflakeAuthMethods.Password, diff --git a/src/webviews/webview-side/integrations/SpannerForm.tsx b/src/webviews/webview-side/integrations/SpannerForm.tsx index a130ca97b2..1e1b75ddcc 100644 --- a/src/webviews/webview-side/integrations/SpannerForm.tsx +++ b/src/webviews/webview-side/integrations/SpannerForm.tsx @@ -1,16 +1,15 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; +import { getDefaultIntegrationName } from './integrationUtils'; function createEmptySpannerConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('spanner')).trim(), type: 'spanner', metadata: { instance: '', diff --git a/src/webviews/webview-side/integrations/TrinoForm.tsx b/src/webviews/webview-side/integrations/TrinoForm.tsx index 61ef215e0a..c93d100a90 100644 --- a/src/webviews/webview-side/integrations/TrinoForm.tsx +++ b/src/webviews/webview-side/integrations/TrinoForm.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; -import { format, getLocString } from '../react-common/locReactSide'; +import { getLocString } from '../react-common/locReactSide'; import { DatabaseIntegrationConfig } from '@deepnote/database-integrations'; import { SshOptionsFields } from './SshOptionsFields'; import { CaCertificateFields } from './CaCertificateFields'; +import { getDefaultIntegrationName } from './integrationUtils'; export interface ITrinoFormProps { integrationId: string; @@ -16,11 +17,9 @@ function createEmptyTrinoConfig(params: { id: string; name?: string; }): Extract { - const unnamedIntegration = getLocString('integrationsUnnamedIntegration', 'Unnamed Integration ({0})'); - return { id: params.id, - name: (params.name || format(unnamedIntegration, params.id)).trim(), + name: (params.name || getDefaultIntegrationName('trino')).trim(), type: 'trino', metadata: { host: '', diff --git a/src/webviews/webview-side/integrations/integrationUtils.ts b/src/webviews/webview-side/integrations/integrationUtils.ts new file mode 100644 index 0000000000..cffc84e05c --- /dev/null +++ b/src/webviews/webview-side/integrations/integrationUtils.ts @@ -0,0 +1,34 @@ +import { getLocString } from '../react-common/locReactSide'; +import { ConfigurableDatabaseIntegrationType } from './types'; + +// Localized labels for integration types (duplicated from sqlCellStatusBarProvider.ts due to import restrictions) +export const integrationTypeLabels: Record = { + alloydb: 'AlloyDB', + athena: 'Amazon Athena', + 'big-query': 'BigQuery', + clickhouse: 'ClickHouse', + databricks: 'Databricks', + dremio: 'Dremio', + mariadb: 'MariaDB', + materialize: 'Materialize', + mindsdb: 'MindsDB', + mongodb: 'MongoDB', + mysql: 'MySQL', + pgsql: 'PostgreSQL', + redshift: 'Amazon Redshift', + snowflake: 'Snowflake', + spanner: 'Google Cloud Spanner', + 'sql-server': 'SQL Server', + trino: 'Trino' +}; + +/** + * Get the default name for a new integration + * @param type The integration type + * @returns The default name in the format "My {type} integration" + */ +export function getDefaultIntegrationName(type: ConfigurableDatabaseIntegrationType): string { + const typeLabel = integrationTypeLabels[type] || type; + return getLocString('integrationsDefaultName', 'My {0} integration').replace('{0}', typeLabel); +} + From 81e3e7c082acad0115019f92162fbaa751702d6a Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 12:21:32 +0100 Subject: [PATCH 06/19] add integration deletion button --- src/messageTypes.ts | 4 + .../integrations/integrationWebview.ts | 4 + src/platform/common/utils/localize.ts | 6 ++ .../integrations/IntegrationItem.tsx | 16 +++- .../integrations/IntegrationList.tsx | 4 +- .../integrations/IntegrationPanel.tsx | 84 ++++++++++++++++++- .../webview-side/integrations/index.tsx | 1 + .../integrations/integrations.css | 17 ++++ 8 files changed, 130 insertions(+), 6 deletions(-) diff --git a/src/messageTypes.ts b/src/messageTypes.ts index 053539219a..21cc66b44b 100644 --- a/src/messageTypes.ts +++ b/src/messageTypes.ts @@ -173,9 +173,13 @@ export type LocalizedMessages = { integrationsConfigure: string; integrationsReconfigure: string; integrationsReset: string; + integrationsDelete: string; integrationsConfirmResetTitle: string; integrationsConfirmResetMessage: string; integrationsConfirmResetDetails: string; + integrationsConfirmDeleteTitle: string; + integrationsConfirmDeleteMessage: string; + integrationsConfirmDeleteDetails: string; integrationsConfigureTitle: string; integrationsCancel: string; integrationsSave: string; diff --git a/src/notebooks/deepnote/integrations/integrationWebview.ts b/src/notebooks/deepnote/integrations/integrationWebview.ts index 4ee04cc298..36bdd10939 100644 --- a/src/notebooks/deepnote/integrations/integrationWebview.ts +++ b/src/notebooks/deepnote/integrations/integrationWebview.ts @@ -123,9 +123,13 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { integrationsConfigure: localize.Integrations.configure, integrationsReconfigure: localize.Integrations.reconfigure, integrationsReset: localize.Integrations.reset, + integrationsDelete: localize.Integrations.deleteIntegration, integrationsConfirmResetTitle: localize.Integrations.confirmResetTitle, integrationsConfirmResetMessage: localize.Integrations.confirmResetMessage, integrationsConfirmResetDetails: localize.Integrations.confirmResetDetails, + integrationsConfirmDeleteTitle: localize.Integrations.confirmDeleteTitle, + integrationsConfirmDeleteMessage: localize.Integrations.confirmDeleteMessage, + integrationsConfirmDeleteDetails: localize.Integrations.confirmDeleteDetails, integrationsConfigureTitle: localize.Integrations.configureTitle, integrationsAddNewIntegration: localize.Integrations.addNewIntegration, integrationsDatabase: localize.Integrations.database, diff --git a/src/platform/common/utils/localize.ts b/src/platform/common/utils/localize.ts index 6e4d82c2cc..f635ca6c19 100644 --- a/src/platform/common/utils/localize.ts +++ b/src/platform/common/utils/localize.ts @@ -822,9 +822,15 @@ export namespace Integrations { export const configure = l10n.t('Configure'); export const reconfigure = l10n.t('Reconfigure'); export const reset = l10n.t('Reset'); + export const deleteIntegration = l10n.t('Delete'); export const confirmResetTitle = l10n.t('Confirm Reset'); export const confirmResetMessage = l10n.t('Are you sure you want to reset this integration configuration?'); export const confirmResetDetails = l10n.t('This will remove the stored credentials. You can reconfigure it later.'); + export const confirmDeleteTitle = l10n.t('Confirm Delete'); + export const confirmDeleteMessage = l10n.t('Are you sure you want to permanently delete this integration?'); + export const confirmDeleteDetails = l10n.t( + 'This will permanently remove the integration from your project. This action cannot be undone.' + ); export const configureTitle = l10n.t('Configure Integration: {0}'); export const cancel = l10n.t('Cancel'); export const save = l10n.t('Save'); diff --git a/src/webviews/webview-side/integrations/IntegrationItem.tsx b/src/webviews/webview-side/integrations/IntegrationItem.tsx index 20332bddb6..7ebeea89bd 100644 --- a/src/webviews/webview-side/integrations/IntegrationItem.tsx +++ b/src/webviews/webview-side/integrations/IntegrationItem.tsx @@ -5,6 +5,7 @@ import { ConfigurableDatabaseIntegrationType, IntegrationWithStatus } from './ty export interface IIntegrationItemProps { integration: IntegrationWithStatus; onConfigure: (integrationId: string) => void; + onReset: (integrationId: string) => void; onDelete: (integrationId: string) => void; } @@ -49,7 +50,7 @@ const getIntegrationTypeLabel = (type: ConfigurableDatabaseIntegrationType): str } }; -export const IntegrationItem: React.FC = ({ integration, onConfigure, onDelete }) => { +export const IntegrationItem: React.FC = ({ integration, onConfigure, onReset, onDelete }) => { const statusClass = integration.status === 'connected' ? 'status-connected' : 'status-disconnected'; const statusText = integration.status === 'connected' @@ -79,10 +80,21 @@ export const IntegrationItem: React.FC = ({ integration, {configureText} {integration.config && ( - )} + {integration.config && ( + + )}
); diff --git a/src/webviews/webview-side/integrations/IntegrationList.tsx b/src/webviews/webview-side/integrations/IntegrationList.tsx index d003ec78dd..51ffcc8a3b 100644 --- a/src/webviews/webview-side/integrations/IntegrationList.tsx +++ b/src/webviews/webview-side/integrations/IntegrationList.tsx @@ -6,10 +6,11 @@ import { IntegrationWithStatus } from './types'; export interface IIntegrationListProps { integrations: IntegrationWithStatus[]; onConfigure: (integrationId: string) => void; + onReset: (integrationId: string) => void; onDelete: (integrationId: string) => void; } -export const IntegrationList: React.FC = ({ integrations, onConfigure, onDelete }) => { +export const IntegrationList: React.FC = ({ integrations, onConfigure, onReset, onDelete }) => { if (integrations.length === 0) { return (

@@ -25,6 +26,7 @@ export const IntegrationList: React.FC = ({ integrations, key={integration.id} integration={integration} onConfigure={onConfigure} + onReset={onReset} onDelete={onDelete} /> ))} diff --git a/src/webviews/webview-side/integrations/IntegrationPanel.tsx b/src/webviews/webview-side/integrations/IntegrationPanel.tsx index 5255e89c91..fc6bda9b68 100644 --- a/src/webviews/webview-side/integrations/IntegrationPanel.tsx +++ b/src/webviews/webview-side/integrations/IntegrationPanel.tsx @@ -27,9 +27,11 @@ export const IntegrationPanel: React.FC = ({ baseTheme, ConfigurableDatabaseIntegrationType | undefined >(undefined); const [message, setMessage] = React.useState<{ type: 'success' | 'error'; text: string } | null>(null); + const [confirmReset, setConfirmReset] = React.useState(null); const [confirmDelete, setConfirmDelete] = React.useState(null); const messageTimerRef = React.useRef(null); + const confirmResetTimerRef = React.useRef(null); const confirmDeleteTimerRef = React.useRef(null); // Cleanup timers on unmount @@ -39,6 +41,10 @@ export const IntegrationPanel: React.FC = ({ baseTheme, clearTimeout(messageTimerRef.current); messageTimerRef.current = null; } + if (confirmResetTimerRef.current) { + clearTimeout(confirmResetTimerRef.current); + confirmResetTimerRef.current = null; + } if (confirmDeleteTimerRef.current) { clearTimeout(confirmDeleteTimerRef.current); confirmDeleteTimerRef.current = null; @@ -96,6 +102,41 @@ export const IntegrationPanel: React.FC = ({ baseTheme, }); }; + const handleReset = (integrationId: string) => { + // Clear any existing confirmReset timer before creating a new one + if (confirmResetTimerRef.current) { + clearTimeout(confirmResetTimerRef.current); + } + + setConfirmReset(integrationId); + }; + + const handleConfirmReset = () => { + if (confirmReset) { + // Clear the timer when user confirms + if (confirmResetTimerRef.current) { + clearTimeout(confirmResetTimerRef.current); + confirmResetTimerRef.current = null; + } + + vscodeApi.postMessage({ + type: 'delete', + integrationId: confirmReset + }); + setConfirmReset(null); + } + }; + + const handleCancelReset = () => { + // Clear the timer when user cancels + if (confirmResetTimerRef.current) { + clearTimeout(confirmResetTimerRef.current); + confirmResetTimerRef.current = null; + } + + setConfirmReset(null); + }; + const handleDelete = (integrationId: string) => { // Clear any existing confirmDelete timer before creating a new one if (confirmDeleteTimerRef.current) { @@ -165,7 +206,12 @@ export const IntegrationPanel: React.FC = ({ baseTheme, {message &&

{message.text}
} - + @@ -180,7 +226,7 @@ export const IntegrationPanel: React.FC = ({ baseTheme, /> )} - {confirmDelete && ( + {confirmReset && (
@@ -201,9 +247,41 @@ export const IntegrationPanel: React.FC = ({ baseTheme,

- + +
+
+
+ )} + + {confirmDelete && ( +
+
+
+

{getLocString('integrationsConfirmDeleteTitle', 'Confirm Delete')}

+
+
+

+ {getLocString( + 'integrationsConfirmDeleteMessage', + 'Are you sure you want to permanently delete this integration?' + )} +

+

+ {getLocString( + 'integrationsConfirmDeleteDetails', + 'This will permanently remove the integration from your project. This action cannot be undone.' + )} +

+
+
+ diff --git a/src/webviews/webview-side/integrations/index.tsx b/src/webviews/webview-side/integrations/index.tsx index c4fc3f7ece..9c912116df 100644 --- a/src/webviews/webview-side/integrations/index.tsx +++ b/src/webviews/webview-side/integrations/index.tsx @@ -6,6 +6,7 @@ import { detectBaseTheme } from '../react-common/themeDetector'; import { IntegrationPanel } from './IntegrationPanel'; import '../common/index.css'; +import '../react-common/codicon/codicon.css'; import './integrations.css'; // This special function talks to vscode from a web panel diff --git a/src/webviews/webview-side/integrations/integrations.css b/src/webviews/webview-side/integrations/integrations.css index 95456bf8f4..24fe60758d 100644 --- a/src/webviews/webview-side/integrations/integrations.css +++ b/src/webviews/webview-side/integrations/integrations.css @@ -118,6 +118,23 @@ button.primary { color: var(--vscode-button-foreground); } +button.icon-button { + padding: 6px 8px; + min-width: auto; + background-color: transparent; + border: 1px solid transparent; + color: var(--vscode-foreground); +} + +button.icon-button:hover { + background-color: var(--vscode-toolbar-hoverBackground); + border-color: var(--vscode-button-border); +} + +button.icon-button .codicon { + font-size: 16px; +} + /* Configuration form overlay */ .configuration-form-overlay { position: fixed; From 3bbc0bf52e8b368c849d11cdd944b86a0c2e707d Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 13:45:29 +0100 Subject: [PATCH 07/19] add svg logos of integrations --- .../integrations/integrationWebview.ts | 2 +- .../integrations/IntegrationTypeSelector.tsx | 60 +++++++++---- .../integrations/icons/alloydb.svg | 14 +++ .../integrations/icons/athena.svg | 82 ++++++++++++++++++ .../integrations/icons/bigquery.svg | 14 +++ .../integrations/icons/clickhouse.svg | 7 ++ .../integrations/icons/databricks.svg | 6 ++ .../integrations/icons/dremio.svg | 86 +++++++++++++++++++ .../integrations/icons/mariadb.svg | 11 +++ .../integrations/icons/materialize.svg | 12 +++ .../integrations/icons/mindsdb.svg | 14 +++ .../integrations/icons/mongodb.svg | 14 +++ .../webview-side/integrations/icons/mysql.svg | 14 +++ .../integrations/icons/postgresql.svg | 66 ++++++++++++++ .../integrations/icons/redshift.svg | 18 ++++ .../integrations/icons/snowflake.svg | 7 ++ .../integrations/icons/spanner.svg | 32 +++++++ .../integrations/icons/sql-server.svg | 49 +++++++++++ .../webview-side/integrations/icons/trino.svg | 84 ++++++++++++++++++ .../webview-side/integrations/images.d.ts | 8 ++ .../integrations/integrations.css | 13 ++- 21 files changed, 591 insertions(+), 22 deletions(-) create mode 100644 src/webviews/webview-side/integrations/icons/alloydb.svg create mode 100644 src/webviews/webview-side/integrations/icons/athena.svg create mode 100644 src/webviews/webview-side/integrations/icons/bigquery.svg create mode 100644 src/webviews/webview-side/integrations/icons/clickhouse.svg create mode 100644 src/webviews/webview-side/integrations/icons/databricks.svg create mode 100644 src/webviews/webview-side/integrations/icons/dremio.svg create mode 100644 src/webviews/webview-side/integrations/icons/mariadb.svg create mode 100644 src/webviews/webview-side/integrations/icons/materialize.svg create mode 100644 src/webviews/webview-side/integrations/icons/mindsdb.svg create mode 100644 src/webviews/webview-side/integrations/icons/mongodb.svg create mode 100644 src/webviews/webview-side/integrations/icons/mysql.svg create mode 100644 src/webviews/webview-side/integrations/icons/postgresql.svg create mode 100644 src/webviews/webview-side/integrations/icons/redshift.svg create mode 100644 src/webviews/webview-side/integrations/icons/snowflake.svg create mode 100644 src/webviews/webview-side/integrations/icons/spanner.svg create mode 100644 src/webviews/webview-side/integrations/icons/sql-server.svg create mode 100644 src/webviews/webview-side/integrations/icons/trino.svg create mode 100644 src/webviews/webview-side/integrations/images.d.ts diff --git a/src/notebooks/deepnote/integrations/integrationWebview.ts b/src/notebooks/deepnote/integrations/integrationWebview.ts index 36bdd10939..f0041f3848 100644 --- a/src/notebooks/deepnote/integrations/integrationWebview.ts +++ b/src/notebooks/deepnote/integrations/integrationWebview.ts @@ -625,7 +625,7 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { - + Deepnote Integrations diff --git a/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx b/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx index 55b7595f74..7424dfb5b4 100644 --- a/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx +++ b/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx @@ -2,6 +2,27 @@ import * as React from 'react'; import { getLocString } from '../react-common/locReactSide'; import { ConfigurableDatabaseIntegrationType } from './types'; +// Import integration logos +/* eslint-disable @typescript-eslint/no-require-imports */ +const postgresqlLogo: string = require('./icons/postgresql.svg'); +const mysqlLogo: string = require('./icons/mysql.svg'); +const mariadbLogo: string = require('./icons/mariadb.svg'); +const mongodbLogo: string = require('./icons/mongodb.svg'); +const sqlServerLogo: string = require('./icons/sql-server.svg'); +const bigqueryLogo: string = require('./icons/bigquery.svg'); +const snowflakeLogo: string = require('./icons/snowflake.svg'); +const alloydbLogo: string = require('./icons/alloydb.svg'); +const spannerLogo: string = require('./icons/spanner.svg'); +const materializeLogo: string = require('./icons/materialize.svg'); +const clickhouseLogo: string = require('./icons/clickhouse.svg'); +const athenaLogo: string = require('./icons/athena.svg'); +const redshiftLogo: string = require('./icons/redshift.svg'); +const databricksLogo: string = require('./icons/databricks.svg'); +const dremioLogo: string = require('./icons/dremio.svg'); +const mindsdbLogo: string = require('./icons/mindsdb.svg'); +const trinoLogo: string = require('./icons/trino.svg'); +/* eslint-enable @typescript-eslint/no-require-imports */ + export interface IIntegrationTypeSelectorProps { onSelectType: (type: ConfigurableDatabaseIntegrationType) => void; } @@ -16,87 +37,87 @@ const INTEGRATION_TYPES: IntegrationTypeInfo[] = [ { type: 'pgsql', label: 'PostgreSQL', - icon: '🐘' + icon: postgresqlLogo }, { type: 'mysql', label: 'MySQL', - icon: '🐬' + icon: mysqlLogo }, { type: 'mariadb', label: 'MariaDB', - icon: '🦭' + icon: mariadbLogo }, { type: 'mongodb', label: 'MongoDB', - icon: '🍃' + icon: mongodbLogo }, { type: 'sql-server', label: 'Microsoft SQL Server', - icon: '🗄️' + icon: sqlServerLogo }, { type: 'big-query', label: 'Google BigQuery', - icon: '📊' + icon: bigqueryLogo }, { type: 'snowflake', label: 'Snowflake', - icon: '❄️' + icon: snowflakeLogo }, { type: 'alloydb', label: 'Google AlloyDB', - icon: '🔷' + icon: alloydbLogo }, { type: 'spanner', label: 'Google Cloud Spanner', - icon: '🔧' + icon: spannerLogo }, { type: 'materialize', label: 'Materialize', - icon: '⚡' + icon: materializeLogo }, { type: 'clickhouse', label: 'ClickHouse', - icon: '🏠' + icon: clickhouseLogo }, { type: 'athena', label: 'Amazon Athena', - icon: '🏛️' + icon: athenaLogo }, { type: 'redshift', label: 'Amazon Redshift', - icon: '🔴' + icon: redshiftLogo }, { type: 'databricks', label: 'Databricks', - icon: '🧱' + icon: databricksLogo }, { type: 'dremio', label: 'Dremio', - icon: '🚀' + icon: dremioLogo }, { type: 'mindsdb', label: 'MindsDB', - icon: '🧠' + icon: mindsdbLogo }, { type: 'trino', label: 'Trino', - icon: '⚙️' + icon: trinoLogo } ]; @@ -112,7 +133,9 @@ export const IntegrationTypeSelector: React.FC = className="integration-type-card" onClick={() => onSelectType(integrationInfo.type)} > -
{integrationInfo.icon}
+
+ {integrationInfo.label} +
{integrationInfo.label}
{getLocString('integrationsDatabase', 'Database')} @@ -123,4 +146,3 @@ export const IntegrationTypeSelector: React.FC =
); }; - diff --git a/src/webviews/webview-side/integrations/icons/alloydb.svg b/src/webviews/webview-side/integrations/icons/alloydb.svg new file mode 100644 index 0000000000..a2e2204b1f --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/alloydb.svg @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/athena.svg b/src/webviews/webview-side/integrations/icons/athena.svg new file mode 100644 index 0000000000..5ee172b152 --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/athena.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/bigquery.svg b/src/webviews/webview-side/integrations/icons/bigquery.svg new file mode 100644 index 0000000000..dc1559bed1 --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/bigquery.svg @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/clickhouse.svg b/src/webviews/webview-side/integrations/icons/clickhouse.svg new file mode 100644 index 0000000000..0107b95513 --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/clickhouse.svg @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/databricks.svg b/src/webviews/webview-side/integrations/icons/databricks.svg new file mode 100644 index 0000000000..12d03cb7ae --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/databricks.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/dremio.svg b/src/webviews/webview-side/integrations/icons/dremio.svg new file mode 100644 index 0000000000..e38437e321 --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/dremio.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/mariadb.svg b/src/webviews/webview-side/integrations/icons/mariadb.svg new file mode 100644 index 0000000000..952028ee8e --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/mariadb.svg @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/materialize.svg b/src/webviews/webview-side/integrations/icons/materialize.svg new file mode 100644 index 0000000000..7eb2f3d150 --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/materialize.svg @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/mindsdb.svg b/src/webviews/webview-side/integrations/icons/mindsdb.svg new file mode 100644 index 0000000000..6b3a3ff917 --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/mindsdb.svg @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/mongodb.svg b/src/webviews/webview-side/integrations/icons/mongodb.svg new file mode 100644 index 0000000000..3b82753e49 --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/mongodb.svg @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/mysql.svg b/src/webviews/webview-side/integrations/icons/mysql.svg new file mode 100644 index 0000000000..3cede5dc3e --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/mysql.svg @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/postgresql.svg b/src/webviews/webview-side/integrations/icons/postgresql.svg new file mode 100644 index 0000000000..56185da3ca --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/postgresql.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/redshift.svg b/src/webviews/webview-side/integrations/icons/redshift.svg new file mode 100644 index 0000000000..2785da5c7f --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/redshift.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/snowflake.svg b/src/webviews/webview-side/integrations/icons/snowflake.svg new file mode 100644 index 0000000000..d2c8bcda6c --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/snowflake.svg @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/spanner.svg b/src/webviews/webview-side/integrations/icons/spanner.svg new file mode 100644 index 0000000000..e812ee6229 --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/spanner.svg @@ -0,0 +1,32 @@ + + + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/sql-server.svg b/src/webviews/webview-side/integrations/icons/sql-server.svg new file mode 100644 index 0000000000..913ebdd905 --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/sql-server.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/icons/trino.svg b/src/webviews/webview-side/integrations/icons/trino.svg new file mode 100644 index 0000000000..f0a6f02290 --- /dev/null +++ b/src/webviews/webview-side/integrations/icons/trino.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/webviews/webview-side/integrations/images.d.ts b/src/webviews/webview-side/integrations/images.d.ts new file mode 100644 index 0000000000..2f56b4321f --- /dev/null +++ b/src/webviews/webview-side/integrations/images.d.ts @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/* eslint-disable */ +declare module '*.svg'; +declare module '*.png'; +declare module '*.jpg'; + diff --git a/src/webviews/webview-side/integrations/integrations.css b/src/webviews/webview-side/integrations/integrations.css index 24fe60758d..c8ada338a6 100644 --- a/src/webviews/webview-side/integrations/integrations.css +++ b/src/webviews/webview-side/integrations/integrations.css @@ -325,9 +325,18 @@ form { } .integration-type-icon { - font-size: 24px; - line-height: 1; + width: 32px; + height: 32px; flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; +} + +.integration-type-icon img { + width: 32px; + height: 32px; + object-fit: contain; } .integration-type-label { From d2f4a10dd973bece211654baaa52b1edbb08d57c Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 14:10:02 +0100 Subject: [PATCH 08/19] replace integration delete icon button with labeled button --- src/webviews/webview-side/integrations/IntegrationItem.tsx | 4 ++-- src/webviews/webview-side/integrations/integrations.css | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/webviews/webview-side/integrations/IntegrationItem.tsx b/src/webviews/webview-side/integrations/IntegrationItem.tsx index 7ebeea89bd..b06fdcf5d0 100644 --- a/src/webviews/webview-side/integrations/IntegrationItem.tsx +++ b/src/webviews/webview-side/integrations/IntegrationItem.tsx @@ -87,12 +87,12 @@ export const IntegrationItem: React.FC = ({ integration, {integration.config && ( )}
diff --git a/src/webviews/webview-side/integrations/integrations.css b/src/webviews/webview-side/integrations/integrations.css index c8ada338a6..30df11599f 100644 --- a/src/webviews/webview-side/integrations/integrations.css +++ b/src/webviews/webview-side/integrations/integrations.css @@ -122,13 +122,12 @@ button.icon-button { padding: 6px 8px; min-width: auto; background-color: transparent; - border: 1px solid transparent; + border: none; color: var(--vscode-foreground); } button.icon-button:hover { background-color: var(--vscode-toolbar-hoverBackground); - border-color: var(--vscode-button-border); } button.icon-button .codicon { From 1cfffbc380a7cf33286af4d9622300f8a97454ca Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 14:14:39 +0100 Subject: [PATCH 09/19] differentiate reset and delete messages from integration webview --- .../integrations/integrationWebview.ts | 45 ++++++++++++++++--- .../integrations/IntegrationPanel.tsx | 2 +- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/notebooks/deepnote/integrations/integrationWebview.ts b/src/notebooks/deepnote/integrations/integrationWebview.ts index f0041f3848..48ffd4cecf 100644 --- a/src/notebooks/deepnote/integrations/integrationWebview.ts +++ b/src/notebooks/deepnote/integrations/integrationWebview.ts @@ -432,6 +432,11 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { await this.saveConfiguration(message.integrationId, message.config); } break; + case 'reset': + if (message.integrationId) { + await this.resetConfiguration(message.integrationId); + } + break; case 'delete': if (message.integrationId) { await this.deleteConfiguration(message.integrationId); @@ -508,9 +513,9 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { } /** - * Delete the configuration for an integration + * Reset the configuration for an integration (clears credentials but keeps the integration entry) */ - private async deleteConfiguration(integrationId: string): Promise { + private async resetConfiguration(integrationId: string): Promise { try { await this.integrationStorage.delete(integrationId); @@ -527,14 +532,44 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { await this.updateWebview(); await this.currentPanel?.webview.postMessage({ - message: l10n.t('Configuration deleted successfully'), + message: l10n.t('Configuration reset successfully'), + type: 'success' + }); + } catch (error) { + logger.error('Failed to reset integration configuration', error); + await this.currentPanel?.webview.postMessage({ + message: l10n.t( + 'Failed to reset configuration: {0}', + error instanceof Error ? error.message : 'Unknown error' + ), + type: 'error' + }); + } + } + + /** + * Delete the integration completely (removes credentials and integration entry) + */ + private async deleteConfiguration(integrationId: string): Promise { + try { + await this.integrationStorage.delete(integrationId); + + // Remove from local state + this.integrations.delete(integrationId); + + // Update the project's integrations list + await this.updateProjectIntegrationsList(); + + await this.updateWebview(); + await this.currentPanel?.webview.postMessage({ + message: l10n.t('Integration deleted successfully'), type: 'success' }); } catch (error) { - logger.error('Failed to delete integration configuration', error); + logger.error('Failed to delete integration', error); await this.currentPanel?.webview.postMessage({ message: l10n.t( - 'Failed to delete configuration: {0}', + 'Failed to delete integration: {0}', error instanceof Error ? error.message : 'Unknown error' ), type: 'error' diff --git a/src/webviews/webview-side/integrations/IntegrationPanel.tsx b/src/webviews/webview-side/integrations/IntegrationPanel.tsx index fc6bda9b68..e74181360b 100644 --- a/src/webviews/webview-side/integrations/IntegrationPanel.tsx +++ b/src/webviews/webview-side/integrations/IntegrationPanel.tsx @@ -120,7 +120,7 @@ export const IntegrationPanel: React.FC = ({ baseTheme, } vscodeApi.postMessage({ - type: 'delete', + type: 'reset', integrationId: confirmReset }); setConfirmReset(null); From 7bd736f9322062d8db6e4056b867bbea88c6e7a0 Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 14:18:27 +0100 Subject: [PATCH 10/19] unify integration type labels --- .../deepnote/sqlCellStatusBarProvider.ts | 2 +- src/platform/common/utils/localize.ts | 2 +- .../integrations/IntegrationItem.tsx | 2 +- .../integrations/IntegrationTypeSelector.tsx | 35 ++++++++++--------- .../integrations/integrationUtils.ts | 3 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/notebooks/deepnote/sqlCellStatusBarProvider.ts b/src/notebooks/deepnote/sqlCellStatusBarProvider.ts index f47b0fcf90..3008cb268d 100644 --- a/src/notebooks/deepnote/sqlCellStatusBarProvider.ts +++ b/src/notebooks/deepnote/sqlCellStatusBarProvider.ts @@ -52,7 +52,7 @@ const integrationTypeLabels: Record redshift: l10n.t('Amazon Redshift'), snowflake: l10n.t('Snowflake'), spanner: l10n.t('Google Cloud Spanner'), - 'sql-server': l10n.t('SQL Server'), + 'sql-server': l10n.t('Microsoft SQL Server'), trino: l10n.t('Trino') }; diff --git a/src/platform/common/utils/localize.ts b/src/platform/common/utils/localize.ts index f635ca6c19..ccb08e7428 100644 --- a/src/platform/common/utils/localize.ts +++ b/src/platform/common/utils/localize.ts @@ -859,7 +859,7 @@ export namespace Integrations { export const duckDBTypeLabel = l10n.t('DuckDB'); export const redshiftTypeLabel = l10n.t('Amazon Redshift'); export const spannerTypeLabel = l10n.t('Google Cloud Spanner'); - export const sqlServerTypeLabel = l10n.t('SQL Server'); + export const sqlServerTypeLabel = l10n.t('Microsoft SQL Server'); export const trinoTypeLabel = l10n.t('Trino'); // PostgreSQL form strings diff --git a/src/webviews/webview-side/integrations/IntegrationItem.tsx b/src/webviews/webview-side/integrations/IntegrationItem.tsx index b06fdcf5d0..cf7af232a8 100644 --- a/src/webviews/webview-side/integrations/IntegrationItem.tsx +++ b/src/webviews/webview-side/integrations/IntegrationItem.tsx @@ -42,7 +42,7 @@ const getIntegrationTypeLabel = (type: ConfigurableDatabaseIntegrationType): str case 'spanner': return getLocString('integrationsSpannerTypeLabel', 'Google Cloud Spanner'); case 'sql-server': - return getLocString('integrationsSQLServerTypeLabel', 'SQL Server'); + return getLocString('integrationsSQLServerTypeLabel', 'Microsoft SQL Server'); case 'trino': return getLocString('integrationsTrinoTypeLabel', 'Trino'); default: diff --git a/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx b/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx index 7424dfb5b4..2eb0f51b59 100644 --- a/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx +++ b/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { getLocString } from '../react-common/locReactSide'; import { ConfigurableDatabaseIntegrationType } from './types'; +import { integrationTypeLabels } from './integrationUtils'; // Import integration logos /* eslint-disable @typescript-eslint/no-require-imports */ @@ -36,87 +37,87 @@ interface IntegrationTypeInfo { const INTEGRATION_TYPES: IntegrationTypeInfo[] = [ { type: 'pgsql', - label: 'PostgreSQL', + label: integrationTypeLabels['pgsql'], icon: postgresqlLogo }, { type: 'mysql', - label: 'MySQL', + label: integrationTypeLabels['mysql'], icon: mysqlLogo }, { type: 'mariadb', - label: 'MariaDB', + label: integrationTypeLabels['mariadb'], icon: mariadbLogo }, { type: 'mongodb', - label: 'MongoDB', + label: integrationTypeLabels['mongodb'], icon: mongodbLogo }, { type: 'sql-server', - label: 'Microsoft SQL Server', + label: integrationTypeLabels['sql-server'], icon: sqlServerLogo }, { type: 'big-query', - label: 'Google BigQuery', + label: integrationTypeLabels['big-query'], icon: bigqueryLogo }, { type: 'snowflake', - label: 'Snowflake', + label: integrationTypeLabels['snowflake'], icon: snowflakeLogo }, { type: 'alloydb', - label: 'Google AlloyDB', + label: integrationTypeLabels['alloydb'], icon: alloydbLogo }, { type: 'spanner', - label: 'Google Cloud Spanner', + label: integrationTypeLabels['spanner'], icon: spannerLogo }, { type: 'materialize', - label: 'Materialize', + label: integrationTypeLabels['materialize'], icon: materializeLogo }, { type: 'clickhouse', - label: 'ClickHouse', + label: integrationTypeLabels['clickhouse'], icon: clickhouseLogo }, { type: 'athena', - label: 'Amazon Athena', + label: integrationTypeLabels['athena'], icon: athenaLogo }, { type: 'redshift', - label: 'Amazon Redshift', + label: integrationTypeLabels['redshift'], icon: redshiftLogo }, { type: 'databricks', - label: 'Databricks', + label: integrationTypeLabels['databricks'], icon: databricksLogo }, { type: 'dremio', - label: 'Dremio', + label: integrationTypeLabels['dremio'], icon: dremioLogo }, { type: 'mindsdb', - label: 'MindsDB', + label: integrationTypeLabels['mindsdb'], icon: mindsdbLogo }, { type: 'trino', - label: 'Trino', + label: integrationTypeLabels['trino'], icon: trinoLogo } ]; diff --git a/src/webviews/webview-side/integrations/integrationUtils.ts b/src/webviews/webview-side/integrations/integrationUtils.ts index cffc84e05c..4398b484b4 100644 --- a/src/webviews/webview-side/integrations/integrationUtils.ts +++ b/src/webviews/webview-side/integrations/integrationUtils.ts @@ -18,7 +18,7 @@ export const integrationTypeLabels: Record Date: Mon, 3 Nov 2025 14:18:47 +0100 Subject: [PATCH 11/19] format files --- src/webviews/webview-side/integrations/images.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/webviews/webview-side/integrations/images.d.ts b/src/webviews/webview-side/integrations/images.d.ts index 2f56b4321f..99bb5184ef 100644 --- a/src/webviews/webview-side/integrations/images.d.ts +++ b/src/webviews/webview-side/integrations/images.d.ts @@ -5,4 +5,3 @@ declare module '*.svg'; declare module '*.png'; declare module '*.jpg'; - From 1deacdc89cbb5019dc1d44234240fe2aa1688d24 Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 14:28:56 +0100 Subject: [PATCH 12/19] remove https from img-src csp in integration webview --- src/notebooks/deepnote/integrations/integrationWebview.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/notebooks/deepnote/integrations/integrationWebview.ts b/src/notebooks/deepnote/integrations/integrationWebview.ts index 48ffd4cecf..dcd574db2e 100644 --- a/src/notebooks/deepnote/integrations/integrationWebview.ts +++ b/src/notebooks/deepnote/integrations/integrationWebview.ts @@ -660,7 +660,7 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { - + Deepnote Integrations From b9a5ea70b8fc5e559766140dac7de2dd0591f8dc Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 14:45:43 +0100 Subject: [PATCH 13/19] fix viewbox alignment of integration type icons --- src/webviews/webview-side/integrations/icons/alloydb.svg | 4 ++-- src/webviews/webview-side/integrations/icons/athena.svg | 4 ++-- src/webviews/webview-side/integrations/icons/bigquery.svg | 4 ++-- .../webview-side/integrations/icons/clickhouse.svg | 4 ++-- .../webview-side/integrations/icons/databricks.svg | 4 ++-- src/webviews/webview-side/integrations/icons/dremio.svg | 6 +++--- src/webviews/webview-side/integrations/icons/mariadb.svg | 4 ++-- .../webview-side/integrations/icons/materialize.svg | 4 ++-- src/webviews/webview-side/integrations/icons/mindsdb.svg | 4 ++-- src/webviews/webview-side/integrations/icons/mongodb.svg | 4 ++-- src/webviews/webview-side/integrations/icons/mysql.svg | 4 ++-- .../webview-side/integrations/icons/postgresql.svg | 4 ++-- src/webviews/webview-side/integrations/icons/redshift.svg | 4 ++-- src/webviews/webview-side/integrations/icons/snowflake.svg | 4 ++-- src/webviews/webview-side/integrations/icons/spanner.svg | 4 ++-- .../webview-side/integrations/icons/sql-server.svg | 4 ++-- src/webviews/webview-side/integrations/icons/trino.svg | 4 ++-- src/webviews/webview-side/integrations/images.d.ts | 7 ------- 18 files changed, 35 insertions(+), 42 deletions(-) delete mode 100644 src/webviews/webview-side/integrations/images.d.ts diff --git a/src/webviews/webview-side/integrations/icons/alloydb.svg b/src/webviews/webview-side/integrations/icons/alloydb.svg index a2e2204b1f..798748dbc6 100644 --- a/src/webviews/webview-side/integrations/icons/alloydb.svg +++ b/src/webviews/webview-side/integrations/icons/alloydb.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/athena.svg b/src/webviews/webview-side/integrations/icons/athena.svg index 5ee172b152..2944f158d9 100644 --- a/src/webviews/webview-side/integrations/icons/athena.svg +++ b/src/webviews/webview-side/integrations/icons/athena.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/bigquery.svg b/src/webviews/webview-side/integrations/icons/bigquery.svg index dc1559bed1..2f51ef50ae 100644 --- a/src/webviews/webview-side/integrations/icons/bigquery.svg +++ b/src/webviews/webview-side/integrations/icons/bigquery.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/clickhouse.svg b/src/webviews/webview-side/integrations/icons/clickhouse.svg index 0107b95513..c4f8f373ee 100644 --- a/src/webviews/webview-side/integrations/icons/clickhouse.svg +++ b/src/webviews/webview-side/integrations/icons/clickhouse.svg @@ -1,7 +1,7 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/databricks.svg b/src/webviews/webview-side/integrations/icons/databricks.svg index 12d03cb7ae..1c160bcc08 100644 --- a/src/webviews/webview-side/integrations/icons/databricks.svg +++ b/src/webviews/webview-side/integrations/icons/databricks.svg @@ -1,6 +1,6 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/dremio.svg b/src/webviews/webview-side/integrations/icons/dremio.svg index e38437e321..41a5ae68b6 100644 --- a/src/webviews/webview-side/integrations/icons/dremio.svg +++ b/src/webviews/webview-side/integrations/icons/dremio.svg @@ -1,5 +1,5 @@ - - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/mariadb.svg b/src/webviews/webview-side/integrations/icons/mariadb.svg index 952028ee8e..5cb7528b7a 100644 --- a/src/webviews/webview-side/integrations/icons/mariadb.svg +++ b/src/webviews/webview-side/integrations/icons/mariadb.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/materialize.svg b/src/webviews/webview-side/integrations/icons/materialize.svg index 7eb2f3d150..7a0c347a69 100644 --- a/src/webviews/webview-side/integrations/icons/materialize.svg +++ b/src/webviews/webview-side/integrations/icons/materialize.svg @@ -1,4 +1,4 @@ - + @@ -9,4 +9,4 @@ /> - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/mindsdb.svg b/src/webviews/webview-side/integrations/icons/mindsdb.svg index 6b3a3ff917..c61fa0cce2 100644 --- a/src/webviews/webview-side/integrations/icons/mindsdb.svg +++ b/src/webviews/webview-side/integrations/icons/mindsdb.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/mongodb.svg b/src/webviews/webview-side/integrations/icons/mongodb.svg index 3b82753e49..a2bf6cbe6e 100644 --- a/src/webviews/webview-side/integrations/icons/mongodb.svg +++ b/src/webviews/webview-side/integrations/icons/mongodb.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/mysql.svg b/src/webviews/webview-side/integrations/icons/mysql.svg index 3cede5dc3e..557f875062 100644 --- a/src/webviews/webview-side/integrations/icons/mysql.svg +++ b/src/webviews/webview-side/integrations/icons/mysql.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/postgresql.svg b/src/webviews/webview-side/integrations/icons/postgresql.svg index 56185da3ca..8acf80b33e 100644 --- a/src/webviews/webview-side/integrations/icons/postgresql.svg +++ b/src/webviews/webview-side/integrations/icons/postgresql.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/redshift.svg b/src/webviews/webview-side/integrations/icons/redshift.svg index 2785da5c7f..7b15f14a61 100644 --- a/src/webviews/webview-side/integrations/icons/redshift.svg +++ b/src/webviews/webview-side/integrations/icons/redshift.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/snowflake.svg b/src/webviews/webview-side/integrations/icons/snowflake.svg index d2c8bcda6c..70f32b6628 100644 --- a/src/webviews/webview-side/integrations/icons/snowflake.svg +++ b/src/webviews/webview-side/integrations/icons/snowflake.svg @@ -1,7 +1,7 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/spanner.svg b/src/webviews/webview-side/integrations/icons/spanner.svg index e812ee6229..ab955453a5 100644 --- a/src/webviews/webview-side/integrations/icons/spanner.svg +++ b/src/webviews/webview-side/integrations/icons/spanner.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/sql-server.svg b/src/webviews/webview-side/integrations/icons/sql-server.svg index 913ebdd905..ba7b395bbb 100644 --- a/src/webviews/webview-side/integrations/icons/sql-server.svg +++ b/src/webviews/webview-side/integrations/icons/sql-server.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/icons/trino.svg b/src/webviews/webview-side/integrations/icons/trino.svg index f0a6f02290..2dba4da6c1 100644 --- a/src/webviews/webview-side/integrations/icons/trino.svg +++ b/src/webviews/webview-side/integrations/icons/trino.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/webviews/webview-side/integrations/images.d.ts b/src/webviews/webview-side/integrations/images.d.ts deleted file mode 100644 index 99bb5184ef..0000000000 --- a/src/webviews/webview-side/integrations/images.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -/* eslint-disable */ -declare module '*.svg'; -declare module '*.png'; -declare module '*.jpg'; From beb83af7ebd4988744ad0e849c5db79c2a31b3e9 Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 14:51:55 +0100 Subject: [PATCH 14/19] split warehouses and databases --- src/messageTypes.ts | 2 + .../integrations/integrationWebview.ts | 2 + src/platform/common/utils/localize.ts | 2 + .../integrations/IntegrationTypeSelector.tsx | 153 ++++++++++-------- .../integrations/integrations.css | 20 ++- 5 files changed, 115 insertions(+), 64 deletions(-) diff --git a/src/messageTypes.ts b/src/messageTypes.ts index 21cc66b44b..9cd884dc5b 100644 --- a/src/messageTypes.ts +++ b/src/messageTypes.ts @@ -185,6 +185,8 @@ export type LocalizedMessages = { integrationsSave: string; integrationsAddNewIntegration: string; integrationsDatabase: string; + integrationsDataWarehousesLakes: string; + integrationsDatabases: string; // Integration type labels integrationsPostgresTypeLabel: string; integrationsBigQueryTypeLabel: string; diff --git a/src/notebooks/deepnote/integrations/integrationWebview.ts b/src/notebooks/deepnote/integrations/integrationWebview.ts index dcd574db2e..1ebef81e76 100644 --- a/src/notebooks/deepnote/integrations/integrationWebview.ts +++ b/src/notebooks/deepnote/integrations/integrationWebview.ts @@ -133,6 +133,8 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider { integrationsConfigureTitle: localize.Integrations.configureTitle, integrationsAddNewIntegration: localize.Integrations.addNewIntegration, integrationsDatabase: localize.Integrations.database, + integrationsDataWarehousesLakes: localize.Integrations.dataWarehousesLakes, + integrationsDatabases: localize.Integrations.databases, integrationsPostgresTypeLabel: localize.Integrations.postgresTypeLabel, integrationsBigQueryTypeLabel: localize.Integrations.bigQueryTypeLabel, integrationsSnowflakeTypeLabel: localize.Integrations.snowflakeTypeLabel, diff --git a/src/platform/common/utils/localize.ts b/src/platform/common/utils/localize.ts index ccb08e7428..9b96c28dd4 100644 --- a/src/platform/common/utils/localize.ts +++ b/src/platform/common/utils/localize.ts @@ -836,6 +836,8 @@ export namespace Integrations { export const save = l10n.t('Save'); export const addNewIntegration = l10n.t('Add New Integration'); export const database = l10n.t('Database'); + export const dataWarehousesLakes = l10n.t('Data Warehouses & Lakes'); + export const databases = l10n.t('Databases'); export const requiredField = l10n.t('*'); export const optionalField = l10n.t('(optional)'); export const unnamedIntegration = (id: string) => l10n.t('Unnamed Integration ({0})', id); diff --git a/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx b/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx index 2eb0f51b59..a6476530c6 100644 --- a/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx +++ b/src/webviews/webview-side/integrations/IntegrationTypeSelector.tsx @@ -34,7 +34,57 @@ interface IntegrationTypeInfo { icon: string; } -const INTEGRATION_TYPES: IntegrationTypeInfo[] = [ +// Data Warehouses & Lakes +const WAREHOUSE_INTEGRATION_TYPES: IntegrationTypeInfo[] = [ + { + type: 'clickhouse', + label: integrationTypeLabels['clickhouse'], + icon: clickhouseLogo + }, + { + type: 'redshift', + label: integrationTypeLabels['redshift'], + icon: redshiftLogo + }, + { + type: 'athena', + label: integrationTypeLabels['athena'], + icon: athenaLogo + }, + { + type: 'big-query', + label: integrationTypeLabels['big-query'], + icon: bigqueryLogo + }, + { + type: 'snowflake', + label: integrationTypeLabels['snowflake'], + icon: snowflakeLogo + }, + { + type: 'databricks', + label: integrationTypeLabels['databricks'], + icon: databricksLogo + }, + { + type: 'dremio', + label: integrationTypeLabels['dremio'], + icon: dremioLogo + }, + { + type: 'trino', + label: integrationTypeLabels['trino'], + icon: trinoLogo + } +]; + +// Databases +const DATABASE_INTEGRATION_TYPES: IntegrationTypeInfo[] = [ + { + type: 'mongodb', + label: integrationTypeLabels['mongodb'], + icon: mongodbLogo + }, { type: 'pgsql', label: integrationTypeLabels['pgsql'], @@ -50,26 +100,11 @@ const INTEGRATION_TYPES: IntegrationTypeInfo[] = [ label: integrationTypeLabels['mariadb'], icon: mariadbLogo }, - { - type: 'mongodb', - label: integrationTypeLabels['mongodb'], - icon: mongodbLogo - }, { type: 'sql-server', label: integrationTypeLabels['sql-server'], icon: sqlServerLogo }, - { - type: 'big-query', - label: integrationTypeLabels['big-query'], - icon: bigqueryLogo - }, - { - type: 'snowflake', - label: integrationTypeLabels['snowflake'], - icon: snowflakeLogo - }, { type: 'alloydb', label: integrationTypeLabels['alloydb'], @@ -85,40 +120,10 @@ const INTEGRATION_TYPES: IntegrationTypeInfo[] = [ label: integrationTypeLabels['materialize'], icon: materializeLogo }, - { - type: 'clickhouse', - label: integrationTypeLabels['clickhouse'], - icon: clickhouseLogo - }, - { - type: 'athena', - label: integrationTypeLabels['athena'], - icon: athenaLogo - }, - { - type: 'redshift', - label: integrationTypeLabels['redshift'], - icon: redshiftLogo - }, - { - type: 'databricks', - label: integrationTypeLabels['databricks'], - icon: databricksLogo - }, - { - type: 'dremio', - label: integrationTypeLabels['dremio'], - icon: dremioLogo - }, { type: 'mindsdb', label: integrationTypeLabels['mindsdb'], icon: mindsdbLogo - }, - { - type: 'trino', - label: integrationTypeLabels['trino'], - icon: trinoLogo } ]; @@ -126,23 +131,45 @@ export const IntegrationTypeSelector: React.FC = return (

{getLocString('integrationsAddNewIntegration', 'Add New Integration')}

-
- {INTEGRATION_TYPES.map((integrationInfo) => ( - - ))} + +
+

+ {getLocString('integrationsDataWarehousesLakes', 'Data Warehouses & Lakes')} +

+
+ {WAREHOUSE_INTEGRATION_TYPES.map((integrationInfo) => ( + + ))} +
+
+ +
+

{getLocString('integrationsDatabases', 'Databases')}

+
+ {DATABASE_INTEGRATION_TYPES.map((integrationInfo) => ( + + ))} +
); diff --git a/src/webviews/webview-side/integrations/integrations.css b/src/webviews/webview-side/integrations/integrations.css index 30df11599f..af620dbbd9 100644 --- a/src/webviews/webview-side/integrations/integrations.css +++ b/src/webviews/webview-side/integrations/integrations.css @@ -291,11 +291,29 @@ form { .integration-type-selector h2 { margin-top: 0; - margin-bottom: 16px; + margin-bottom: 24px; font-size: 1.2em; font-weight: 600; } +.integration-type-section { + margin-bottom: 32px; +} + +.integration-type-section:last-child { + margin-bottom: 0; +} + +.integration-type-section-title { + margin-top: 0; + margin-bottom: 12px; + font-size: 0.95em; + font-weight: 600; + color: var(--vscode-descriptionForeground); + text-transform: uppercase; + letter-spacing: 0.5px; +} + .integration-type-grid { display: grid; grid-template-columns: repeat(3, 1fr); From 6dcb177787ff9b3fcc68aa10abbf934d83b69bba Mon Sep 17 00:00:00 2001 From: jankuca Date: Mon, 3 Nov 2025 15:05:22 +0100 Subject: [PATCH 15/19] add icons to existing integration items --- .../integrations/IntegrationItem.tsx | 19 +++++-- .../integrations/IntegrationPanel.tsx | 4 +- .../integrations/IntegrationTypeSelector.tsx | 57 ++++++------------- .../integrations/integrationUtils.ts | 42 ++++++++++++++ .../integrations/integrations.css | 35 +++++++++++- 5 files changed, 112 insertions(+), 45 deletions(-) diff --git a/src/webviews/webview-side/integrations/IntegrationItem.tsx b/src/webviews/webview-side/integrations/IntegrationItem.tsx index cf7af232a8..0474ea63e3 100644 --- a/src/webviews/webview-side/integrations/IntegrationItem.tsx +++ b/src/webviews/webview-side/integrations/IntegrationItem.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { getLocString } from '../react-common/locReactSide'; import { ConfigurableDatabaseIntegrationType, IntegrationWithStatus } from './types'; +import { integrationTypeIcons } from './integrationUtils'; export interface IIntegrationItemProps { integration: IntegrationWithStatus; @@ -66,14 +67,24 @@ export const IntegrationItem: React.FC = ({ integration, // Get the type: prefer config type, then integration type from project const type = integration.config?.type || integration.integrationType; - // Build display name with type - const displayName = type ? `${name} (${getIntegrationTypeLabel(type)})` : name; + // Get the type label and icon + const typeLabel = type ? getIntegrationTypeLabel(type) : undefined; + const typeIcon = type ? integrationTypeIcons[type] : undefined; return (
+ {typeIcon && ( +
+ {typeLabel +
+ )}
-
{displayName}
-
{statusText}
+
{name}
+
+ {typeLabel && {typeLabel}} + {typeLabel && } + {statusText} +