From 3ab346efd965f46bfeb32c322fd8c19491726fed Mon Sep 17 00:00:00 2001 From: KarimTamani Date: Tue, 3 Jun 2025 22:22:56 +0100 Subject: [PATCH] rename database feature implemented --- src/components/menu/menu-dropdown.tsx | 5 +- src/components/menu/menu.tsx | 6 +- src/components/modal/modal.tsx | 42 +++++-- src/components/navbar/navbar.tsx | 6 +- src/components/navbar/rename-database.tsx | 109 ++++++++++++++++++ src/i18/languages/en.ts | 9 +- src/pages/database/database-page.tsx | 2 +- .../database/modals/delete-database-modal.tsx | 42 +++++++ .../database-history-provider.tsx | 1 + .../database-provider/database-context.ts | 2 +- .../database-provider/database-provider.tsx | 29 +++-- .../modal-provider/modal-contxet.tsx | 3 +- .../modal-provider/modal-provider.tsx | 6 +- src/utils/database.ts | 9 +- 14 files changed, 237 insertions(+), 34 deletions(-) create mode 100644 src/components/navbar/rename-database.tsx create mode 100644 src/pages/database/modals/delete-database-modal.tsx diff --git a/src/components/menu/menu-dropdown.tsx b/src/components/menu/menu-dropdown.tsx index 99da743..75faa07 100644 --- a/src/components/menu/menu-dropdown.tsx +++ b/src/components/menu/menu-dropdown.tsx @@ -45,18 +45,19 @@ const DropdownMenu: React.FC = ({ title, children, clickHand { !child.children ? -
+
{child.title} { child.divide && diff --git a/src/components/menu/menu.tsx b/src/components/menu/menu.tsx index 9fa486f..8e92316 100644 --- a/src/components/menu/menu.tsx +++ b/src/components/menu/menu.tsx @@ -59,7 +59,11 @@ const Menu: React.FC = ({ }) => { ], }, { title: t("menu.export_orm_models"), divide: true }, - { title: t("menu.delete_project"), theme: "danger" }, + { + title: t("menu.delete_project"), theme: "danger", clickHandler: () => { + open(Modals.DELETE_DATABASE) + } + }, ], }, { diff --git a/src/components/modal/modal.tsx b/src/components/modal/modal.tsx index d02f177..e71c203 100644 --- a/src/components/modal/modal.tsx +++ b/src/components/modal/modal.tsx @@ -22,22 +22,23 @@ export interface ModalProps { actionName?: string, actionHandler?: () => void, isDisabled?: boolean, - header?: string + header?: string, + variant?: "default" | "danger" } -const Modal: React.FC = ({ isOpen, onOpenChange, className, backdrop = "opaque", title, children, actionName = "Action", header, actionHandler, isDisabled }) => { +const Modal: React.FC = ({ isOpen, onOpenChange, className, backdrop = "opaque", title, children, actionName = "Action", header, actionHandler, isDisabled, variant = "default" }) => { const targetRef = React.useRef(null); const { moveProps } = useDraggable({ targetRef, canOverflow: true, isDisabled: !isOpen }); - const [isLoading , setIsLoading] = useState( false ) ; + const [isLoading, setIsLoading] = useState(false); const { t } = useTranslation(); const handleAction = async (onClose: () => void) => { - setIsLoading( true) - actionHandler && await actionHandler(); - setIsLoading( false) ; + setIsLoading(true) + actionHandler && await actionHandler(); + setIsLoading(false); onClose() } return ( @@ -68,12 +69,29 @@ const Modal: React.FC = ({ isOpen, onOpenChange, className, backdrop
- - + { + variant == "default" && + <> + + + + } + { + variant == "danger" && + <> + + + + + }
diff --git a/src/components/navbar/navbar.tsx b/src/components/navbar/navbar.tsx index b11725b..d870911 100644 --- a/src/components/navbar/navbar.tsx +++ b/src/components/navbar/navbar.tsx @@ -2,6 +2,7 @@ import Menu from "../menu/menu"; import ConnectionStatus from "./connection-status"; +import RenameDatabase from "./rename-database"; interface Props { @@ -23,9 +24,12 @@ const Navbar: React.FC = ({ }) => { />

StackRender

-
+
+
+ +
diff --git a/src/components/navbar/rename-database.tsx b/src/components/navbar/rename-database.tsx new file mode 100644 index 0000000..89745a4 --- /dev/null +++ b/src/components/navbar/rename-database.tsx @@ -0,0 +1,109 @@ +import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider"; +import React, { useEffect, useState } from "react"; +import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip/tooltip"; +import { useTranslation } from "react-i18next"; +import { Button, Divider, Image, Input } from "@heroui/react"; +import { getDatabaseByDialect } from "@/lib/database"; +import { Save } from "lucide-react"; + + + +const RenameDatabase: React.FC = ({ }) => { + const { database } = useDatabase(); + const { editDatabase } = useDatabaseOperations(); + const [editMode, setEditMode] = useState(true); + const [dbName, setDbName] = useState(database.name); + const { t } = useTranslation(); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + console.log (database.name) ; + setDbName(database.name); + }, [database.name]) + + const saveDatabaseName = async () => { + if (!dbName || dbName.trim().length == 0) + return; + setIsLoading(true); + + await editDatabase({ + id: database.id, + name: dbName + }); + setIsLoading(false); + setEditMode(false); + } + return ( +
+ { + !editMode && + + + + + + {t("navbar.rename_db")} + + + } + { + editMode && +
+ + + + setDbName(event.target.value)} + value={dbName} + onBlur={saveDatabaseName} + className="h-8 w-full focus-visible:ring-0 shadow-none" + classNames={{ + inputWrapper: "dark:bg-default border-divider group-hover:border-primary ", + }} + + + /> + + + +
+ } +
+ ) +} + + +export default React.memo(RenameDatabase); \ No newline at end of file diff --git a/src/i18/languages/en.ts b/src/i18/languages/en.ts index 7fe057a..7953e54 100644 --- a/src/i18/languages/en.ts +++ b/src/i18/languages/en.ts @@ -12,6 +12,9 @@ export const en = { color_picker: { default_color: "Default color" }, + navbar : { + rename_db : "Rename database" , + } , db_controller: { filter: "Filter", add_table: "Add Table", @@ -116,6 +119,7 @@ export const en = { show_docs: "Show Docs", join_discord: "Join Discord", + }, modals: { @@ -128,7 +132,10 @@ export const en = { continue : "Continue" , open : "Open" , open_database : "Open Database" , - open_database_header : "Open a database by selecting one from the list." + open_database_header : "Open a database by selecting one from the list." , + delete_database : "Delete Database" , + delete_database_content : "This action is irreversible and will permanently remove the diagram." , + delete : "Delete" } } diff --git a/src/pages/database/database-page.tsx b/src/pages/database/database-page.tsx index 5f562a1..dd185b6 100644 --- a/src/pages/database/database-page.tsx +++ b/src/pages/database/database-page.tsx @@ -205,7 +205,7 @@ const DatabasePage: React.FC = () => { /> - +
= ({ isOpen, onOpenChange }) => { + + const { currentDatabaseId } = useDatabase(); + const { deleteDatabase , switchDatabase} = useDatabaseOperations(); + const { t } = useTranslation(); + + const onDelete = useCallback(async () => { + return await new Promise(async (res, rej) => { + await deleteDatabase(currentDatabaseId as string); + switchDatabase( undefined ) ; + res(currentDatabaseId) + }) + }, [currentDatabaseId]) + + + return ( + +

+ {t("modals.delete_database_content")} +

+
+ ) +} + + +export default DeleteDatabaseModal; + diff --git a/src/providers/database-history/database-history-provider.tsx b/src/providers/database-history/database-history-provider.tsx index ade19b3..b94bb84 100644 --- a/src/providers/database-history/database-history-provider.tsx +++ b/src/providers/database-history/database-history-provider.tsx @@ -54,6 +54,7 @@ const DatabaseHistoryProvider: React.FC = ({ children }) => { const normalizedPresent = normalizeDatabase(datatbaseState.present); const differences = compare(normalizedDatabase, normalizedPresent); + if (differences && differences.length > 0) { setIsProcessing(true); diff --git a/src/providers/database-provider/database-context.ts b/src/providers/database-provider/database-context.ts index 8b7ac4b..96ca316 100644 --- a/src/providers/database-provider/database-context.ts +++ b/src/providers/database-provider/database-context.ts @@ -30,7 +30,7 @@ interface DatabaseOperationsContextType { createDatabase: (database: DatabaseInsertType) => Promise, editDatabase: (database: DatabaseInsertType) => Promise, deleteDatabase: (id: string) => Promise, - switchDatabase: (databaseId: string) => void, + switchDatabase: (databaseId: string | undefined ) => void, createTable: (table: TableInsertType) => Promise, editTable: (table: TableInsertType) => Promise, diff --git a/src/providers/database-provider/database-provider.tsx b/src/providers/database-provider/database-provider.tsx index 4242d58..13e379f 100644 --- a/src/providers/database-provider/database-provider.tsx +++ b/src/providers/database-provider/database-provider.tsx @@ -15,13 +15,13 @@ import { getTimestamp } from "@/utils/utils"; import { IndexInsertType, indices } from "@/lib/schemas/index-schema"; import { field_indices } from "@/lib/schemas/field_index-schema"; import { v4 } from "uuid"; -import { SQLiteTransaction } from "drizzle-orm/sqlite-core"; + interface Props { children: React.ReactNode } const DatabaseProvider: React.FC = ({ children }) => { - const [currentDatabaseId, setCurrentDatabaseId] = useState(undefined); + const [currentDatabaseId, setCurrentDatabaseId] = useState(localStorage.getItem("database_id") as string | undefined); // Fetch all databases const { data: databases, isLoading: loadingDatabases } = useQuery(toCompilableQuery( @@ -71,9 +71,12 @@ const DatabaseProvider: React.FC = ({ children }) => { ); - const switchDatabase = useCallback((databaseId: string) => { + const switchDatabase = useCallback((databaseId: string | undefined) => { setCurrentDatabaseId(databaseId); - localStorage.setItem("database_id", databaseId); + if (databaseId) + localStorage.setItem("database_id", databaseId); + else + localStorage.removeItem("database_id") }, []) // Normalize result to single object @@ -111,12 +114,12 @@ const DatabaseProvider: React.FC = ({ children }) => { const updateDbNumTables = useCallback(async (databaseId: string, tx: any) => { - const [{ count : numOfTables }] = await tx + const [{ count: numOfTables }] = await tx .select({ count: count() }) .from(tables) .where(eq(tables.databaseId, databaseId)) - console.log (numOfTables) + console.log(numOfTables) await tx.update(databaseModel).set({ numOfTables }).where(eq(databaseModel.id, databaseId)) @@ -132,9 +135,6 @@ const DatabaseProvider: React.FC = ({ children }) => { databaseId: currentDatabaseId, createdAt: table.createdAt || getTimestamp() }); - - - await updateDbNumTables(currentDatabaseId, tx); if (table.fields) { await tx.insert(fields).values( @@ -279,7 +279,14 @@ const DatabaseProvider: React.FC = ({ children }) => { try { await db.transaction(async (tx) => { for (const operation of operations) { - if (operation.type == "CREATE_TABLE") { + + if ( operation.type == "RENAME_DATABASE") { + currentDatabaseId && await tx.update(databaseModel).set({ + name : operation.chnages.name + }).where(eq(databaseModel.id , currentDatabaseId)) ; + } + + else if (operation.type == "CREATE_TABLE") { await tx.insert(tables).values(operation.table); currentDatabaseId && await updateDbNumTables(currentDatabaseId, tx); if (operation.table.fields && Object.values(operation.table.fields).length > 0) @@ -393,7 +400,7 @@ const DatabaseProvider: React.FC = ({ children }) => { = ({ children }) => { || currentModal.modal == Modals.CREATE_DATABASE && - + || currentModal.modal == Modals.OPEN_DATABASE && + || + currentModal.modal == Modals.DELETE_DATABASE && + ) : undefined } {children} diff --git a/src/utils/database.ts b/src/utils/database.ts index 14e4e6d..5363a51 100644 --- a/src/utils/database.ts +++ b/src/utils/database.ts @@ -1,5 +1,5 @@ // Importing types from schema definitions -import { DatabaseType } from "@/lib/schemas/database-schema"; +import { DatabaseInsertType, DatabaseType } from "@/lib/schemas/database-schema"; import { FieldType } from "@/lib/schemas/field-schema"; import { RelationshipType } from "@/lib/schemas/relationship-schema"; import { TableType } from "@/lib/schemas/table-schema"; @@ -9,6 +9,7 @@ import { FieldIndexType } from "@/lib/schemas/field_index-schema"; // Define the possible operations that can be performed when diffing databases export type DBDiffOperation = + | { type: "RENAME_DATABASE", chnages: DatabaseInsertType } | { type: 'CREATE_TABLE'; table: TableType } | { type: 'DELETE_TABLE'; tableId: string } | { type: 'UPDATE_TABLE'; tableId: string; changes: Partial } @@ -53,8 +54,12 @@ export function mapDiffToDBDiffOperation(patch: any[]): DBDiffOperation[] { for (const op of patch) { const parts = op.path.split('/').filter(Boolean); // Split JSON path into parts + if (op.op == "replace" && parts[0] == "name") { + operations.push({ type: "RENAME_DATABASE", chnages: { name: op.value } as DatabaseInsertType }) + } + // Handle table-related changes - if (parts[0] == "tables") { + else if (parts[0] == "tables") { const tableId = parts[1]; // Whole table operations (add/delete table)