tianji/src/client/pages/Layout.tsx

177 lines
5.5 KiB
TypeScript
Raw Normal View History

2024-01-07 16:01:49 +00:00
import React, { useState } from 'react';
import { Outlet, useNavigate, useSearchParams } from 'react-router-dom';
2023-08-31 16:11:47 +00:00
import { NavItem } from '../components/NavItem';
2024-01-07 16:01:49 +00:00
import { MobileNavItem } from '../components/MobileNavItem';
2023-08-31 16:11:47 +00:00
import { UserOutlined } from '@ant-design/icons';
2024-01-24 13:26:42 +00:00
import { Button, Divider, Drawer, Dropdown } from 'antd';
import { useUserStore } from '../store/user';
import { useLogout } from '../api/model/user';
2023-12-08 13:49:49 +00:00
import { ColorSchemeSwitcher } from '../components/ColorSchemeSwitcher';
2024-01-24 13:26:42 +00:00
import { version } from '@tianji/shared';
2024-01-07 16:01:49 +00:00
import { useIsMobile } from '../hooks/useIsMobile';
import { RiMenuUnfoldLine } from 'react-icons/ri';
2024-02-11 16:10:38 +00:00
import { useTranslation } from '@i18next-toolkit/react';
import { LanguageSelector } from '../components/LanguageSelector';
2023-08-31 16:11:47 +00:00
export const Layout: React.FC = React.memo(() => {
2023-10-08 11:06:59 +00:00
const [params] = useSearchParams();
const workspaces = useUserStore((state) => {
const userInfo = state.info;
if (userInfo) {
return userInfo.workspaces.map((w) => ({
id: w.workspace.id,
name: w.workspace.name,
role: w.role,
current: userInfo.currentWorkspace?.id === w.workspace.id,
}));
}
return [];
});
2024-01-07 16:01:49 +00:00
const [openDraw, setOpenDraw] = useState(false);
const logout = useLogout();
2024-01-07 16:01:49 +00:00
const isMobile = useIsMobile();
2023-10-08 11:06:59 +00:00
const showHeader = !params.has('hideHeader');
const navigate = useNavigate();
2024-02-11 16:10:38 +00:00
const { t } = useTranslation();
2024-01-07 16:01:49 +00:00
const accountEl = (
<Dropdown
placement="bottomRight"
menu={{
items: [
{
key: 'workspaces',
2024-02-11 16:10:38 +00:00
label: t('Workspaces'),
2024-01-07 16:01:49 +00:00
children: workspaces.map((w) => ({
key: w.id,
label: `${w.name}${w.current ? '(current)' : ''}`,
disabled: w.current,
})),
},
{
key: 'settings',
2024-02-11 16:10:38 +00:00
label: t('Settings'),
onClick: () => {
navigate('/settings');
},
},
2024-01-07 16:01:49 +00:00
{
key: 'logout',
2024-02-11 16:10:38 +00:00
label: t('Logout'),
2024-01-07 16:01:49 +00:00
onClick: () => {
logout();
},
},
{
type: 'divider',
},
{
key: 'version',
label: `v${version}`,
disabled: true,
},
],
}}
>
<Button shape="circle" size="large" icon={<UserOutlined />} />
</Dropdown>
);
2023-08-31 16:11:47 +00:00
return (
2023-12-08 13:49:49 +00:00
<div className="flex flex-col h-full dark:bg-gray-900 dark:text-gray-300">
2023-10-08 11:06:59 +00:00
{showHeader && (
2024-01-07 16:01:49 +00:00
<div className="flex items-center bg-gray-100 dark:bg-gray-800 px-4 sticky top-0 z-20 h-[62px]">
{isMobile && (
<>
<Button
className="mr-2"
icon={<RiMenuUnfoldLine className="anticon" />}
onClick={() => setOpenDraw(true)}
/>
<Drawer
open={openDraw}
onClose={() => setOpenDraw(false)}
placement="left"
closeIcon={false}
>
<div className="flex flex-col h-full pt-12">
<div className="flex-1">
<MobileNavItem
to="/dashboard"
2024-02-11 16:10:38 +00:00
label={t('Dashboard')}
2024-01-07 16:01:49 +00:00
onClick={() => setOpenDraw(false)}
/>
<MobileNavItem
to="/monitor"
2024-02-11 16:10:38 +00:00
label={t('Monitor')}
2024-01-07 16:01:49 +00:00
onClick={() => setOpenDraw(false)}
/>
<MobileNavItem
to="/website"
2024-02-11 16:10:38 +00:00
label={t('Website')}
2024-01-07 16:01:49 +00:00
onClick={() => setOpenDraw(false)}
/>
<MobileNavItem
to="/servers"
2024-02-11 16:10:38 +00:00
label={t('Servers')}
2024-01-07 16:01:49 +00:00
onClick={() => setOpenDraw(false)}
/>
<MobileNavItem
to="/settings"
2024-02-11 16:10:38 +00:00
label={t('Settings')}
2024-01-07 16:01:49 +00:00
onClick={() => setOpenDraw(false)}
/>
</div>
<Divider />
<div className="flex justify-between">
<ColorSchemeSwitcher />
{accountEl}
</div>
</div>
</Drawer>
</>
)}
2023-10-13 13:50:17 +00:00
<div className="px-2 mr-10 font-bold flex items-center">
<img src="/icon.svg" className="w-10 h-10 mr-2" />
2023-12-08 13:49:49 +00:00
<span className="text-xl dark:text-gray-200">Tianji</span>
2023-10-13 13:50:17 +00:00
</div>
2023-08-31 16:11:47 +00:00
2024-01-07 16:01:49 +00:00
{!isMobile && (
<>
<div className="flex gap-8">
2024-02-11 16:10:38 +00:00
<NavItem to="/dashboard" label={t('Dashboard')} />
<NavItem to="/monitor" label={t('Monitor')} />
<NavItem to="/website" label={t('Website')} />
<NavItem to="/servers" label={t('Servers')} />
<NavItem to="/settings" label={t('Settings')} />
2024-01-07 16:01:49 +00:00
</div>
2023-08-31 16:11:47 +00:00
2024-01-07 16:01:49 +00:00
<div className="flex-1" />
2023-12-08 13:49:49 +00:00
2024-01-07 16:01:49 +00:00
<div className="flex gap-2">
2024-02-11 16:10:38 +00:00
<LanguageSelector />
2024-01-07 16:01:49 +00:00
<ColorSchemeSwitcher />
{accountEl}
</div>
</>
)}
2023-08-31 16:11:47 +00:00
</div>
2023-10-08 11:06:59 +00:00
)}
<div className="flex-1 w-full overflow-hidden">
2023-12-12 11:19:50 +00:00
<div className="h-full px-1 sm:px-4 overflow-auto">
<div className="max-w-7xl m-auto">
<Outlet />
</div>
</div>
2023-08-31 16:11:47 +00:00
</div>
</div>
);
});
Layout.displayName = 'Layout';