feat: deal attributes with select and options

This commit is contained in:
2025-10-29 19:36:58 +04:00
parent 8019fa566c
commit 4cc6360bb4
14 changed files with 489 additions and 20 deletions

View File

@ -1,6 +1,7 @@
import { CSSProperties, FC, JSX } from "react";
import { Checkbox, NumberInput, TextInput } from "@mantine/core";
import { DatePickerInput, DateTimePicker } from "@mantine/dates";
import AttrOptionSelect from "@/app/module-editor/[moduleId]/components/shared/AttrOptionSelect/AttrOptionSelect";
import { DealModuleAttributeSchema } from "@/lib/client";
import { naiveDateTimeStringToUtc } from "@/utils/datetime";
@ -25,15 +26,13 @@ const AttributeValueInput: FC<Props> = ({
error,
};
const renderCheckbox = () => {
return (
<Checkbox
{...commonProps}
checked={Boolean(value)}
onChange={e => onChange(e.currentTarget.checked)}
/>
);
};
const renderCheckbox = () => (
<Checkbox
{...commonProps}
checked={Boolean(value)}
onChange={e => onChange(e.currentTarget.checked)}
/>
);
const renderDatePicker = () => (
<DatePickerInput
@ -84,6 +83,18 @@ const AttributeValueInput: FC<Props> = ({
/>
);
const renderSelect = () => {
if (!attrInfo.select?.id) return <></>;
return (
<AttrOptionSelect
{...commonProps}
value={value}
onChange={onChange}
selectId={attrInfo.select.id}
/>
);
};
const renderingFuncMap: Record<string, () => JSX.Element> = {
bool: renderCheckbox,
date: renderDatePicker,
@ -91,6 +102,7 @@ const AttributeValueInput: FC<Props> = ({
str: renderTextInput,
int: renderNumberInput,
float: renderNumberInput,
select: renderSelect,
};
const render = renderingFuncMap[attrInfo.type.type];

View File

@ -0,0 +1,51 @@
import { useEffect, useState } from "react";
import { omit } from "lodash";
import useAttributeOptionsList from "@/app/module-editor/[moduleId]/components/shared/AttrOptionSelect/useAttributeOptionsList";
import ObjectSelect from "@/components/selects/ObjectSelect/ObjectSelect";
import { AttrOptionSchema } from "@/lib/client";
type Props = {
value: any;
onChange: (val: any) => void;
selectId: number;
error?: string;
label?: string;
};
const AttrOptionSelect = (props: Props) => {
const { options } = useAttributeOptionsList(props);
const [selectedOption, setSelectedOption] = useState<AttrOptionSchema>();
useEffect(() => {
if (!props.value) {
setSelectedOption(undefined);
return;
}
setSelectedOption(options.find(option => option.value === props.value));
}, [props.value, options]);
const restProps = omit(props, ["value, onChange", "selectId"]);
return (
<ObjectSelect
label={"Значение"}
{...restProps}
data={options}
value={selectedOption}
onChange={option => {
setSelectedOption(option);
props.onChange(option.value);
}}
onClear={() => {
setSelectedOption(undefined);
props.onChange(null);
}}
getLabelFn={option => option.label}
clearable
searchable
/>
);
};
export default AttrOptionSelect;

View File

@ -0,0 +1,19 @@
import { useQuery } from "@tanstack/react-query";
import { getAttrSelectOptionsOptions } from "@/lib/client/@tanstack/react-query.gen";
type Props = {
selectId: number;
};
const useAttributeSelectsList = ({ selectId }: Props) => {
const { data, refetch } = useQuery(
getAttrSelectOptionsOptions({ path: { selectId } })
);
return {
options: data?.items ?? [],
refetch,
};
};
export default useAttributeSelectsList;

View File

@ -0,0 +1,24 @@
import ObjectSelect, { ObjectSelectProps } from "@/components/selects/ObjectSelect/ObjectSelect";
import { AttributeSelectSchema } from "@/lib/client";
import useAttributeSelectsList from "./useAttributeSelectsList";
type Props = Omit<
ObjectSelectProps<AttributeSelectSchema>,
"data" | "getLabelFn"
>;
const AttrSelectSelect = (props: Props) => {
const { selects } = useAttributeSelectsList();
return (
<ObjectSelect
label={"Объект для выбора"}
getLabelFn={select => select.label}
data={selects}
{...props}
/>
);
};
export default AttrSelectSelect;

View File

@ -0,0 +1,13 @@
import { useQuery } from "@tanstack/react-query";
import { getAttrSelectsOptions } from "@/lib/client/@tanstack/react-query.gen";
const useAttributeSelectsList = () => {
const { data, refetch } = useQuery(getAttrSelectsOptions());
return {
selects: data?.items ?? [],
refetch,
};
};
export default useAttributeSelectsList;

View File

@ -20,6 +20,10 @@ const useAttributesTableColumns = () => {
{
title: "Тип",
accessor: "type.name",
render: attr =>
attr.type.type === "select"
? `Выбор "${attr.label}"`
: attr.type.name,
},
{
accessor: "actions",

View File

@ -1,11 +1,12 @@
import { Checkbox, NumberInput, TextInput } from "@mantine/core";
import { DatePickerInput, DateTimePicker } from "@mantine/dates";
import { UseFormReturnType } from "@mantine/form";
import { UpdateAttributeSchema } from "@/lib/client";
import AttrOptionSelect from "@/app/module-editor/[moduleId]/components/shared/AttrOptionSelect/AttrOptionSelect";
import { AttributeSchema } from "@/lib/client";
import { naiveDateTimeStringToUtc } from "@/utils/datetime";
type Props = {
form: UseFormReturnType<Partial<UpdateAttributeSchema>>;
form: UseFormReturnType<Partial<AttributeSchema>>;
};
const DefaultAttributeValueInput = ({ form }: Props) => {
@ -87,8 +88,18 @@ const DefaultAttributeValueInput = ({ form }: Props) => {
}
/>
);
} else if (type === "select") {
if (!form.values.select?.id) return <></>;
return (
<AttrOptionSelect
label={"Значение по умолчанию"}
{...form.getInputProps("defaultValue")}
value={form.values.defaultValue}
onChange={value => form.setFieldValue("defaultValue", value)}
selectId={form.values.select?.id}
/>
);
}
return <></>;
};

View File

@ -40,7 +40,10 @@ const ModuleAttribute: FC<Props> = ({ attribute }) => {
align={"center"}>
<Stack gap={7}>
<>{getAttrLabelText()}</>
<Text>Тип: {attribute.type.name}</Text>
<Text>
Тип: {attribute.type.name}{" "}
{attribute.select && `"${attribute.select.label}"`}
</Text>
</Stack>
<Group
justify={"end"}

View File

@ -5,6 +5,7 @@ import { Checkbox, Stack, Textarea, TextInput } from "@mantine/core";
import { useForm } from "@mantine/form";
import { ContextModalProps } from "@mantine/modals";
import AttributeTypeSelect from "@/app/module-editor/[moduleId]/components/shared/AttributeTypeSelect/AttributeTypeSelect";
import AttrSelectSelect from "@/app/module-editor/[moduleId]/components/shared/AttrSelectSelect/AttrSelectSelect";
import DefaultAttributeValueInput from "@/app/module-editor/[moduleId]/components/shared/DefaultAttributeValueInput/DefaultAttributeValueInput";
import {
AttributeSchema,
@ -30,7 +31,7 @@ const AttributeEditorModal = ({
const [isNullableInputShown, setIsNullableInputShown] = useState(true);
const [copyTypeId, setCopyTypeId] = useState<number>();
const form = useForm<Partial<UpdateAttributeSchema>>({
const form = useForm<any>({
initialValues: innerProps.isEditing
? innerProps.entity
: ({
@ -38,11 +39,13 @@ const AttributeEditorModal = ({
name: "",
typeId: undefined,
type: undefined,
selectId: undefined,
select: undefined,
isApplicableToGroup: false,
isNullable: false,
defaultValue: null,
description: "",
} as Partial<CreateAttributeSchema>),
} as Partial<AttributeSchema>),
validate: {
label: label => !label?.trim() && "Название не заполнено",
type: type => !type && "Тип атрибута не выбран",
@ -62,7 +65,7 @@ const AttributeEditorModal = ({
if (!isInitial) {
if (type === "bool") {
form.setFieldValue("isNullable", false);
form.setFieldValue("defaultValue", { value: false });
form.setFieldValue("defaultValue", false);
} else {
form.setFieldValue("defaultValue", null);
}
@ -88,10 +91,27 @@ const AttributeEditorModal = ({
disabled={innerProps.isEditing}
{...form.getInputProps("type")}
onChange={type => {
if (type.type !== "select") {
form.setFieldValue("select", undefined);
form.setFieldValue("selectId", undefined);
}
form.setFieldValue("type", type);
form.setFieldValue("typeId", type.id);
}}
/>
{form.values.type?.type === "select" && (
<AttrSelectSelect
withAsterisk
searchable
disabled={innerProps.isEditing}
{...form.getInputProps("select")}
onChange={select => {
form.setFieldValue("select", select);
form.setFieldValue("selectId", select.id);
form.setFieldValue("defaultValue", null);
}}
/>
)}
<Checkbox
label={"Значение синхронизировано в группе"}
{...form.getInputProps("isApplicableToGroup", {

View File

@ -27,6 +27,10 @@ const useAttributesInnerTableColumns = () => {
{
title: "Тип",
accessor: "type.name",
render: attr =>
attr.type.type === "select"
? `Выбор "${attr.label}"`
: attr.type.name,
},
{
title: "Значение по умолчанию",