Skip to content

Commit fff1e71

Browse files
committed
add new integration creating cards to integration management
1 parent 8ec95e5 commit fff1e71

File tree

6 files changed

+219
-0
lines changed

6 files changed

+219
-0
lines changed

src/messageTypes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ export type LocalizedMessages = {
179179
integrationsConfigureTitle: string;
180180
integrationsCancel: string;
181181
integrationsSave: string;
182+
integrationsAddNewIntegration: string;
183+
integrationsDatabase: string;
182184
// Integration type labels
183185
integrationsPostgresTypeLabel: string;
184186
integrationsBigQueryTypeLabel: string;

src/notebooks/deepnote/integrations/integrationWebview.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider {
127127
integrationsConfirmResetMessage: localize.Integrations.confirmResetMessage,
128128
integrationsConfirmResetDetails: localize.Integrations.confirmResetDetails,
129129
integrationsConfigureTitle: localize.Integrations.configureTitle,
130+
integrationsAddNewIntegration: localize.Integrations.addNewIntegration,
131+
integrationsDatabase: localize.Integrations.database,
130132
integrationsPostgresTypeLabel: localize.Integrations.postgresTypeLabel,
131133
integrationsBigQueryTypeLabel: localize.Integrations.bigQueryTypeLabel,
132134
integrationsSnowflakeTypeLabel: localize.Integrations.snowflakeTypeLabel,
@@ -464,9 +466,20 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider {
464466
// Update local state
465467
const integration = this.integrations.get(integrationId);
466468
if (integration) {
469+
// Existing integration - update it
467470
integration.config = config;
468471
integration.status = IntegrationStatus.Connected;
472+
integration.integrationName = config.name;
473+
integration.integrationType = config.type;
469474
this.integrations.set(integrationId, integration);
475+
} else {
476+
// New integration - add it to the map
477+
this.integrations.set(integrationId, {
478+
config,
479+
status: IntegrationStatus.Connected,
480+
integrationName: config.name,
481+
integrationType: config.type
482+
});
470483
}
471484

472485
// Update the project's integrations list

src/platform/common/utils/localize.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,8 @@ export namespace Integrations {
828828
export const configureTitle = l10n.t('Configure Integration: {0}');
829829
export const cancel = l10n.t('Cancel');
830830
export const save = l10n.t('Save');
831+
export const addNewIntegration = l10n.t('Add New Integration');
832+
export const database = l10n.t('Database');
831833
export const requiredField = l10n.t('*');
832834
export const optionalField = l10n.t('(optional)');
833835
export const unnamedIntegration = (id: string) => l10n.t('Unnamed Integration ({0})', id);

src/webviews/webview-side/integrations/IntegrationPanel.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from 'react';
22
import { IVsCodeApi } from '../react-common/postOffice';
33
import { getLocString, storeLocStrings } from '../react-common/locReactSide';
44
import { IntegrationList } from './IntegrationList';
5+
import { IntegrationTypeSelector } from './IntegrationTypeSelector';
56
import { ConfigurationForm } from './ConfigurationForm';
67
import {
78
ConfigurableDatabaseIntegrationConfig,
@@ -143,6 +144,19 @@ export const IntegrationPanel: React.FC<IIntegrationPanelProps> = ({ baseTheme,
143144
const handleCancel = () => {
144145
setSelectedIntegrationId(null);
145146
setSelectedConfig(null);
147+
setSelectedIntegrationDefaultName(undefined);
148+
setSelectedIntegrationType(undefined);
149+
};
150+
151+
const handleSelectIntegrationType = (type: ConfigurableDatabaseIntegrationType) => {
152+
// Generate a new UUID for the integration
153+
const newId = crypto.randomUUID();
154+
155+
// Set up the form for creating a new integration
156+
setSelectedIntegrationId(newId);
157+
setSelectedConfig(null);
158+
setSelectedIntegrationDefaultName(undefined);
159+
setSelectedIntegrationType(type);
146160
};
147161

148162
return (
@@ -153,6 +167,8 @@ export const IntegrationPanel: React.FC<IIntegrationPanelProps> = ({ baseTheme,
153167

154168
<IntegrationList integrations={integrations} onConfigure={handleConfigure} onDelete={handleDelete} />
155169

170+
<IntegrationTypeSelector onSelectType={handleSelectIntegrationType} />
171+
156172
{selectedIntegrationId && selectedIntegrationType && (
157173
<ConfigurationForm
158174
integrationId={selectedIntegrationId}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import * as React from 'react';
2+
import { getLocString } from '../react-common/locReactSide';
3+
import { ConfigurableDatabaseIntegrationType } from './types';
4+
5+
export interface IIntegrationTypeSelectorProps {
6+
onSelectType: (type: ConfigurableDatabaseIntegrationType) => void;
7+
}
8+
9+
interface IntegrationTypeInfo {
10+
type: ConfigurableDatabaseIntegrationType;
11+
label: string;
12+
icon: string;
13+
}
14+
15+
const INTEGRATION_TYPES: IntegrationTypeInfo[] = [
16+
{
17+
type: 'pgsql',
18+
label: 'PostgreSQL',
19+
icon: '🐘'
20+
},
21+
{
22+
type: 'mysql',
23+
label: 'MySQL',
24+
icon: '🐬'
25+
},
26+
{
27+
type: 'mariadb',
28+
label: 'MariaDB',
29+
icon: '🦭'
30+
},
31+
{
32+
type: 'mongodb',
33+
label: 'MongoDB',
34+
icon: '🍃'
35+
},
36+
{
37+
type: 'sql-server',
38+
label: 'Microsoft SQL Server',
39+
icon: '🗄️'
40+
},
41+
{
42+
type: 'big-query',
43+
label: 'Google BigQuery',
44+
icon: '📊'
45+
},
46+
{
47+
type: 'snowflake',
48+
label: 'Snowflake',
49+
icon: '❄️'
50+
},
51+
{
52+
type: 'alloydb',
53+
label: 'Google AlloyDB',
54+
icon: '🔷'
55+
},
56+
{
57+
type: 'spanner',
58+
label: 'Google Cloud Spanner',
59+
icon: '🔧'
60+
},
61+
{
62+
type: 'materialize',
63+
label: 'Materialize',
64+
icon: '⚡'
65+
},
66+
{
67+
type: 'clickhouse',
68+
label: 'ClickHouse',
69+
icon: '🏠'
70+
},
71+
{
72+
type: 'athena',
73+
label: 'Amazon Athena',
74+
icon: '🏛️'
75+
},
76+
{
77+
type: 'redshift',
78+
label: 'Amazon Redshift',
79+
icon: '🔴'
80+
},
81+
{
82+
type: 'databricks',
83+
label: 'Databricks',
84+
icon: '🧱'
85+
},
86+
{
87+
type: 'dremio',
88+
label: 'Dremio',
89+
icon: '🚀'
90+
},
91+
{
92+
type: 'mindsdb',
93+
label: 'MindsDB',
94+
icon: '🧠'
95+
},
96+
{
97+
type: 'trino',
98+
label: 'Trino',
99+
icon: '⚙️'
100+
}
101+
];
102+
103+
export const IntegrationTypeSelector: React.FC<IIntegrationTypeSelectorProps> = ({ onSelectType }) => {
104+
return (
105+
<div className="integration-type-selector">
106+
<h2>{getLocString('integrationsAddNewIntegration', 'Add New Integration')}</h2>
107+
<div className="integration-type-grid">
108+
{INTEGRATION_TYPES.map((integrationInfo) => (
109+
<button
110+
key={integrationInfo.type}
111+
type="button"
112+
className="integration-type-card"
113+
onClick={() => onSelectType(integrationInfo.type)}
114+
>
115+
<div className="integration-type-icon">{integrationInfo.icon}</div>
116+
<div className="integration-type-label">{integrationInfo.label}</div>
117+
<div className="integration-type-category">
118+
{getLocString('integrationsDatabase', 'Database')}
119+
</div>
120+
</button>
121+
))}
122+
</div>
123+
</div>
124+
);
125+
};
126+

src/webviews/webview-side/integrations/integrations.css

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
.integration-panel {
22
padding: 20px;
3+
max-width: 640px;
4+
margin: 0 auto;
35
color: var(--vscode-foreground);
46
font-family: var(--vscode-font-family);
57
font-size: var(--vscode-font-size);
@@ -263,3 +265,61 @@ form {
263265
.form-actions button {
264266
flex: 1;
265267
}
268+
269+
/* Integration type selector */
270+
.integration-type-selector {
271+
margin-top: 32px;
272+
padding-top: 32px;
273+
border-top: 1px solid var(--vscode-panel-border);
274+
}
275+
276+
.integration-type-selector h2 {
277+
margin-top: 0;
278+
margin-bottom: 16px;
279+
font-size: 1.2em;
280+
font-weight: 600;
281+
}
282+
283+
.integration-type-grid {
284+
display: grid;
285+
grid-template-columns: repeat(3, 1fr);
286+
gap: 10px;
287+
}
288+
289+
.integration-type-card {
290+
display: flex;
291+
flex-direction: row;
292+
align-items: center;
293+
gap: 12px;
294+
padding: 12px 16px;
295+
background-color: var(--vscode-editor-background);
296+
border: 1px solid var(--vscode-panel-border);
297+
border-radius: 4px;
298+
cursor: pointer;
299+
text-align: left;
300+
transition:
301+
background-color 0.2s,
302+
border-color 0.2s;
303+
}
304+
305+
.integration-type-card:hover {
306+
background-color: var(--vscode-toolbar-hoverBackground);
307+
border-color: var(--vscode-focusBorder);
308+
}
309+
310+
.integration-type-icon {
311+
font-size: 24px;
312+
line-height: 1;
313+
flex-shrink: 0;
314+
}
315+
316+
.integration-type-label {
317+
font-weight: 400;
318+
font-size: 13px;
319+
flex: 1;
320+
color: var(--vscode-foreground);
321+
}
322+
323+
.integration-type-category {
324+
display: none;
325+
}

0 commit comments

Comments
 (0)