import React, { useEffect, useState } from 'react'; import { Add, ArrowDropDown, Delete, Settings } from '@mui/icons-material'; import { DatabaseInfo, useAppDispatch, useDatabaseAPI, useSchemaGraph, useSessionCache } from '@graphpolaris/shared/lib/data-access'; import { updateCurrentDatabase } from '@graphpolaris/shared/lib/data-access/store/sessionSlice'; import { SettingsForm } from './forms/settings'; import { NewDatabaseForm } from './forms/AddDatabase/newdatabase'; import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner'; import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice'; import { clearQB } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice'; import { clearSchema } from '@graphpolaris/shared/lib/data-access/store/schemaSlice'; export default function DatabaseSelector({}) { const dispatch = useAppDispatch(); const api = useDatabaseAPI(); const session = useSessionCache(); const schemaGraph = useSchemaGraph(); const dbSelectionMenuRef = React.useRef<HTMLDivElement>(null); const [hovered, setHovered] = useState<string | null>(null); const [connecting, setConnecting] = useState<boolean>(false); const [dbSelectionMenuOpen, setDbSelectionMenuOpen] = useState<boolean>(false); const [settingsMenuOpen, setSettingsMenuOpen] = useState<boolean>(false); const [selectedDatabase, setSelectedDatabase] = useState<DatabaseInfo | null>(null); const [addDatabaseFormOpen, setAddDatabaseFormOpen] = useState<boolean>(false); useEffect(() => { const handleClickOutside = ({ target }: MouseEvent) => { if (dbSelectionMenuRef.current && !dbSelectionMenuRef.current.contains(target as Node)) { setDbSelectionMenuOpen(false); } }; document.addEventListener('click', handleClickOutside); return () => { document.removeEventListener('click', handleClickOutside); }; }, []); useEffect(() => { setConnecting(false); }, [schemaGraph]); useEffect(() => { let timeoutId: ReturnType<typeof setTimeout>; if (connecting) { timeoutId = setTimeout(() => { dispatch(addError("Couldn't establish connection")); setConnecting(false); dispatch(updateCurrentDatabase(undefined)); dispatch(clearQB()); dispatch(clearSchema()); }, 10000); } return () => { if (timeoutId) { clearTimeout(timeoutId); } }; }, [connecting]); return ( <> <SettingsForm open={settingsMenuOpen} database={selectedDatabase} onClose={() => { setSettingsMenuOpen(false); }} /> <NewDatabaseForm open={addDatabaseFormOpen} onClose={() => { setAddDatabaseFormOpen(false); }} /> <div className="relative flex-shrink max-md:flex-grow border w-full xl:w-[30rem] min-w-0 max-h-full ml-auto mr-auto cursor-pointer" ref={dbSelectionMenuRef} > <div className="flex w-full px-4 py-2 hover:bg-slate-200 transition-colors duration-300" onClick={() => { if (session.databases?.length === 0) setAddDatabaseFormOpen(true); else setDbSelectionMenuOpen(!dbSelectionMenuOpen); }} > <div className="flex items-center w-full shrink-0"> {connecting ? ( <> <LoadingSpinner /> <p className="ml-2 truncate"> <span className="max-md:hidden">Connecting to </span> {session.currentDatabase} </p> </> ) : session.currentDatabase ? ( <> <div className="h-[8px] w-[8px] shrink-0 rounded-full bg-green-500" /> <p className="ml-2 truncate"> <span className="max-md:hidden">Connected DB: </span> {session.currentDatabase} </p> </> ) : session.databases === undefined ? ( <> <LoadingSpinner /> <p className="ml-2">Retrieving databases</p> </> ) : session.databases?.length === 0 ? ( <> <p className="ml-2">Add your first Database</p> </> ) : ( <> <div className="h-2 w-2 rounded-full bg-slate-500" /> <p className="ml-2">Select a database</p> </> )} </div> <ArrowDropDown /> </div> {dbSelectionMenuOpen && session.databases && ( <div className="absolute w-full top-11 z-50 bg-slate-100 border"> <div className="flex items-center p-2 hover:bg-slate-200" onClick={(e) => { e.preventDefault(); setDbSelectionMenuOpen(false); setConnecting(false); setAddDatabaseFormOpen(true); }} title="Add new database" > {session.databases.length === 0 ? ( <> <Add /> <p className="ml-2">Add your first database</p> </> ) : ( <> <Add /> <p className="ml-2">Add database</p> </> )} </div> {session.databases.map((db) => ( <div key={db.Name} className="flex justify-between items-center px-4 py-2 hover:bg-slate-200 gap-2" onClick={(e) => { if (db.Name !== session.currentDatabase) { e.preventDefault(); setDbSelectionMenuOpen(false); setConnecting(true); dispatch(updateCurrentDatabase(db.Name)); dispatch(clearQB()); dispatch(clearSchema()); } else { setDbSelectionMenuOpen(false); } }} onMouseEnter={() => setHovered(db.Name)} onMouseLeave={() => setHovered(null)} title={`Connect to ${db.Name}`} > <div className={`h-[8px] w-[8px] rounded-full shrink-0 ${db.status ? 'bg-green-500' : 'bg-red-500'}`} /> <div className="w-full shrink min-w-0 flex flex-col"> <p className="truncate w-full shrink-0 min-w-0">{db.Name}</p> <p className="text-xs text-slate-400 truncate w-fit shrink-0 min-w-0 max-w-full h-full border border-slate-300 rounded-sm p-0.5"> {db.Protocol} {db.URL} </p> </div> {hovered === db.Name && ( <div className="flex items-center ml-2"> <div className="text-slate-700 hover:text-slate-400 transition-colors duration-300" onClick={(e) => { e.preventDefault(); e.stopPropagation(); setSettingsMenuOpen(true); setSelectedDatabase(db); }} > <Settings /> </div> <div className="text-slate-700 hover:text-slate-400 transition-colors duration-300" onClick={(e) => { e.preventDefault(); dispatch(updateCurrentDatabase(undefined)); dispatch(clearQB()); dispatch(clearSchema()); api.DeleteDatabase(db.Name); }} title="Delete database" > <Delete /> </div> </div> )} </div> ))} </div> )} </div> </> ); }