Skip to content

Commit 81e3e7c

Browse files
committed
add integration deletion button
1 parent 21cf5d2 commit 81e3e7c

File tree

8 files changed

+130
-6
lines changed

8 files changed

+130
-6
lines changed

src/messageTypes.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,13 @@ export type LocalizedMessages = {
173173
integrationsConfigure: string;
174174
integrationsReconfigure: string;
175175
integrationsReset: string;
176+
integrationsDelete: string;
176177
integrationsConfirmResetTitle: string;
177178
integrationsConfirmResetMessage: string;
178179
integrationsConfirmResetDetails: string;
180+
integrationsConfirmDeleteTitle: string;
181+
integrationsConfirmDeleteMessage: string;
182+
integrationsConfirmDeleteDetails: string;
179183
integrationsConfigureTitle: string;
180184
integrationsCancel: string;
181185
integrationsSave: string;

src/notebooks/deepnote/integrations/integrationWebview.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,13 @@ export class IntegrationWebviewProvider implements IIntegrationWebviewProvider {
123123
integrationsConfigure: localize.Integrations.configure,
124124
integrationsReconfigure: localize.Integrations.reconfigure,
125125
integrationsReset: localize.Integrations.reset,
126+
integrationsDelete: localize.Integrations.deleteIntegration,
126127
integrationsConfirmResetTitle: localize.Integrations.confirmResetTitle,
127128
integrationsConfirmResetMessage: localize.Integrations.confirmResetMessage,
128129
integrationsConfirmResetDetails: localize.Integrations.confirmResetDetails,
130+
integrationsConfirmDeleteTitle: localize.Integrations.confirmDeleteTitle,
131+
integrationsConfirmDeleteMessage: localize.Integrations.confirmDeleteMessage,
132+
integrationsConfirmDeleteDetails: localize.Integrations.confirmDeleteDetails,
129133
integrationsConfigureTitle: localize.Integrations.configureTitle,
130134
integrationsAddNewIntegration: localize.Integrations.addNewIntegration,
131135
integrationsDatabase: localize.Integrations.database,

src/platform/common/utils/localize.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,9 +822,15 @@ export namespace Integrations {
822822
export const configure = l10n.t('Configure');
823823
export const reconfigure = l10n.t('Reconfigure');
824824
export const reset = l10n.t('Reset');
825+
export const deleteIntegration = l10n.t('Delete');
825826
export const confirmResetTitle = l10n.t('Confirm Reset');
826827
export const confirmResetMessage = l10n.t('Are you sure you want to reset this integration configuration?');
827828
export const confirmResetDetails = l10n.t('This will remove the stored credentials. You can reconfigure it later.');
829+
export const confirmDeleteTitle = l10n.t('Confirm Delete');
830+
export const confirmDeleteMessage = l10n.t('Are you sure you want to permanently delete this integration?');
831+
export const confirmDeleteDetails = l10n.t(
832+
'This will permanently remove the integration from your project. This action cannot be undone.'
833+
);
828834
export const configureTitle = l10n.t('Configure Integration: {0}');
829835
export const cancel = l10n.t('Cancel');
830836
export const save = l10n.t('Save');

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ConfigurableDatabaseIntegrationType, IntegrationWithStatus } from './ty
55
export interface IIntegrationItemProps {
66
integration: IntegrationWithStatus;
77
onConfigure: (integrationId: string) => void;
8+
onReset: (integrationId: string) => void;
89
onDelete: (integrationId: string) => void;
910
}
1011

@@ -49,7 +50,7 @@ const getIntegrationTypeLabel = (type: ConfigurableDatabaseIntegrationType): str
4950
}
5051
};
5152

