From 9181d5d83c684e8ece21ccd3630335ded58cef33 Mon Sep 17 00:00:00 2001 From: Richard Osborne Date: Thu, 15 Jan 2026 18:43:01 +0100 Subject: [PATCH] fix(editor): replace prompt/confirm with proper dialogs in BackendServicesPanel Electron doesn't support browser prompt()/confirm() APIs. Replaced with: - Inline TextInput form for creating local backends - useConfirmationDialog hook for delete confirmations Fixes console error when clicking + button on Local Backends section. --- .../BackendServicesPanel.tsx | 126 ++++++++++++++---- 1 file changed, 100 insertions(+), 26 deletions(-) diff --git a/packages/noodl-editor/src/editor/src/views/panels/BackendServicesPanel/BackendServicesPanel.tsx b/packages/noodl-editor/src/editor/src/views/panels/BackendServicesPanel/BackendServicesPanel.tsx index 614c848..a591df9 100644 --- a/packages/noodl-editor/src/editor/src/views/panels/BackendServicesPanel/BackendServicesPanel.tsx +++ b/packages/noodl-editor/src/editor/src/views/panels/BackendServicesPanel/BackendServicesPanel.tsx @@ -16,9 +16,11 @@ import { BackendServices, BackendServicesEvent } from '@noodl-models/BackendServ import { ActivityIndicator } from '@noodl-core-ui/components/common/ActivityIndicator'; import { IconName, IconSize } from '@noodl-core-ui/components/common/Icon'; import { IconButton } from '@noodl-core-ui/components/inputs/IconButton'; +import { PrimaryButton, PrimaryButtonVariant, PrimaryButtonSize } from '@noodl-core-ui/components/inputs/PrimaryButton'; +import { TextInput } from '@noodl-core-ui/components/inputs/TextInput'; import { Box } from '@noodl-core-ui/components/layout/Box'; import { Container } from '@noodl-core-ui/components/layout/Container'; -import { VStack } from '@noodl-core-ui/components/layout/Stack'; +import { HStack, VStack } from '@noodl-core-ui/components/layout/Stack'; import { useConfirmationDialog } from '@noodl-core-ui/components/popups/ConfirmationDialog/ConfirmationDialog.hooks'; import { BasePanel } from '@noodl-core-ui/components/sidebar/BasePanel'; import { Section, SectionVariant } from '@noodl-core-ui/components/sidebar/Section'; @@ -36,6 +38,10 @@ export function BackendServicesPanel() { const [isAddDialogVisible, setIsAddDialogVisible] = useState(false); const [hasActivity, setHasActivity] = useState(false); + // Local backend creation state + const [isAddLocalVisible, setIsAddLocalVisible] = useState(false); + const [newLocalBackendName, setNewLocalBackendName] = useState(''); + // Local backends hook const { backends: localBackends, @@ -65,13 +71,42 @@ export function BackendServicesPanel() { setActiveBackendId(id); }); - // Delete confirmation dialog + // Delete confirmation dialog for external backends const [DeleteDialog, confirmDelete] = useConfirmationDialog({ title: 'Delete Backend', message: 'Are you sure you want to delete this backend configuration? This cannot be undone.', isDangerousAction: true }); + // Delete confirmation dialog for local backends + const [DeleteLocalDialog, confirmDeleteLocal] = useConfirmationDialog({ + title: 'Delete Local Backend', + message: 'Delete this local backend? All data will be permanently lost.', + isDangerousAction: true + }); + + // Handle delete local backend + const handleDeleteLocalBackend = useCallback( + async (id: string) => { + try { + await confirmDeleteLocal(); + await deleteLocalBackend(id); + } catch { + // User cancelled + } + }, + [confirmDeleteLocal, deleteLocalBackend] + ); + + // Handle create local backend + const handleCreateLocalBackend = useCallback(async () => { + if (newLocalBackendName.trim()) { + await createLocalBackend(newLocalBackendName.trim()); + setNewLocalBackendName(''); + setIsAddLocalVisible(false); + } + }, [newLocalBackendName, createLocalBackend]); + // Handle delete backend const handleDeleteBackend = useCallback( async (id: string) => { @@ -124,6 +159,7 @@ export function BackendServicesPanel() { return ( + {isLoading || isLoadingLocal ? ( @@ -136,19 +172,59 @@ export function BackendServicesPanel() { title="Local Backends" variant={SectionVariant.Panel} actions={ - { - const name = prompt('Enter backend name:'); - if (name) { - await createLocalBackend(name); - } - }} - testId="add-local-backend-button" - /> + !isAddLocalVisible && ( + setIsAddLocalVisible(true)} + testId="add-local-backend-button" + /> + ) } > + {/* Add Local Backend Form */} + {isAddLocalVisible && ( + + + Create Local Backend + + setNewLocalBackendName(e.target.value)} + placeholder="Backend name" + onKeyDown={(e) => { + if (e.key === 'Enter') handleCreateLocalBackend(); + if (e.key === 'Escape') { + setIsAddLocalVisible(false); + setNewLocalBackendName(''); + } + }} + /> + + + + + { + setIsAddLocalVisible(false); + setNewLocalBackendName(''); + }} + /> + + + + + )} + {localBackends.length > 0 ? ( {localBackends.map((backend) => ( @@ -157,23 +233,21 @@ export function BackendServicesPanel() { backend={backend} onStart={async () => startLocalBackend(backend.id)} onStop={async () => stopLocalBackend(backend.id)} - onDelete={() => { - if (confirm('Delete this local backend? All data will be lost.')) { - deleteLocalBackend(backend.id); - } - }} + onDelete={() => handleDeleteLocalBackend(backend.id)} /> ))} ) : ( - - No local backends - - - Create a local SQLite backend for zero-config database development. - - - + !isAddLocalVisible && ( + + No local backends + + + Create a local SQLite backend for zero-config database development. + + + + ) )}