This commit is contained in:
2024-07-20 09:32:01 +03:00
parent 5c6e7cf5f5
commit 54c9ca8908
48 changed files with 1057 additions and 87 deletions

View File

@@ -0,0 +1,34 @@
import {forwardRef, RefObject, useContext, useRef} from "react";
import {getRouterContext, Outlet} from "@tanstack/react-router";
import {motion, useIsPresent} from "framer-motion";
import {cloneDeep} from "lodash";
const AnimatedOutlet = forwardRef<HTMLDivElement>((_, ref) => {
const RouterContext = getRouterContext();
const routerContext = useContext(RouterContext);
const renderedContext = useRef(routerContext);
const isPresent = useIsPresent();
if (isPresent) {
renderedContext.current = cloneDeep(routerContext);
}
return (
<motion.div ref={ref}
initial={{x: "-100%"}}
animate={{x: 0}}
transition={{duration: 0.3}}
onAnimationComplete={()=>{
(ref as RefObject<HTMLDivElement>).current?.style.removeProperty("transform")
}}
>
<RouterContext.Provider value={renderedContext.current}>
<Outlet/>
</RouterContext.Provider>
</motion.div>
);
});
export default AnimatedOutlet

View File

@@ -26,7 +26,7 @@ export type BaseTableRef<T extends MRT_RowData> = {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
export const BaseTable = forwardRef<BaseTableRef<never>, Props<never>>((props, ref) => {
const {data, columns, restProps, striped, onSelectionChange} = props;
const {data, columns, restProps, striped = true, onSelectionChange} = props;
const table = useMantineReactTable({
localization: MRT_Localization_RU,

View File

@@ -2,7 +2,7 @@ import {Center, Image, rem, Stack, Tooltip, UnstyledButton, useMantineColorSchem
import {
IconBarcode,
IconBox,
IconCash,
IconCash, IconDashboard,
IconFileBarcode,
IconHome2,
IconLogout,
@@ -76,7 +76,7 @@ export function Navbar() {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const router = useRouterState();
const {colorScheme, toggleColorScheme} = useMantineColorScheme({keepTransitions: false});
const {colorScheme, toggleColorScheme} = useMantineColorScheme({keepTransitions: true});
const onLogoutClick = () => {
dispatch(logout());
navigate({to: '/login'});
@@ -110,6 +110,12 @@ export function Navbar() {
</div>
<Stack w={"100%"} justify="center" gap={0}>
<NavbarLink icon={IconDashboard}
href={"/admin"}
index={-1}
label={"Панель администратора"}
onClick={() => onNavlinkClick({href: "/admin", index: -1, icon: IconDashboard})}
/>
<NavbarLink label={"Сменить тему"} onClick={toggleColorScheme}
icon={colorScheme == "dark" ? IconSun : IconMoon} href={"#"} index={-1}/>
<NavbarLink index={-1} href={"#"} onClick={onLogoutClick} icon={IconLogout} label="Выйти"/>

View File

@@ -1,35 +1,55 @@
import {Select, SelectProps} from "@mantine/core";
import {useEffect, useMemo, useState} from "react";
import {ObjectWithNameAndId} from "../../types/utils.ts";
import {groupBy, omit} from "lodash";
interface ObjectWithIdAndName {
id: number,
name: string
}
export type SelectObjectType<T extends ObjectWithNameAndId> = T;
export type SelectObjectType<T> = T;
type ControlledValueProps<T extends ObjectWithNameAndId> = {
type ControlledValueProps<T> = {
value: SelectObjectType<T>,
onChange: (value: SelectObjectType<T>) => void;
}
type CustomLabelAndKeyProps<T> = {
getLabelFn: (item: SelectObjectType<T>) => string;
getValueFn: (item: SelectObjectType<T>) => string;
}
type RestProps<T extends ObjectWithNameAndId> = {
type RestProps<T> = {
defaultValue?: SelectObjectType<T>
onChange: (value: SelectObjectType<T>) => void;
data: SelectObjectType<T>[];
groupBy?: (item: SelectObjectType<T>) => string;
filterBy?: (item: SelectObjectType<T>) => boolean;
};
const defaultGetLabelFn = <T extends { name: string }>(item: T): string => {
return item.name;
}
export type ObjectSelectProps<T extends ObjectWithNameAndId> =
const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
return item.id.toString();
}
export type ObjectSelectProps<T> =
(RestProps<T> & Partial<ControlledValueProps<T>>)
& Omit<SelectProps, 'value' | 'onChange' | 'data'>;
& Omit<SelectProps, 'value' | 'onChange' | 'data'>
& (T extends ObjectWithIdAndName ? Partial<CustomLabelAndKeyProps<T>> : CustomLabelAndKeyProps<T>)
const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<T>) => {
const ObjectSelect = <T, >(props: ObjectSelectProps<T>) => {
const isControlled = 'value' in props;
const haveGetValueFn = 'getValueFn' in props;
const haveGetLabelFn = 'getLabelFn' in props;
const [internalValue, setInternalValue] = useState<SelectObjectType<T> | undefined>(props.defaultValue);
const value = isControlled ? props.value : internalValue;
const getValueFn = (haveGetValueFn && props.getValueFn) || defaultGetValueFn;
const getLabelFn = (haveGetLabelFn && props.getLabelFn) || defaultGetLabelFn;
const data = useMemo(() => {
const propsData = props.filterBy ? props.data.filter(props.filterBy) : props.data;
if (props.groupBy) {
@@ -38,21 +58,21 @@ const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<
return Object.entries(groupedData).map(([group, items]) => ({
group,
items: items.map(item => ({
label: item.name,
value: item.id.toString()
label: getLabelFn(item),
value: getValueFn(item)
}))
}));
} else {
return propsData.map(item => ({
label: item.name,
value: item.id.toString()
label: getLabelFn(item),
value: getValueFn(item)
}));
}
}, [props.data, props.groupBy]);
const handleOnChange = (event: string | null) => {
if (!event) return;
const object = props.data.find(item => parseInt(event) == item.id);
const object = props.data.find(item => event == getValueFn(item));
if (!object) return;
if (isControlled) {
props.onChange(object);
@@ -65,11 +85,11 @@ const ObjectSelect = <T extends ObjectWithNameAndId, >(props: ObjectSelectProps<
if (isControlled || !internalValue) return;
props.onChange(internalValue);
}, [internalValue]);
const restProps = omit(props, ['filterBy', 'groupBy']);
const restProps = omit(props, ['filterBy', 'groupBy', 'getValueFn', 'getLabelFn']);
return (
<Select
{...restProps}
value={value?.id.toString()}
value={value && getValueFn(value)}
onChange={handleOnChange}
data={data}
/>