|
import { useState, useRef, useEffect } from 'react' |
|
import PropTypes from 'prop-types' |
|
import { useSelector, useDispatch } from 'react-redux' |
|
import { useNavigate } from 'react-router-dom' |
|
|
|
|
|
import { useTheme } from '@mui/material/styles' |
|
import { |
|
Box, |
|
ButtonBase, |
|
Avatar, |
|
ClickAwayListener, |
|
Divider, |
|
List, |
|
ListItemButton, |
|
ListItemIcon, |
|
ListItemText, |
|
Paper, |
|
Popper, |
|
Typography |
|
} from '@mui/material' |
|
|
|
|
|
import PerfectScrollbar from 'react-perfect-scrollbar' |
|
|
|
|
|
import MainCard from 'ui-component/cards/MainCard' |
|
import Transitions from 'ui-component/extended/Transitions' |
|
import { BackdropLoader } from 'ui-component/loading/BackdropLoader' |
|
import AboutDialog from 'ui-component/dialog/AboutDialog' |
|
|
|
|
|
import { IconLogout, IconSettings, IconFileExport, IconFileDownload, IconInfoCircle } from '@tabler/icons' |
|
|
|
|
|
import databaseApi from 'api/database' |
|
|
|
import { SET_MENU } from 'store/actions' |
|
|
|
import './index.css' |
|
|
|
|
|
|
|
const ProfileSection = ({ username, handleLogout }) => { |
|
const theme = useTheme() |
|
const dispatch = useDispatch() |
|
const navigate = useNavigate() |
|
|
|
const customization = useSelector((state) => state.customization) |
|
|
|
const [open, setOpen] = useState(false) |
|
const [loading, setLoading] = useState(false) |
|
const [aboutDialogOpen, setAboutDialogOpen] = useState(false) |
|
|
|
const anchorRef = useRef(null) |
|
const uploadRef = useRef(null) |
|
|
|
const handleClose = (event) => { |
|
if (anchorRef.current && anchorRef.current.contains(event.target)) { |
|
return |
|
} |
|
setOpen(false) |
|
} |
|
|
|
const handleToggle = () => { |
|
setOpen((prevOpen) => !prevOpen) |
|
} |
|
|
|
const handleExportDB = async () => { |
|
setOpen(false) |
|
try { |
|
const response = await databaseApi.getExportDatabase() |
|
const exportItems = response.data |
|
let dataStr = JSON.stringify(exportItems) |
|
let dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr) |
|
|
|
let exportFileDefaultName = `DB.json` |
|
|
|
let linkElement = document.createElement('a') |
|
linkElement.setAttribute('href', dataUri) |
|
linkElement.setAttribute('download', exportFileDefaultName) |
|
linkElement.click() |
|
} catch (e) { |
|
console.error(e) |
|
} |
|
} |
|
|
|
const handleFileUpload = (e) => { |
|
if (!e.target.files) return |
|
|
|
const file = e.target.files[0] |
|
const reader = new FileReader() |
|
reader.onload = async (evt) => { |
|
if (!evt?.target?.result) { |
|
return |
|
} |
|
const { result } = evt.target |
|
|
|
if (result.includes(`"chatmessages":[`) && result.includes(`"chatflows":[`) && result.includes(`"apikeys":[`)) { |
|
dispatch({ type: SET_MENU, opened: false }) |
|
setLoading(true) |
|
|
|
try { |
|
await databaseApi.createLoadDatabase(JSON.parse(result)) |
|
setLoading(false) |
|
navigate('/', { replace: true }) |
|
navigate(0) |
|
} catch (e) { |
|
console.error(e) |
|
setLoading(false) |
|
} |
|
} else { |
|
alert('Incorrect Flowise Database Format') |
|
} |
|
} |
|
reader.readAsText(file) |
|
} |
|
|
|
const prevOpen = useRef(open) |
|
useEffect(() => { |
|
if (prevOpen.current === true && open === false) { |
|
anchorRef.current.focus() |
|
} |
|
|
|
prevOpen.current = open |
|
}, [open]) |
|
|
|
return ( |
|
<> |
|
<ButtonBase ref={anchorRef} sx={{ borderRadius: '12px', overflow: 'hidden' }}> |
|
<Avatar |
|
variant='rounded' |
|
sx={{ |
|
...theme.typography.commonAvatar, |
|
...theme.typography.mediumAvatar, |
|
transition: 'all .2s ease-in-out', |
|
background: theme.palette.secondary.light, |
|
color: theme.palette.secondary.dark, |
|
'&:hover': { |
|
background: theme.palette.secondary.dark, |
|
color: theme.palette.secondary.light |
|
} |
|
}} |
|
onClick={handleToggle} |
|
color='inherit' |
|
> |
|
<IconSettings stroke={1.5} size='1.3rem' /> |
|
</Avatar> |
|
</ButtonBase> |
|
<Popper |
|
placement='bottom-end' |
|
open={open} |
|
anchorEl={anchorRef.current} |
|
role={undefined} |
|
transition |
|
disablePortal |
|
popperOptions={{ |
|
modifiers: [ |
|
{ |
|
name: 'offset', |
|
options: { |
|
offset: [0, 14] |
|
} |
|
} |
|
] |
|
}} |
|
> |
|
{({ TransitionProps }) => ( |
|
<Transitions in={open} {...TransitionProps}> |
|
<Paper> |
|
<ClickAwayListener onClickAway={handleClose}> |
|
<MainCard border={false} elevation={16} content={false} boxShadow shadow={theme.shadows[16]}> |
|
{username && ( |
|
<Box sx={{ p: 2 }}> |
|
<Typography component='span' variant='h4'> |
|
{username} |
|
</Typography> |
|
</Box> |
|
)} |
|
<PerfectScrollbar style={{ height: '100%', maxHeight: 'calc(100vh - 250px)', overflowX: 'hidden' }}> |
|
<Box sx={{ p: 2 }}> |
|
<Divider /> |
|
<List |
|
component='nav' |
|
sx={{ |
|
width: '100%', |
|
maxWidth: 250, |
|
minWidth: 200, |
|
backgroundColor: theme.palette.background.paper, |
|
borderRadius: '10px', |
|
[theme.breakpoints.down('md')]: { |
|
minWidth: '100%' |
|
}, |
|
'& .MuiListItemButton-root': { |
|
mt: 0.5 |
|
} |
|
}} |
|
> |
|
<ListItemButton |
|
sx={{ borderRadius: `${customization.borderRadius}px` }} |
|
onClick={() => { |
|
setOpen(false) |
|
uploadRef.current.click() |
|
}} |
|
> |
|
<ListItemIcon> |
|
<IconFileDownload stroke={1.5} size='1.3rem' /> |
|
</ListItemIcon> |
|
<ListItemText primary={<Typography variant='body2'>Load Database</Typography>} /> |
|
</ListItemButton> |
|
<ListItemButton |
|
sx={{ borderRadius: `${customization.borderRadius}px` }} |
|
onClick={handleExportDB} |
|
> |
|
<ListItemIcon> |
|
<IconFileExport stroke={1.5} size='1.3rem' /> |
|
</ListItemIcon> |
|
<ListItemText primary={<Typography variant='body2'>Export Database</Typography>} /> |
|
</ListItemButton> |
|
<ListItemButton |
|
sx={{ borderRadius: `${customization.borderRadius}px` }} |
|
onClick={() => { |
|
setOpen(false) |
|
setAboutDialogOpen(true) |
|
}} |
|
> |
|
<ListItemIcon> |
|
<IconInfoCircle stroke={1.5} size='1.3rem' /> |
|
</ListItemIcon> |
|
<ListItemText primary={<Typography variant='body2'>About Flowise</Typography>} /> |
|
</ListItemButton> |
|
{localStorage.getItem('username') && localStorage.getItem('password') && ( |
|
<ListItemButton |
|
sx={{ borderRadius: `${customization.borderRadius}px` }} |
|
onClick={handleLogout} |
|
> |
|
<ListItemIcon> |
|
<IconLogout stroke={1.5} size='1.3rem' /> |
|
</ListItemIcon> |
|
<ListItemText primary={<Typography variant='body2'>Logout</Typography>} /> |
|
</ListItemButton> |
|
)} |
|
</List> |
|
</Box> |
|
</PerfectScrollbar> |
|
</MainCard> |
|
</ClickAwayListener> |
|
</Paper> |
|
</Transitions> |
|
)} |
|
</Popper> |
|
<input ref={uploadRef} type='file' hidden accept='.json' onChange={(e) => handleFileUpload(e)} /> |
|
<BackdropLoader open={loading} /> |
|
<AboutDialog show={aboutDialogOpen} onCancel={() => setAboutDialogOpen(false)} /> |
|
</> |
|
) |
|
} |
|
|
|
ProfileSection.propTypes = { |
|
username: PropTypes.string, |
|
handleLogout: PropTypes.func |
|
} |
|
|
|
export default ProfileSection |
|
|