import React, { useEffect, useState } from 'react'; import { Add, Delete, Settings, StorageOutlined } from '@mui/icons-material'; import { useAppDispatch, useSchemaGraph, useSessionCache, useAuthorizationCache } from '@graphpolaris/shared/lib/data-access'; import { deleteSaveState, selectSaveState } from '@graphpolaris/shared/lib/data-access/store/sessionSlice'; import { SettingsForm } from './forms/settings'; import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner'; import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice'; import { DropdownTrigger, DropdownContainer, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns'; import { clearQB } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice'; import { clearSchema } from '@graphpolaris/shared/lib/data-access/store/schemaSlice'; import { DatabaseStatus, SaveStateI, nilUUID, wsDeleteState } from '@graphpolaris/shared/lib/data-access/broker'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@graphpolaris/shared/lib/components/tooltip'; import { Icon } from '@graphpolaris/shared'; export default function DatabaseSelector({}) { const dispatch = useAppDispatch(); const session = useSessionCache(); const schemaGraph = useSchemaGraph(); const authCache = useAuthorizationCache(); const [hovered, setHovered] = useState<string | null>(null); const [connecting, setConnecting] = useState<boolean>(false); const [dbSelectionMenuOpen, setDbSelectionMenuOpen] = useState<boolean>(false); const [settingsMenuOpen, setSettingsMenuOpen] = useState<'add' | 'update' | undefined>(undefined); const [selectedSaveState, setSelectedSaveState] = useState<SaveStateI | null>(null); // const [addDbConnectionFormOpen, setAddDbConnectionFormOpen] = useState<boolean>(false); useEffect(() => { if ( (!session.fetchingSaveStates && session.saveStates && Object.keys(session.saveStates).length === 0 && settingsMenuOpen === undefined) || session.currentSaveState === nilUUID ) { setSettingsMenuOpen('add'); } }, [session, settingsMenuOpen]); useEffect(() => { setConnecting(false); }, [schemaGraph]); useEffect(() => { let timeoutId: ReturnType<typeof setTimeout>; if (connecting) { timeoutId = setTimeout(() => { dispatch(addError("Couldn't establish connection")); setConnecting(false); dispatch(selectSaveState(undefined)); dispatch(clearQB()); dispatch(clearSchema()); }, 10000); } return () => { if (timeoutId) clearTimeout(timeoutId); }; }, [connecting]); return ( <div className="menu-walkthrough"> <TooltipProvider delayDuration={1000}> {settingsMenuOpen !== undefined && ( <SettingsForm open={settingsMenuOpen} saveState={settingsMenuOpen === 'update' ? selectedSaveState : null} disableCancel={ (session.saveStates && Object.keys(session.saveStates).length === 0) || session.currentSaveState === '00000000-0000-0000-0000-000000000000' } onClose={() => { setSettingsMenuOpen(undefined); }} /> )} <DropdownContainer open={dbSelectionMenuOpen} onOpenChange={(ret) => { if (!ret) { if (session.saveStates && Object.keys(session.saveStates).length === 0) setSettingsMenuOpen('add'); else setDbSelectionMenuOpen(!dbSelectionMenuOpen); } else { setDbSelectionMenuOpen(true); } }} > <DropdownTrigger onClick={() => { setDbSelectionMenuOpen(!dbSelectionMenuOpen); }} className="w-[18rem]" size="md" disabled={connecting || authCache.authorized === false || !!authCache.roomID} title={ <div className="flex items-center"> {connecting && session.currentSaveState && session.currentSaveState in session.saveStates ? ( <> <LoadingSpinner /> <p className="ml-2 truncate">Connecting to {session.saveStates[session.currentSaveState].name}</p> </> ) : session.currentSaveState && session.currentSaveState in session.saveStates && session.currentSaveState !== nilUUID ? ( <div className="flex items-center"> <div className="relative self-center"> <Icon component={<StorageOutlined />} size={20} /> <div className={`absolute bottom-0 left-0 h-2 w-2 border border-light rounded-full ${session.testedSaveState[session.currentSaveState] === DatabaseStatus.tested ? 'bg-success-500' : 'bg-danger-500'}`} /> </div> <p className="ml-2 truncate">{session.saveStates[session.currentSaveState].name}</p> </div> ) : session.saveStates === undefined ? ( <> <LoadingSpinner /> <p className="ml-2">Retrieving databases</p> </> ) : Object.keys(session.saveStates).length === 0 || session.currentSaveState === nilUUID ? ( <> <p className="ml-2">Add your first Database</p> </> ) : ( <> <div className="h-2 w-2 rounded-full bg-secondary-500" /> <p className="ml-2">Select a database</p> </> )} </div> } /> {session.saveStates !== undefined && ( <DropdownItemContainer> <li className="flex items-center p-2 hover:bg-secondary-50 cursor-pointer" onClick={(e) => { e.preventDefault(); setDbSelectionMenuOpen(false); setConnecting(false); setSettingsMenuOpen('add'); }} title="Add new database" > {session.saveStates && Object.keys(session.saveStates).length === 0 ? ( <> <Add /> <p className="ml-2">Add your first database</p> </> ) : ( <> <Add /> <p className="ml-2">Add database</p> </> )} </li> {Object.values(session.saveStates) .filter((save) => save.id !== nilUUID) .map((save) => ( <li key={save.id} className="flex justify-between items-center px-4 py-2 hover:bg-primary-100 gap-2 cursor-pointer" onClick={(e) => { if (save.id !== session.currentSaveState) { e.preventDefault(); setDbSelectionMenuOpen(false); setConnecting(true); dispatch(selectSaveState(save.id)); dispatch(clearQB()); dispatch(clearSchema()); } else { setDbSelectionMenuOpen(false); } }} onMouseEnter={() => setHovered(save.id)} onMouseLeave={() => setHovered(null)} > <Tooltip placement={'left'}> <TooltipTrigger> <div className={`h-[8px] w-[8px] rounded-full shrink-0 ${ session.testedSaveState[save.id] === DatabaseStatus.tested ? 'bg-success-500' : 'bg-danger-500' }`} /> </TooltipTrigger> <TooltipContent> <p> {session.testedSaveState[save.id] === DatabaseStatus.tested ? 'Database connection tested' : 'Something went wrong when trying to connect'} </p> </TooltipContent> </Tooltip> <div className="w-full shrink min-w-0 flex flex-col"> <p className="truncate w-full shrink-0 min-w-0">{save.name}</p> <p className="bg-light text-2xs text-secondary-500 truncate w-fit shrink-0 min-w-0 max-w-full h-full border border-secondary-200 rounded-sm p-0.5"> {save.db.protocol} {save.db.url} </p> </div> <div className={`flex items-center ml-2 ${hovered === save.id ? 'display' : 'invisible'}`}> <div className="text-secondary-700 hover:text-secondary-400 transition-colors duration-300" onClick={(e) => { e.preventDefault(); e.stopPropagation(); setSettingsMenuOpen('update'); setSelectedSaveState(save); }} > <Tooltip> <TooltipTrigger> <Settings /> </TooltipTrigger> <TooltipContent> <p>Change the connection details</p> </TooltipContent> </Tooltip> </div> <div className="text-secondary-700 hover:text-secondary-400 transition-colors duration-300" onClick={(e) => { e.preventDefault(); e.stopPropagation(); setDbSelectionMenuOpen(false); if (session.currentSaveState === save.id) { dispatch(clearQB()); dispatch(clearSchema()); } wsDeleteState(save.id); dispatch(deleteSaveState(save.id)); }} > <Tooltip> <TooltipTrigger> <Delete /> </TooltipTrigger> <TooltipContent> <p>Delete the database</p> </TooltipContent> </Tooltip> </div> </div> </li> ))} </DropdownItemContainer> )} </DropdownContainer> </TooltipProvider> </div> ); }