From c43e8d7f30934e2a49d7fa9fb827c683e970ebea Mon Sep 17 00:00:00 2001 From: Guy Ben-Aharon Date: Fri, 23 Aug 2024 03:17:52 +0300 Subject: [PATCH] clear diagram data --- package-lock.json | 29 + package.json | 1 + src/components/alert-dialog/alert-dialog.tsx | 139 +++++ .../chartdb-context/chartdb-context.tsx | 2 + .../chartdb-context/chartdb-provider.tsx | 21 +- .../storage-context/storage-context.tsx | 4 + .../storage-context/storage-provider.tsx | 16 + .../export-sql-dialog/export-sql-dialog.tsx | 1 + .../editor-page/top-navbar/top-navbar.tsx | 504 ++++++++++-------- 9 files changed, 498 insertions(+), 219 deletions(-) create mode 100644 src/components/alert-dialog/alert-dialog.tsx diff --git a/package-lock.json b/package-lock.json index 00af81a5..20a14341 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@ai-sdk/openai": "^0.0.51", "@radix-ui/react-accordion": "^1.2.0", + "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-collapsible": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", @@ -1402,6 +1403,34 @@ } } }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.1.tgz", + "integrity": "sha512-wmCoJwj7byuVuiLKqDLlX7ClSUU0vd9sdCeM+2Ls+uf13+cpSJoMgwysHq1SGVVkJj5Xn0XWi1NoRCdkMpr6Mw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-dialog": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", diff --git a/package.json b/package.json index 5db83dc3..c2660adf 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dependencies": { "@ai-sdk/openai": "^0.0.51", "@radix-ui/react-accordion": "^1.2.0", + "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-collapsible": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", diff --git a/src/components/alert-dialog/alert-dialog.tsx b/src/components/alert-dialog/alert-dialog.tsx new file mode 100644 index 00000000..2bb872c6 --- /dev/null +++ b/src/components/alert-dialog/alert-dialog.tsx @@ -0,0 +1,139 @@ +import * as React from 'react'; +import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'; + +import { cn } from '@/lib/utils'; +import { buttonVariants } from '../button/button-variants'; + +const AlertDialog = AlertDialogPrimitive.Root; + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger; + +const AlertDialogPortal = AlertDialogPrimitive.Portal; + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)); +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +AlertDialogHeader.displayName = 'AlertDialogHeader'; + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +AlertDialogFooter.displayName = 'AlertDialogFooter'; + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName; + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +}; diff --git a/src/context/chartdb-context/chartdb-context.tsx b/src/context/chartdb-context/chartdb-context.tsx index dd358f8c..a39a9c02 100644 --- a/src/context/chartdb-context/chartdb-context.tsx +++ b/src/context/chartdb-context/chartdb-context.tsx @@ -23,6 +23,7 @@ export interface ChartDBContext { ) => Promise; loadDiagram: (diagramId: string) => Promise; updateDiagramUpdatedAt: () => Promise; + clearDiagramData: () => Promise; // Database type operations updateDatabaseType: (databaseType: DatabaseType) => Promise; @@ -138,6 +139,7 @@ export const chartDBContext = createContext({ updateDiagramName: emptyFn, updateDiagramUpdatedAt: emptyFn, loadDiagram: emptyFn, + clearDiagramData: emptyFn, // Database type operations updateDatabaseType: emptyFn, diff --git a/src/context/chartdb-context/chartdb-provider.tsx b/src/context/chartdb-context/chartdb-provider.tsx index 2c3a0559..bb9e907c 100644 --- a/src/context/chartdb-context/chartdb-provider.tsx +++ b/src/context/chartdb-context/chartdb-provider.tsx @@ -15,7 +15,8 @@ export const ChartDBProvider: React.FC = ({ children, }) => { const db = useStorage(); - const { addUndoAction, resetRedoStack } = useRedoUndoStack(); + const { addUndoAction, resetRedoStack, resetUndoStack } = + useRedoUndoStack(); const [diagramId, setDiagramId] = useState(''); const [diagramName, setDiagramName] = useState(''); const [diagramCreatedAt, setDiagramCreatedAt] = useState(new Date()); @@ -47,6 +48,23 @@ export const ChartDBProvider: React.FC = ({ ] ); + const clearDiagramData: ChartDBContext['clearDiagramData'] = + useCallback(async () => { + const updatedAt = new Date(); + setTables([]); + setRelationships([]); + setDiagramUpdatedAt(updatedAt); + + resetRedoStack(); + resetUndoStack(); + + await Promise.all([ + db.updateDiagram({ id: diagramId, attributes: { updatedAt } }), + db.deleteDiagramTables(diagramId), + db.deleteDiagramRelationships(diagramId), + ]); + }, [db, diagramId, resetRedoStack, resetUndoStack]); + const updateDiagramUpdatedAt: ChartDBContext['updateDiagramUpdatedAt'] = useCallback(async () => { const updatedAt = new Date(); @@ -928,6 +946,7 @@ export const ChartDBProvider: React.FC = ({ updateDiagramName, loadDiagram, updateDatabaseType, + clearDiagramData, updateDiagramUpdatedAt, createTable, addTable, diff --git a/src/context/storage-context/storage-context.tsx b/src/context/storage-context/storage-context.tsx index 96e8236d..9238b00e 100644 --- a/src/context/storage-context/storage-context.tsx +++ b/src/context/storage-context/storage-context.tsx @@ -41,6 +41,7 @@ export interface StorageContext { }) => Promise; deleteTable: (params: { diagramId: string; id: string }) => Promise; listTables: (diagramId: string) => Promise; + deleteDiagramTables: (diagramId: string) => Promise; // Relationships operations addRelationship: (params: { @@ -60,6 +61,7 @@ export interface StorageContext { id: string; }) => Promise; listRelationships: (diagramId: string) => Promise; + deleteDiagramRelationships: (diagramId: string) => Promise; } export const storageContext = createContext({ @@ -77,10 +79,12 @@ export const storageContext = createContext({ updateTable: emptyFn, deleteTable: emptyFn, listTables: emptyFn, + deleteDiagramTables: emptyFn, addRelationship: emptyFn, getRelationship: emptyFn, updateRelationship: emptyFn, deleteRelationship: emptyFn, listRelationships: emptyFn, + deleteDiagramRelationships: emptyFn, }); diff --git a/src/context/storage-context/storage-provider.tsx b/src/context/storage-context/storage-provider.tsx index 7e3efea4..1065875e 100644 --- a/src/context/storage-context/storage-provider.tsx +++ b/src/context/storage-context/storage-provider.tsx @@ -188,6 +188,12 @@ export const StorageProvider: React.FC = ({ return await db.db_tables.get({ id, diagramId }); }; + const deleteDiagramTables: StorageContext['deleteDiagramTables'] = async ( + diagramId: string + ) => { + await db.db_tables.where('diagramId').equals(diagramId).delete(); + }; + const updateTable: StorageContext['updateTable'] = async ({ id, attributes, @@ -238,6 +244,14 @@ export const StorageProvider: React.FC = ({ }); }; + const deleteDiagramRelationships: StorageContext['deleteDiagramRelationships'] = + async (diagramId: string) => { + await db.db_relationships + .where('diagramId') + .equals(diagramId) + .delete(); + }; + const getRelationship: StorageContext['getRelationship'] = async ({ id, diagramId, @@ -302,6 +316,8 @@ export const StorageProvider: React.FC = ({ updateRelationship, deleteRelationship, listRelationships, + deleteDiagramTables, + deleteDiagramRelationships, }} > {children} diff --git a/src/dialogs/export-sql-dialog/export-sql-dialog.tsx b/src/dialogs/export-sql-dialog/export-sql-dialog.tsx index ac0f3c35..d18a3d7f 100644 --- a/src/dialogs/export-sql-dialog/export-sql-dialog.tsx +++ b/src/dialogs/export-sql-dialog/export-sql-dialog.tsx @@ -45,6 +45,7 @@ export const ExportSQLDialog: React.FC = ({ }, [targetDatabaseType, currentDiagram]); useEffect(() => { + if (!dialog.open) return; setScript(undefined); const fetchScript = async () => { const script = await exportSQLScript(); diff --git a/src/pages/editor-page/top-navbar/top-navbar.tsx b/src/pages/editor-page/top-navbar/top-navbar.tsx index 8858d42f..55754672 100644 --- a/src/pages/editor-page/top-navbar/top-navbar.tsx +++ b/src/pages/editor-page/top-navbar/top-navbar.tsx @@ -32,16 +32,28 @@ import { databaseTypeToLabelMap, } from '@/lib/databases'; import { DatabaseType } from '@/lib/domain/database-type'; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@/components/alert-dialog/alert-dialog'; export interface TopNavbarProps {} export const TopNavbar: React.FC = () => { - const { diagramName, updateDiagramName, currentDiagram } = useChartDB(); + const { diagramName, updateDiagramName, currentDiagram, clearDiagramData } = + useChartDB(); const { openCreateDiagramDialog, openOpenDiagramDialog, openExportSQLDialog, } = useDialog(); + const [showClearAlert, setShowClearAlert] = useState(false); const [editMode, setEditMode] = useState(false); const { exportImage } = useExportImage(); const [editedDiagramName, setEditedDiagramName] = @@ -79,6 +91,11 @@ export const TopNavbar: React.FC = () => { setEditMode(true); }; + const clearDiagramDataHandler = useCallback(async () => { + setShowClearAlert(false); + await clearDiagramData(); + }, [clearDiagramData]); + const exportPNG = useCallback(() => { exportImage('png'); }, [exportImage]); @@ -103,228 +120,279 @@ export const TopNavbar: React.FC = () => { }, []); return ( - + + + + + + Are you absolutely sure? + + + This action cannot be undone. This will permanently + delete all the data in the diagram. + + + + setShowClearAlert(false)} + > + Cancel + + + Clear + + + + + ); };