feat: projects redux storage and select

This commit is contained in:
2025-07-30 17:44:30 +04:00
parent b8d431ae99
commit cb168b6415
10 changed files with 226 additions and 2 deletions

View File

@ -0,0 +1,110 @@
import { useEffect, useMemo, useState } from "react";
import { groupBy, omit } from "lodash";
import { Select, SelectProps } from "@mantine/core";
interface ObjectWithIdAndName {
id: number;
name: string;
}
export type SelectObjectType<T> = T;
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> = {
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;
};
const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
if (!item) return item;
return item.id.toString();
};
export type ObjectSelectProps<T> = (RestProps<T> &
Partial<ControlledValueProps<T>>) &
Omit<SelectProps, "value" | "onChange" | "data"> &
(T extends ObjectWithIdAndName
? Partial<CustomLabelAndKeyProps<T>>
: CustomLabelAndKeyProps<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) {
const groupedData = groupBy(propsData, props.groupBy);
return Object.entries(groupedData).map(([group, items]) => ({
group,
items: items.map(item => ({
label: getLabelFn(item),
value: getValueFn(item),
})),
}));
}
return propsData.map(item => ({
label: getLabelFn(item),
value: getValueFn(item),
}));
}, [props.data, props.groupBy]);
const handleOnChange = (event: string | null) => {
if (!event) return;
const object = props.data.find(item => event === getValueFn(item));
if (!object) return;
if (isControlled) {
props.onChange(object);
return;
}
setInternalValue(object);
};
useEffect(() => {
if (isControlled || !internalValue) return;
props.onChange(internalValue);
}, [internalValue]);
const restProps = omit(props, [
"filterBy",
"groupBy",
"getValueFn",
"getLabelFn",
]);
return (
<Select
{...restProps}
value={value && getValueFn(value)}
onChange={handleOnChange}
data={data}
/>
);
};
export default ObjectSelect;

View File

@ -0,0 +1,26 @@
"use client";
import { FC } from "react";
import { ProjectSchema } from "@/types/ProjectSchema";
import ObjectSelect, { ObjectSelectProps } from "@/components/selects/ObjectSelect/ObjectSelect";
type Props = Omit<
ObjectSelectProps<ProjectSchema | null>,
"getLabelFn" | "getValueFn"
>;
const ProjectSelect: FC<Props> = ({ data, ...props }) => {
const onClear = () => props.onChange(null);
return (
<ObjectSelect
data={data}
searchable
placeholder={"Выберите проект"}
onClear={onClear}
{...props}
/>
);
};
export default ProjectSelect;