52-
export const IntegrationItem: React.FC<IIntegrationItemProps> = ({ integration, onConfigure, onDelete }) => {
53+
export const IntegrationItem: React.FC<IIntegrationItemProps> = ({ integration, onConfigure, onReset, onDelete }) => {
5354
const statusClass = integration.status === 'connected' ? 'status-connected' : 'status-disconnected';
5455
const statusText =
5556
integration.status === 'connected'
@@ -79,10 +80,21 @@ export const IntegrationItem: React.FC<IIntegrationItemProps> = ({ integration,
7980
{configureText}
8081
</button>
8182
{integration.config && (
82-
<button type="button" className="secondary" onClick={() => onDelete(integration.id)}>
83+
<button type="button" className="secondary" onClick={() => onReset(integration.id)}>
8384
{getLocString('integrationsReset', 'Reset')}
8485
</button>
8586
)}
87+
{integration.config && (
88+
<button
89+
type="button"
90+
className="icon-button"
91+
onClick={() => onDelete(integration.id)}
92+
title={getLocString('integrationsDelete', 'Delete')}
93+
aria-label={getLocString('integrationsDelete', 'Delete')}
94+
>
95+
<span className="codicon codicon-trash" />
96+
</button>
97+
)}
8698
</div>
8799
</div>
88100
);

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import { IntegrationWithStatus } from './types';
66
export interface IIntegrationListProps {
77
integrations: IntegrationWithStatus[];
88
onConfigure: (integrationId: string) => void;
9+
onReset: (integrationId: string) => void;
910
onDelete: (integrationId: string) => void;
1011
}
1112

12-
export const IntegrationList: React.FC<IIntegrationListProps> = ({ integrations, onConfigure, onDelete }) => {
13+
export const IntegrationList: React.FC<IIntegrationListProps> = ({ integrations, onConfigure, onReset, onDelete }) => {
1314
if (integrations.length === 0) {
1415
return (
1516
<p className="no-integrations">
@@ -25,6 +26,7 @@ export const IntegrationList: React.FC<IIntegrationListProps> = ({ integrations,
2526
key={integration.id}
2627
integration={integration}
2728
onConfigure={onConfigure}
29+
onReset={onReset}
2830
onDelete={onDelete}
2931
/>
3032
))}

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

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ export const IntegrationPanel: React.FC<IIntegrationPanelProps> = ({ baseTheme,
2727
ConfigurableDatabaseIntegrationType | undefined
2828
>(undefined);
2929
const [message, setMessage] = React.useState<{ type: 'success' | 'error'; text: string } | null>(null);
30+
const [confirmReset, setConfirmReset] = React.useState<string | null>(null);
3031
const [confirmDelete, setConfirmDelete] = React.useState<string | null>(null);
3132

3233
const messageTimerRef = React.useRef<NodeJS.Timeout | null>(null);
34+
const confirmResetTimerRef = React.useRef<NodeJS.Timeout | null>(null);
3335
const confirmDeleteTimerRef = React.useRef<NodeJS.Timeout | null>(null);
3436

3537
// Cleanup timers on unmount
@@ -39,6 +41,10 @@ export const IntegrationPanel: React.FC<IIntegrationPanelProps> = ({ baseTheme,
3941
clearTimeout(messageTimerRef.current);
4042
messageTimerRef.current = null;
4143
}
44+
if (confirmResetTimerRef.current) {
45+
clearTimeout(confirmResetTimerRef.current);
46+
confirmResetTimerRef.current = null;
47+
}
4248
if (confirmDeleteTimerRef.current) {
4349
clearTimeout(confirmDeleteTimerRef.current);
4450
confirmDeleteTimerRef.current = null;
@@ -96,6 +102,41 @@ export const IntegrationPanel: React.FC<IIntegrationPanelProps> = ({ baseTheme,
96102
});
97103
};
98104

105+
const handleReset = (integrationId: string) => {
106+
// Clear any existing confirmReset timer before creating a new one
107+
if (confirmResetTimerRef.current) {
108+
clearTimeout(confirmResetTimerRef.current);
109+
}
110+
111+
setConfirmReset(integrationId);
112+
};
113+
114+
const handleConfirmReset = () => {
115+
if (confirmReset) {
116+
// Clear the timer when user confirms
117+
if (confirmResetTimerRef.current) {
118+
clearTimeout(confirmResetTimerRef.current);
119+
confirmResetTimerRef.current = null;
120+
}
121+
122+
vscodeApi.postMessage({
123+
type: 'delete',
124+
integrationId: confirmReset
125+
});
126+
setConfirmReset(null);
127+
}
128+
};
129+
130+
const handleCancelReset = () => {
131+
// Clear the timer when user cancels
132+
if (confirmResetTimerRef.current) {
133+
clearTimeout(confirmResetTimerRef.current);
134+
confirmResetTimerRef.current = null;
135+
}
136+
137+
setConfirmReset(null);
138+
};
139+
99140
const handleDelete = (integrationId: string) => {
100141
// Clear any existing confirmDelete timer before creating a new one
101142
if (confirmDeleteTimerRef.current) {
@@ -165,7 +206,12 @@ export const IntegrationPanel: React.FC<IIntegrationPanelProps> = ({ baseTheme,
165206

166207
{message && <div className={`message message-${message.type}`}>{message.text}</div>}
167208

168-
<IntegrationList integrations={integrations} onConfigure={handleConfigure} onDelete={handleDelete} />
209+
<IntegrationList
210+
integrations={integrations}
211+
onConfigure={handleConfigure}
212+
onReset={handleReset}
213+
onDelete={handleDelete}
214+
/>
169215

170216
<IntegrationTypeSelector onSelectType={handleSelectIntegrationType} />
171217

@@ -180,7 +226,7 @@ export const IntegrationPanel: React.FC<IIntegrationPanelProps> = ({ baseTheme,
180226
/>
181227
)}
182228

183-
{confirmDelete && (
229+
{confirmReset && (
184230
<div className="configuration-form-overlay">
185231
<div className="configuration-form-container" style={{ maxWidth: '400px' }}>
186232
<div className="configuration-form-header">
@@ -201,9 +247,41 @@ export const IntegrationPanel: React.FC<IIntegrationPanelProps> = ({ baseTheme,
201247
</p>
202248
</div>
203249
<div className="form-actions">
204-
<button type="button" className="primary" onClick={handleConfirmDelete}>
250+
<button type="button" className="primary" onClick={handleConfirmReset}>
205251
{getLocString('integrationsReset', 'Reset')}
206252
</button>
253+
<button type="button" className="secondary" onClick={handleCancelReset}>
254+
{getLocString('integrationsCancel', 'Cancel')}
255+
</button>
256+
</div>
257+
</div>
258+
</div>
259+
)}
260+
261+
{confirmDelete && (
262+
<div className="configuration-form-overlay">
263+
<div className="configuration-form-container" style={{ maxWidth: '400px' }}>
264+
<div className="configuration-form-header">
265+
<h2>{getLocString('integrationsConfirmDeleteTitle', 'Confirm Delete')}</h2>
266+
</div>
267+
<div className="configuration-form-body">
268+
<p>
269+
{getLocString(
270+
'integrationsConfirmDeleteMessage',
271+
'Are you sure you want to permanently delete this integration?'
272+
)}
273+
</p>
274+
<p style={{ marginTop: '10px', fontSize: '0.9em', opacity: 0.8 }}>
275+
{getLocString(
276+
'integrationsConfirmDeleteDetails',
277+
'This will permanently remove the integration from your project. This action cannot be undone.'
278+
)}
279+
</p>
280+
</div>
281+
<div className="form-actions">
282+
<button type="button" className="primary" onClick={handleConfirmDelete}>
283+
{getLocString('integrationsDelete', 'Delete')}
284+
</button>
207285
<button type="button" className="secondary" onClick={handleCancelDelete}>
208286
{getLocString('integrationsCancel', 'Cancel')}
209287
</button>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { detectBaseTheme } from '../react-common/themeDetector';
66
import { IntegrationPanel } from './IntegrationPanel';
77

88
import '../common/index.css';
9+
import '../react-common/codicon/codicon.css';
910
import './integrations.css';
1011

1112
// This special function talks to vscode from a web panel

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,23 @@ button.primary {
118118
color: var(--vscode-button-foreground);
119119
}
120120

121+
button.icon-button {
122+
padding: 6px 8px;
123+
min-width: auto;
124+
background-color: transparent;
125+
border: 1px solid transparent;
126+
color: var(--vscode-foreground);
127+
}
128+
129+
button.icon-button:hover {
130+
background-color: var(--vscode-toolbar-hoverBackground);
131+
border-color: var(--vscode-button-border);
132+
}
133+
134+
button.icon-button .codicon {
135+
font-size: 16px;
136+
}
137+
121138
/* Configuration form overlay */
122139
.configuration-form-overlay {
123140
position: fixed;

0 commit comments

Comments
 (0)