Files
kevisual-center-v1/src/modules/layout/LayoutUser.tsx
2025-05-21 18:21:52 +08:00

165 lines
5.1 KiB
TypeScript

import { useShallow } from 'zustand/react/shallow';
import { useLayoutStore } from './store';
import clsx from 'clsx';
import { Menu, MenuItem, Tooltip } from '@mui/material';
import { Button } from '@mui/material';
import { message } from '@/modules/message';
import SmileOutlined from '@ant-design/icons/SmileOutlined';
import SwitcherOutlined from '@ant-design/icons/SwitcherOutlined';
import { useEffect, useMemo, useState } from 'react';
import { query, queryLogin } from '../query';
import { useNewNavigate } from '../navicate';
import { LogOut, Map, SquareUser, Users, X, ArrowDownLeftFromSquareIcon } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import React from 'react';
export const LayoutUser = () => {
const { open, setOpen, isAdmin, ...store } = useLayoutStore(
useShallow((state) => ({
open: state.openUser, //
setOpen: state.setOpenUser,
me: state.me,
switchOrg: state.switchOrg,
isAdmin: state.isAdmin,
})),
);
const navigate = useNewNavigate();
const { t } = useTranslation();
const items = useMemo(() => {
const orgs = store.me?.orgs || [];
return orgs.map((item) => {
return {
label: item,
key: item,
icon: <SmileOutlined />,
};
});
}, [store.me]);
const menu = useMemo(() => {
const orgs = store.me?.orgs || [];
const hasOrg = orgs.length > 0;
const items = [
{
title: t('Your profile'),
icon: <SquareUser size={16} />,
link: '/user/profile',
},
{
title: t('Your orgs'),
icon: <SwitcherOutlined />,
link: '/org/edit/list',
isOrg: true,
},
{
title: t('Site Map'),
icon: <Map size={16} />,
link: '/map',
},
{
title: t('Domain'),
icon: <ArrowDownLeftFromSquareIcon size={16} />,
link: '/domain/edit/list',
isAdmin: true,
},
];
return items.filter((item) => {
if (item.isOrg) {
return hasOrg;
}
if (item.isAdmin) {
return isAdmin;
}
return true;
});
}, [store.me]);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div className={clsx('w-full h-full absolute z-20 no-drag text-primary', !open && 'hidden')}>
<div
className='w-full absolute h-full opacity-60 z-0'
onClick={() => {
setOpen(false);
}}></div>
<div className='w-[400px] bg-amber-900 transition-all duration-300 h-full absolute top-0 right-0 rounded-l-lg'>
<div className='flex justify-between p-6 mt-4 font-bold items-center border-b'>
<div className='flex items-center gap-2'>
{t('User')}: <span className='text-primary'>{store.me?.username}</span>
</div>
<div className='flex gap-4'>
{items.length > 0 && (
<Tooltip title={t('Switch Org')}>
<Button aria-controls='switch-org-menu' aria-haspopup='true' onClick={handleClick}>
<Users />
</Button>
</Tooltip>
)}
<Button onClick={() => setOpen(false)}>
<X />
</Button>
</div>
</div>
<div className='mt-3 font-medium'>
{menu.map((item, index) => {
return (
<div
key={index}
className='flex items-center p-4 hover:bg-secondary hover:text-white cursor-pointer'
onClick={() => {
if (item.link) {
navigate(item.link);
setOpen(false);
} else {
message.info('Coming soon');
}
}}>
<div className='mr-4'>{item.icon}</div>
<div>{item.title}</div>
</div>
);
})}
</div>
<div
className='flex items-center p-4 hover:bg-secondary hover:text-white cursor-pointer'
onClick={async () => {
const res = await queryLogin.logout();
// console.log(res);
if (res.success) {
window.open('/user/login', '_self');
} else {
message.error(res.message || 'Logout failed');
}
}}>
<div className='mr-4'>
<LogOut size={16} />
</div>
<div>{t('Login Out')}</div>
</div>
</div>
<Menu id='simple-menu' anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
{items.map((item, index) => {
return (
<MenuItem
key={index}
onClick={() => {
store.switchOrg(item.key, 'org');
handleClose();
}}>
<div className='mr-4'>{item.icon}</div>
<div>{item.label}</div>
</MenuItem>
);
})}
</Menu>
</div>
);
};