This commit is contained in:
2024-03-03 07:23:41 +03:00
parent d43c0a5839
commit 0db252bb27
57 changed files with 1707 additions and 105 deletions

View File

@@ -0,0 +1,34 @@
.control {
font-weight: 500;
display: block;
width: 100%;
padding: var(--mantine-spacing-xs) var(--mantine-spacing-md);
color: var(--mantine-color-text);
font-size: var(--mantine-font-size-sm);
@mixin hover {
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
}
}
.link {
font-weight: 500;
display: block;
text-decoration: none;
padding: var(--mantine-spacing-xs) var(--mantine-spacing-md);
padding-left: var(--mantine-spacing-md);
margin-left: var(--mantine-spacing-xl);
font-size: var(--mantine-font-size-sm);
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
border-left: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
@mixin hover {
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
}
}
.chevron {
transition: transform 200ms ease;
}

View File

@@ -0,0 +1,73 @@
import {useState} from 'react';
import {Box, Collapse, Group, rem, ThemeIcon, UnstyledButton} from '@mantine/core';
import {IconCalendarStats, IconChevronRight} from '@tabler/icons-react';
import classes from './LinksGroup.module.css';
import {Link} from "@tanstack/react-router";
interface LinksGroupProps {
icon: React.FC<any>;
label: string;
initiallyOpened?: boolean;
links?: { label: string; link: string }[];
}
export function LinksGroup({icon: Icon, label, initiallyOpened, links}: LinksGroupProps) {
const hasLinks = Array.isArray(links);
const [opened, setOpened] = useState(initiallyOpened || false);
const items = (hasLinks ? links : []).map((link) => (
<Link to={link.link}
className={classes.link}
key={link.label}
>
{link.label}
</Link>
));
return (
<>
<UnstyledButton onClick={() => setOpened((o) => !o)} className={classes.control}>
<Group justify="space-between" gap={0}>
<Box style={{display: 'flex', alignItems: 'center'}}>
<ThemeIcon variant="light" size={30}>
<Icon style={{width: rem(18), height: rem(18)}}/>
</ThemeIcon>
<Box ml="md">{label}</Box>
</Box>
{hasLinks && (
<IconChevronRight
className={classes.chevron}
stroke={1.5}
style={{
width: rem(16),
height: rem(16),
transform: opened ? 'rotate(-90deg)' : 'none',
}}
/>
)}
</Group>
</UnstyledButton>
{hasLinks ? <Collapse in={opened}>{items}</Collapse> : null}
</>
);
}
const mockdata = {
label: 'Releases',
icon: IconCalendarStats,
links: [
{label: 'Upcoming releases', link: '/'},
{label: 'Previous releases', link: '/'},
{label: 'Releases schedule', link: '/'},
],
};
export function NavbarLinksGroup() {
return (
<Box mih={220} p="md">
<LinksGroup {...mockdata} />
</Box>
);
}

View File

@@ -0,0 +1,37 @@
export function Logo(props: React.ComponentPropsWithoutRef<'svg'>) {
return (
<svg {...props} version="1.0" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024.000000 1024.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,1024.000000) scale(0.100000,-0.100000)"
fill="#2a75ec" stroke="none">
<path d="M2600 7290 l0 -760 690 0 690 0 0 196 0 195 728 -4 c613 -3 743 -7
831 -20 57 -10 106 -15 108 -13 7 7 341 -80 358 -93 8 -7 79 -34 95 -36 28 -4
293 -151 298 -166 2 -5 8 -9 13 -9 9 0 75 -51 177 -138 44 -38 120 -123 218
-245 73 -91 215 -390 230 -484 3 -21 8 -42 11 -46 20 -33 46 -190 58 -355 12
-165 12 -219 0 -385 -12 -164 -38 -321 -58 -354 -3 -4 -8 -25 -11 -46 -15 -95
-157 -394 -230 -484 -96 -119 -128 -156 -176 -203 -65 -65 -206 -180 -219
-180 -5 0 -11 -4 -13 -9 -7 -20 -253 -151 -333 -177 -27 -9 -55 -20 -60 -25
-6 -4 -45 -17 -88 -28 -42 -12 -111 -30 -152 -41 -42 -11 -88 -21 -103 -22
-15 0 -67 -8 -117 -16 -72 -12 -238 -16 -827 -19 l-738 -4 0 245 0 246 -687
-2 -688 -3 -3 -808 -2 -807 1487 0 c1611 0 1542 -2 1908 56 88 14 207 37 265
51 108 27 369 106 405 123 11 5 52 21 90 36 168 65 524 255 588 314 11 11 27
20 34 20 7 0 13 3 13 8 0 4 39 37 88 72 160 119 365 330 507 525 58 79 122
173 132 195 4 8 22 39 41 68 18 29 35 61 37 70 2 10 17 41 34 70 17 29 31 57
31 63 0 5 11 32 25 59 28 55 78 194 102 280 8 30 26 94 39 141 13 48 28 120
34 160 5 41 17 121 27 179 26 154 25 728 -1 880 -9 58 -22 137 -27 175 -5 39
-20 111 -33 160 -13 50 -30 113 -37 140 -7 28 -23 74 -36 104 -12 29 -23 60
-23 67 0 8 -16 48 -35 90 -19 41 -35 79 -35 84 0 5 -14 33 -31 62 -17 29 -33
61 -35 71 -2 11 -10 27 -18 36 -7 9 -24 36 -36 60 -12 24 -26 49 -32 55 -8 9
-40 57 -103 155 -5 9 -15 20 -21 26 -6 5 -40 48 -76 95 -127 165 -291 325
-473 459 -38 29 -83 62 -98 74 -16 12 -40 29 -55 37 -15 8 -67 39 -115 69 -99
59 -339 177 -443 216 -245 93 -524 167 -774 205 -335 51 -306 50 -1877 50
l-1473 0 0 -760z"/>
<path d="M1730 5175 l0 -1095 1580 0 1580 0 0 1095 0 1095 -1580 0 -1580 0 0
-1095z"/>
</g>
</svg>
)
}

