NestJS backend rewrite; migrate client to FluentUI V9
This commit is contained in:
@ -1,15 +1,53 @@
|
||||
import { AppShell, Avatar, Burger, Button, Flex, Group, Image, Menu, NavLink, rem, Text, useMantineColorScheme } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { useMantineColorScheme } from '@mantine/core';
|
||||
import { Outlet, useNavigate } from 'react-router-dom';
|
||||
import { IconChevronDown, IconLogout, IconSettings, IconMoon, IconSun } from '@tabler/icons-react';
|
||||
import { IconLogout, IconSettings, IconMoon, IconSun, IconMenu2, IconUser } from '@tabler/icons-react';
|
||||
import { getUserData, logout, useAuthStore } from '../store/auth';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { UserData } from '../interfaces/auth';
|
||||
import { pages } from '../constants/app';
|
||||
import { Button, Image, makeStyles, Menu, MenuButton, MenuItem, MenuList, MenuPopover, MenuTrigger, Text } from '@fluentui/react-components';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
display: 'grid',
|
||||
gridTemplateRows: 'min-content auto',
|
||||
height: '100vh',
|
||||
maxHeight: '100vh',
|
||||
overflow: 'hidden'
|
||||
},
|
||||
header: {
|
||||
display: 'flex',
|
||||
maxHeight: '3rem',
|
||||
borderBottom: '1px solid var(--colorNeutralShadowKey)'
|
||||
},
|
||||
|
||||
main: {
|
||||
display: 'flex',
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
navbar: {
|
||||
overflow: 'auto',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
maxWidth: '200px',
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
transition: 'max-width .2s ease-in-out',
|
||||
borderRight: '1px solid var(--colorNeutralShadowKey)'
|
||||
},
|
||||
content: {
|
||||
overflow: 'auto',
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}
|
||||
})
|
||||
|
||||
function DashboardLayout() {
|
||||
const [mobileOpened, { toggle: toggleMobile }] = useDisclosure()
|
||||
const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(false)
|
||||
const navigate = useNavigate()
|
||||
|
||||
const getPageTitle = () => {
|
||||
@ -33,115 +71,201 @@ function DashboardLayout() {
|
||||
|
||||
const { colorScheme, setColorScheme } = useMantineColorScheme();
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
const [navbarOpen, setNavbarOpen] = useState(true)
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
header={{ height: 60 }}
|
||||
navbar={{
|
||||
width: desktopOpened ? 200 : 50,
|
||||
breakpoint: 'sm',
|
||||
collapsed: { mobile: !mobileOpened },
|
||||
}}
|
||||
>
|
||||
<AppShell.Header>
|
||||
<Flex h="100%" px="md" w='100%' align='center' gap='sm'>
|
||||
<Group>
|
||||
<Burger opened={mobileOpened} onClick={toggleMobile} hiddenFrom="sm" size="sm" />
|
||||
<Burger opened={desktopOpened} onClick={toggleDesktop} visibleFrom="sm" size="sm" />
|
||||
</Group>
|
||||
<div className={classes.root}>
|
||||
<div className={classes.header}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
gap: '0.75rem',
|
||||
padding: '0.5rem 0.5rem 0.5rem 0.25rem',
|
||||
}}>
|
||||
<Button appearance='subtle' onClick={() => setNavbarOpen(!navbarOpen)} icon={<IconMenu2 />} />
|
||||
|
||||
<Group w='auto'>
|
||||
<Text fw='600'>{getPageTitle()}</Text>
|
||||
</Group>
|
||||
<Text weight='bold' size={400}>
|
||||
{getPageTitle()}
|
||||
</Text>
|
||||
|
||||
<Group id='header-portal' w='auto' ml='auto'>
|
||||
<div id='header-portal' style={{ marginLeft: 'auto' }}>
|
||||
|
||||
</Group>
|
||||
</div>
|
||||
|
||||
<Group style={{ flexShrink: 0 }}>
|
||||
<Menu
|
||||
width={260}
|
||||
position="bottom-end"
|
||||
transitionProps={{ transition: 'pop-top-right' }}
|
||||
withinPortal
|
||||
>
|
||||
<Menu.Target>
|
||||
<Button variant='transparent'>
|
||||
<Group gap={7}>
|
||||
<Avatar name={`${userData?.name} ${userData?.surname}`} radius="xl" size={30} />
|
||||
<Text fw={500} size="sm" lh={1} mr={3}>
|
||||
{`${userData?.name} ${userData?.surname}`}
|
||||
</Text>
|
||||
<IconChevronDown style={{ width: rem(12), height: rem(12) }} stroke={1.5} />
|
||||
</Group>
|
||||
</Button>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Label>{userData?.login}</Menu.Label>
|
||||
<Menu.Item
|
||||
leftSection={
|
||||
colorScheme === 'dark' ? <IconMoon style={{ width: rem(16), height: rem(16) }} stroke={1.5} /> : <IconSun style={{ width: rem(16), height: rem(16) }} stroke={1.5} />
|
||||
}
|
||||
onClick={() => colorScheme === 'dark' ? setColorScheme('light') : setColorScheme('dark')}
|
||||
>
|
||||
Тема: {colorScheme === 'dark' ? 'тёмная' : 'светлая'}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
leftSection={
|
||||
<IconSettings style={{ width: rem(16), height: rem(16) }} stroke={1.5} />
|
||||
}
|
||||
onClick={() => navigate('/settings')}
|
||||
>
|
||||
Настройки профиля
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
<div style={{ flexShrink: 0 }}>
|
||||
<Menu positioning={{ autoSize: true }}>
|
||||
<MenuTrigger>
|
||||
<MenuButton appearance='transparent' icon={<IconUser />}>{`${userData?.name} ${userData?.surname}`}</MenuButton>
|
||||
</MenuTrigger>
|
||||
|
||||
<MenuPopover>
|
||||
<MenuList>
|
||||
<MenuItem icon={
|
||||
colorScheme === 'dark' ? <IconMoon /> : <IconSun />
|
||||
} onClick={() => colorScheme === 'dark' ? setColorScheme('light') : setColorScheme('dark')}>Тема: {colorScheme === 'dark' ? 'тёмная' : 'светлая'}</MenuItem>
|
||||
<MenuItem icon={<IconSettings />} onClick={() => navigate('/settings')}>Настройки профиля</MenuItem>
|
||||
<MenuItem icon={<IconLogout />} onClick={() => {
|
||||
logout()
|
||||
navigate("/auth/signin")
|
||||
}}
|
||||
leftSection={<IconLogout style={{ width: rem(16), height: rem(16) }} stroke={1.5} />}
|
||||
>
|
||||
Выход
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Item>
|
||||
<Flex gap='sm' align='center'>
|
||||
<Image src={'/logo2.svg'} w={32} />
|
||||
<Text>0.1.0</Text>
|
||||
</Flex>
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
}}>Выход</MenuItem>
|
||||
<MenuItem icon={<Image src={'/logo2.svg'} width={24} />}>
|
||||
0.1.0
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</MenuPopover>
|
||||
</Menu>
|
||||
</Group>
|
||||
</Flex>
|
||||
</AppShell.Header>
|
||||
<AppShell.Navbar style={{ transition: "width 0.2s ease" }}>
|
||||
{pages.filter((page) => page.drawer).filter((page) => page.enabled).map((item) => (
|
||||
<NavLink
|
||||
key={item.path}
|
||||
onClick={() => navigate(item.path)}
|
||||
label={item.label}
|
||||
leftSection={item.icon}
|
||||
active={location.pathname === item.path}
|
||||
style={{ textWrap: 'nowrap' }}
|
||||
// styles={(theme, { active }) => ({
|
||||
// root: {
|
||||
// color: active ? theme.colors.blue[6] : theme.colors.dark[5],
|
||||
// fontWeight: active ? "bold" : "normal",
|
||||
// },
|
||||
// leftSection: {
|
||||
// color: active ? theme.colors.blue[6] : theme.colors.dark[5], // Icon color
|
||||
// }
|
||||
// })}
|
||||
/>
|
||||
))}
|
||||
</AppShell.Navbar>
|
||||
<AppShell.Main>
|
||||
<Flex bg={colorScheme === 'dark' ? undefined : '#E8E8E8'} w={{ sm: desktopOpened ? 'calc(100% - 200px)' : 'calc(100% - 50px)', base: '100%' }} h={'calc(100% - 60px)'} style={{ transition: "width 0.2s ease" }} pos={'fixed'}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.main}>
|
||||
<div className={classes.navbar} style={{
|
||||
maxWidth: navbarOpen ? '200px' : '2.70rem',
|
||||
}}>
|
||||
{pages.filter((page) => page.drawer).filter((page) => page.enabled).map((item) => (
|
||||
<Button key={item.path} style={{ paddingLeft: '0.5rem', flexShrink: 0, flexWrap: 'nowrap', textWrap: 'nowrap', borderRadius: 0 }} appearance='subtle' onClick={() => navigate(item.path)}>
|
||||
<div style={{ display: 'flex', }}>
|
||||
{item.icon}
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
marginLeft: '1rem',
|
||||
}}>
|
||||
{item.label}
|
||||
</div>
|
||||
|
||||
</Button>
|
||||
// <NavItem style={{ flexShrink: 0, flexWrap: 'nowrap', textWrap: 'nowrap' }} onClick={() => navigate(item.path)} icon={item.icon} value={item.path}>
|
||||
// {item.label}
|
||||
// </NavItem>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className={classes.content}>
|
||||
<Outlet />
|
||||
</Flex>
|
||||
</AppShell.Main>
|
||||
</AppShell>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
// return (
|
||||
// <AppShell
|
||||
// header={{ height: 60 }}
|
||||
// navbar={{
|
||||
// width: desktopOpened ? 200 : 50,
|
||||
// breakpoint: 'sm',
|
||||
// collapsed: { mobile: !mobileOpened },
|
||||
// }}
|
||||
// >
|
||||
// <AppShell.Header>
|
||||
// <Flex h="100%" px="md" w='100%' align='center' gap='sm'>
|
||||
// <Group>
|
||||
// <Burger opened={mobileOpened} onClick={toggleMobile} hiddenFrom="sm" size="sm" />
|
||||
// <Burger opened={desktopOpened} onClick={toggleDesktop} visibleFrom="sm" size="sm" />
|
||||
// </Group>
|
||||
|
||||
// <Group w='auto'>
|
||||
// <Text fw='600'>{getPageTitle()}</Text>
|
||||
// </Group>
|
||||
|
||||
// <Group id='header-portal' w='auto' ml='auto'>
|
||||
|
||||
// </Group>
|
||||
|
||||
// <Group style={{ flexShrink: 0 }}>
|
||||
// <Menu
|
||||
// width={260}
|
||||
// position="bottom-end"
|
||||
// transitionProps={{ transition: 'pop-top-right' }}
|
||||
// withinPortal
|
||||
// >
|
||||
// <Menu.Target>
|
||||
// <Button variant='transparent'>
|
||||
// <Group gap={7}>
|
||||
// <Avatar name={`${userData?.name} ${userData?.surname}`} radius="xl" size={30} />
|
||||
// <Text fw={500} size="sm" lh={1} mr={3}>
|
||||
// {`${userData?.name} ${userData?.surname}`}
|
||||
// </Text>
|
||||
// <IconChevronDown style={{ width: rem(12), height: rem(12) }} stroke={1.5} />
|
||||
// </Group>
|
||||
// </Button>
|
||||
// </Menu.Target>
|
||||
// <Menu.Dropdown>
|
||||
// <Menu.Label>{userData?.login}</Menu.Label>
|
||||
// <Menu.Item
|
||||
// leftSection={
|
||||
// colorScheme === 'dark' ? <IconMoon style={{ width: rem(16), height: rem(16) }} stroke={1.5} /> : <IconSun style={{ width: rem(16), height: rem(16) }} stroke={1.5} />
|
||||
// }
|
||||
// onClick={() => colorScheme === 'dark' ? setColorScheme('light') : setColorScheme('dark')}
|
||||
// >
|
||||
// Тема: {colorScheme === 'dark' ? 'тёмная' : 'светлая'}
|
||||
// </Menu.Item>
|
||||
// <Menu.Item
|
||||
// leftSection={
|
||||
// <IconSettings style={{ width: rem(16), height: rem(16) }} stroke={1.5} />
|
||||
// }
|
||||
// onClick={() => navigate('/settings')}
|
||||
// >
|
||||
// Настройки профиля
|
||||
// </Menu.Item>
|
||||
// <Menu.Item
|
||||
// onClick={() => {
|
||||
// logout()
|
||||
// navigate("/auth/signin")
|
||||
// }}
|
||||
// leftSection={<IconLogout style={{ width: rem(16), height: rem(16) }} stroke={1.5} />}
|
||||
// >
|
||||
// Выход
|
||||
// </Menu.Item>
|
||||
|
||||
// <Menu.Item>
|
||||
// <Flex gap='sm' align='center'>
|
||||
// <Image src={'/logo2.svg'} w={32} />
|
||||
// <Text>0.1.0</Text>
|
||||
// </Flex>
|
||||
// </Menu.Item>
|
||||
// </Menu.Dropdown>
|
||||
// </Menu>
|
||||
// </Group>
|
||||
// </Flex>
|
||||
// </AppShell.Header>
|
||||
// <AppShell.Navbar style={{ transition: "width 0.2s ease" }}>
|
||||
// {pages.filter((page) => page.drawer).filter((page) => page.enabled).map((item) => (
|
||||
// <NavLink
|
||||
// key={item.path}
|
||||
// onClick={() => navigate(item.path)}
|
||||
// label={item.label}
|
||||
// leftSection={item.icon}
|
||||
// active={location.pathname === item.path}
|
||||
// style={{ textWrap: 'nowrap' }}
|
||||
// // styles={(theme, { active }) => ({
|
||||
// // root: {
|
||||
// // color: active ? theme.colors.blue[6] : theme.colors.dark[5],
|
||||
// // fontWeight: active ? "bold" : "normal",
|
||||
// // },
|
||||
// // leftSection: {
|
||||
// // color: active ? theme.colors.blue[6] : theme.colors.dark[5], // Icon color
|
||||
// // }
|
||||
// // })}
|
||||
// />
|
||||
// ))}
|
||||
// </AppShell.Navbar>
|
||||
// <AppShell.Main>
|
||||
// <Flex bg={colorScheme === 'dark' ? undefined : '#E8E8E8'} w={{ sm: desktopOpened ? 'calc(100% - 200px)' : 'calc(100% - 50px)', base: '100%' }} h={'calc(100% - 60px)'} style={{ transition: "width 0.2s ease" }} pos={'fixed'}>
|
||||
// <Outlet />
|
||||
// </Flex>
|
||||
// </AppShell.Main>
|
||||
// </AppShell>
|
||||
// )
|
||||
}
|
||||
|
||||
export default DashboardLayout
|
@ -1,10 +1,21 @@
|
||||
import { Flex } from "@mantine/core";
|
||||
import { makeStyles } from "@fluentui/react-components";
|
||||
import { Outlet } from "react-router-dom";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
height: '100%',
|
||||
width: '100%'
|
||||
}
|
||||
})
|
||||
|
||||
export default function MainLayout() {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Flex align='center' justify='center' h='100%' w='100%'>
|
||||
<div className={classes.root}>
|
||||
<Outlet />
|
||||
</Flex>
|
||||
</div>
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user