View File

@@ -0,0 +1,36 @@
.navbar {
width: rem(80px);
height: 100%;
padding: var(--mantine-spacing-md);
display: flex;
flex-direction: column;
border-right: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
}
.navbarMain {
flex: 1;
margin-top: rem(50px);
}
.link {
width: rem(50px);
height: rem(50px);
border-radius: var(--mantine-radius-md);
display: flex;
align-items: center;
justify-content: center;
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
&:hover {
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-5));
}
&[data-active] {
&,
&:hover {
background-color: var(--mantine-color-blue-light);
color: var(--mantine-color-blue-light-color);
}
}
}

View File

@@ -0,0 +1,89 @@
import {Center, Image, rem, Stack, Tooltip, UnstyledButton, useMantineColorScheme} from '@mantine/core';
import {IconCash, IconHome2, IconLogout, IconMoon, IconSun,} from '@tabler/icons-react';
import classes from './Navbar.module.css';
import {useAppDispatch} from "../../redux/store.ts";
import {logout} from "../../features/authSlice.ts";
import {useNavigate, useRouterState} from "@tanstack/react-router";
interface NavbarLinkProps {
icon: typeof IconHome2;
label?: string;
active?: boolean;
href: string;
onClick?(navlink: NavbarLinkProps): void;
index: number;
}
function NavbarLink(props: NavbarLinkProps) {
const {icon: Icon, label, active, onClick} = props;
return (
<Tooltip display={!label ? "none" : "flex"} label={label} position="right" transitionProps={{duration: 0}}>
<UnstyledButton onClick={() => onClick && onClick(props)}
className={classes.link}
data-active={active || undefined}>
<Icon style={{width: rem(20), height: rem(20)}} stroke={1.5}/>
</UnstyledButton>
</Tooltip>
);
}
const mockdata = [
{
icon: IconHome2,
label: 'Главная',
href: '/'
},
{
icon: IconCash,
label: 'Сделки',
href: '/leads'
},
];
export function Navbar() {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const router = useRouterState();
const {colorScheme, toggleColorScheme} = useMantineColorScheme({keepTransitions: false});
const onLogoutClick = () => {
dispatch(logout());
navigate({to: '/login'});
}
const onNavlinkClick = (props: NavbarLinkProps) => {
navigate({to: props.href});
}
const links = mockdata.map((link, index) => (
<NavbarLink
{...link}
index={index}
key={link.label}
active={router.location.pathname === link.href}
onClick={onNavlinkClick}
/>
));
return (
<nav className={classes.navbar}>
<Center>
<Image
style={{filter: "drop-shadow(0 0 30px #fff)"}}
src={colorScheme == "dark" ? "/icons/logo-light.png" : "/icons/logo.png"}
/>
</Center>
<div className={classes.navbarMain}>
<Stack justify="center" gap={rem(10)}>
{links}
</Stack>
</div>
<Stack justify="center" gap={0}>
<NavbarLink label={"Сменить тему"} onClick={toggleColorScheme} icon={colorScheme == "dark" ? IconSun : IconMoon} href={"#"} index={-1}/>
<NavbarLink index={-1} href={"#"} onClick={onLogoutClick} icon={IconLogout} label="Выйти"/>
</Stack>
</nav>
);
}

View File

@@ -0,0 +1,10 @@
.user {
display: block;
width: 100%;
padding: var(--mantine-spacing-md);
color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
@mixin hover {
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-8));
}
}

View File

@@ -0,0 +1,28 @@
import { UnstyledButton, Group, Avatar, Text, rem } from '@mantine/core';
import { IconChevronRight } from '@tabler/icons-react';
import classes from './UserButton.module.css';
export function UserButton() {
return (
<UnstyledButton className={classes.user}>
<Group>
<Avatar
src="https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-8.png"
radius="xl"
/>
<div style={{ flex: 1 }}>
<Text size="sm" fw={500}>
Harriette Spoonlicker
</Text>
<Text c="dimmed" size="xs">
hspoonlicker@outlook.com
</Text>
</div>
<IconChevronRight style={{ width: rem(14), height: rem(14) }} stroke={1.5} />
</Group>
</UnstyledButton>
);
}

View File

@@ -0,0 +1,25 @@
import {IconAdjustments, IconGauge} from "@tabler/icons-react";
export const NavbarLinks = [
{
label: 'Главная',
icon: IconGauge,
links: [
{
label: "123",
link: "/login"
},
],
},
{
label: 'Настройки',
icon: IconAdjustments,
links:[
{
label: "Профиль"
}
]
},
];