feat: dnd for options in select editor

This commit is contained in:
2025-11-05 20:52:08 +04:00
parent 38ae35795b
commit d3270a3532
23 changed files with 279 additions and 215 deletions

View File

@ -43,7 +43,6 @@
"next": "15.4.7", "next": "15.4.7",
"phone": "^3.1.67", "phone": "^3.1.67",
"react": "19.1.0", "react": "19.1.0",
"react-dom": "19.1.0",
"react-imask": "^7.6.1", "react-imask": "^7.6.1",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"redux-persist": "^6.0.0", "redux-persist": "^6.0.0",

View File

@ -0,0 +1,49 @@
"use client";
import React, { FC } from "react";
import { Drawer } from "@mantine/core";
import EditorBody from "@/app/attributes/drawers/AttrSelectEditorDrawer/components/EditorBody";
import { SelectEditorContextProvider } from "@/app/attributes/drawers/AttrSelectEditorDrawer/contexts/SelectEditorContext";
import { DrawerProps } from "@/drawers/types";
import useIsMobile from "@/hooks/utils/useIsMobile";
import { AttrSelectSchema, UpdateAttrSelectSchema } from "@/lib/client";
type Props = {
select: AttrSelectSchema;
onSelectChange: (
values: UpdateAttrSelectSchema,
onSuccess: () => void
) => void;
};
const AttrSelectEditorDrawer: FC<DrawerProps<Props>> = ({
onClose,
opened,
props,
}) => {
const isMobile = useIsMobile();
return (
<Drawer
size={isMobile ? "100%" : "30%"}
title={"Редактирование справочника"}
position={"left"}
onClose={onClose}
removeScrollProps={{ allowPinchZoom: true }}
withCloseButton
opened={opened}
trapFocus={false}
styles={{
body: {
display: "flex",
flexDirection: "column",
},
}}>
<SelectEditorContextProvider {...props}>
<EditorBody />
</SelectEditorContextProvider>
</Drawer>
);
};
export default AttrSelectEditorDrawer;

View File

@ -1,6 +1,6 @@
import { Button, Flex, TextInput } from "@mantine/core"; import { Button, Flex, TextInput } from "@mantine/core";
import { useForm } from "@mantine/form"; import { useForm } from "@mantine/form";
import { useSelectEditorContext } from "@/app/attributes/modals/AttrSelectEditorModal/contexts/SelectEditorContext"; import { useSelectEditorContext } from "@/app/attributes/drawers/AttrSelectEditorDrawer/contexts/SelectEditorContext";
import { UpdateAttrSelectSchema } from "@/lib/client"; import { UpdateAttrSelectSchema } from "@/lib/client";
const CommonInfoEditor = () => { const CommonInfoEditor = () => {

View File

@ -1,6 +1,6 @@
import { IconCheck } from "@tabler/icons-react"; import { IconCheck } from "@tabler/icons-react";
import { Flex, TextInput } from "@mantine/core"; import { Flex, TextInput } from "@mantine/core";
import { useSelectEditorContext } from "@/app/attributes/modals/AttrSelectEditorModal/contexts/SelectEditorContext"; import { useSelectEditorContext } from "@/app/attributes/drawers/AttrSelectEditorDrawer/contexts/SelectEditorContext";
import ActionIconWithTip from "@/components/ui/ActionIconWithTip/ActionIconWithTip"; import ActionIconWithTip from "@/components/ui/ActionIconWithTip/ActionIconWithTip";
import InlineButton from "@/components/ui/InlineButton/InlineButton"; import InlineButton from "@/components/ui/InlineButton/InlineButton";
@ -16,14 +16,20 @@ const CreateOptionButton = () => {
if (!isCreatingOption) { if (!isCreatingOption) {
return ( return (
<InlineButton onClick={onStartCreating}> <Flex flex={1}>
<InlineButton
fullWidth
onClick={onStartCreating}>
Добавить опцию Добавить опцию
</InlineButton> </InlineButton>
</Flex>
); );
} }
return ( return (
<Flex gap={"xs"}> <Flex
gap={"xs"}
flex={1}>
<TextInput <TextInput
{...createOptionForm.getInputProps("name")} {...createOptionForm.getInputProps("name")}
flex={1} flex={1}

View File

@ -0,0 +1,22 @@
import { Divider, Flex } from "@mantine/core";
import CommonInfoEditor from "@/app/attributes/drawers/AttrSelectEditorDrawer/components/CommonInfoEditor";
import CreateOptionButton from "@/app/attributes/drawers/AttrSelectEditorDrawer/components/CreateOptionButton";
import OptionsTable from "@/app/attributes/drawers/AttrSelectEditorDrawer/components/OptionsTable";
const EditorBody = () => {
return (
<Flex
gap={"xs"}
direction={"column"}>
<CommonInfoEditor />
<Divider
label={"Опции"}
my={"xs"}
/>
<CreateOptionButton />
<OptionsTable />
</Flex>
);
};
export default EditorBody;

View File

@ -0,0 +1,82 @@
import React, { FC, ReactNode } from "react";
import { IconCheck, IconEdit, IconTrash } from "@tabler/icons-react";
import { Divider, Flex, Group, Stack, TextInput } from "@mantine/core";
import { useSelectEditorContext } from "@/app/attributes/drawers/AttrSelectEditorDrawer/contexts/SelectEditorContext";
import ActionIconWithTip from "@/components/ui/ActionIconWithTip/ActionIconWithTip";
import { AttrOptionSchema } from "@/lib/client";
type Props = {
option: AttrOptionSchema;
renderDraggable?: (item: AttrOptionSchema) => ReactNode;
};
const OptionTableRow: FC<Props> = ({ option, renderDraggable }) => {
const {
optionsActions: {
onStartEditing,
onFinishEditing,
onDelete,
editingOptionsData,
setEditingOptionsData,
},
} = useSelectEditorContext();
const onChange = (
e: React.ChangeEvent<HTMLInputElement>,
optionId: number
) => {
setEditingOptionsData(prev => {
prev.set(optionId, e.currentTarget.value);
return new Map(prev);
});
};
return (
<Stack
gap={"xs"}
mt={"xs"}>
<Group
wrap={"nowrap"}
justify={"space-between"}>
<Group wrap={"nowrap"}>
{renderDraggable && renderDraggable(option)}
{editingOptionsData.has(option.id) ? (
<TextInput
value={editingOptionsData.get(option.id)}
onChange={e => onChange(e, option.id)}
/>
) : (
option.name
)}
</Group>
<Flex
justify={"center"}
gap={"xs"}>
{editingOptionsData.has(option.id) ? (
<ActionIconWithTip
onClick={() => onFinishEditing(option)}
tipLabel={"Сохранить"}>
<IconCheck />
</ActionIconWithTip>
) : (
<ActionIconWithTip
onClick={() => onStartEditing(option)}
tipLabel={"Редактировать"}>
<IconEdit />
</ActionIconWithTip>
)}
<ActionIconWithTip
color={"red"}
onClick={() => onDelete(option)}
tipLabel={"Удалить"}>
<IconTrash />
</ActionIconWithTip>
</Flex>
</Group>
<Divider />
</Stack>
);
};
export default OptionTableRow;

View File

@ -0,0 +1,38 @@
import React from "react";
import { IconGripVertical } from "@tabler/icons-react";
import { Box, Divider, Stack } from "@mantine/core";
import OptionTableRow from "@/app/attributes/drawers/AttrSelectEditorDrawer/components/OptionTableRow";
import { useSelectEditorContext } from "@/app/attributes/drawers/AttrSelectEditorDrawer/contexts/SelectEditorContext";
import SortableDnd from "@/components/dnd/SortableDnd";
const OptionsTable = () => {
const { options } = useSelectEditorContext();
const { onDragEnd } = useSelectEditorContext();
const renderDraggable = () => (
<Box p={"xs"}>
<IconGripVertical />
</Box>
);
return (
<Stack gap={0}>
<Divider />
<SortableDnd
initialItems={options}
onDragEnd={onDragEnd}
renderItem={(item, renderDraggable) => (
<OptionTableRow
option={item}
renderDraggable={renderDraggable}
/>
)}
renderDraggable={renderDraggable}
dragHandleStyle={{ width: "auto" }}
vertical
/>
</Stack>
);
};
export default OptionsTable;

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import useAttrOptionsList from "@/app/attributes/modals/AttrSelectEditorModal/hooks/useAttrOptionsList"; import useAttrOptionsList from "@/app/attributes/drawers/AttrSelectEditorDrawer/hooks/useAttrOptionsList";
import { import {
AttrOptionSchema, AttrOptionSchema,
AttrSelectSchema, AttrSelectSchema,
@ -15,6 +15,7 @@ type SelectEditorContextState = {
onSelectChange: (values: UpdateAttrSelectSchema) => void; onSelectChange: (values: UpdateAttrSelectSchema) => void;
options: AttrOptionSchema[]; options: AttrOptionSchema[];
optionsActions: OptionsActions; optionsActions: OptionsActions;
onDragEnd: (itemId: number, newLexorank: string) => void;
}; };
type Props = { type Props = {
@ -31,7 +32,7 @@ const useSelectEditorContextState = ({
}: Props): SelectEditorContextState => { }: Props): SelectEditorContextState => {
const { options, queryKey } = useAttrOptionsList({ selectId: select.id }); const { options, queryKey } = useAttrOptionsList({ selectId: select.id });
const optionsActions = useOptionsActions({ queryKey, select }); const optionsActions = useOptionsActions({ queryKey, select, options });
const onSelectChangeWithMsg = (values: UpdateAttrSelectSchema) => { const onSelectChangeWithMsg = (values: UpdateAttrSelectSchema) => {
onSelectChange(values, () => { onSelectChange(values, () => {
@ -41,11 +42,16 @@ const useSelectEditorContextState = ({
}); });
}; };
const onDragEnd = (itemId: number, newLexorank: string) => {
optionsActions.onUpdate(itemId, { lexorank: newLexorank });
};
return { return {
select, select,
onSelectChange: onSelectChangeWithMsg, onSelectChange: onSelectChangeWithMsg,
options, options,
optionsActions, optionsActions,
onDragEnd,
}; };
}; };

View File

@ -1,3 +1,4 @@
import { LexoRank } from "lexorank";
import { useCrudOperations } from "@/hooks/cruds/baseCrud"; import { useCrudOperations } from "@/hooks/cruds/baseCrud";
import { import {
AttrOptionSchema, AttrOptionSchema,
@ -9,9 +10,12 @@ import {
deleteAttrOptionMutation, deleteAttrOptionMutation,
updateAttrOptionMutation, updateAttrOptionMutation,
} from "@/lib/client/@tanstack/react-query.gen"; } from "@/lib/client/@tanstack/react-query.gen";
import { getNewLexorank } from "@/utils/lexorank/generation";
import { getMaxByLexorank } from "@/utils/lexorank/max";
type Props = { type Props = {
queryKey: any[]; queryKey: any[];
options: AttrOptionSchema[];
}; };
export type AttrOptionsCrud = { export type AttrOptionsCrud = {
@ -27,7 +31,10 @@ export type AttrOptionsCrud = {
onDelete: (option: AttrOptionSchema, onSuccess?: () => void) => void; onDelete: (option: AttrOptionSchema, onSuccess?: () => void) => void;
}; };
export const useAttrOptionsCrud = ({ queryKey }: Props): AttrOptionsCrud => { export const useAttrOptionsCrud = ({
queryKey,
options,
}: Props): AttrOptionsCrud => {
return useCrudOperations< return useCrudOperations<
AttrOptionSchema, AttrOptionSchema,
UpdateAttrOptionSchema, UpdateAttrOptionSchema,
@ -40,13 +47,21 @@ export const useAttrOptionsCrud = ({ queryKey }: Props): AttrOptionsCrud => {
update: updateAttrOptionMutation(), update: updateAttrOptionMutation(),
delete: deleteAttrOptionMutation(), delete: deleteAttrOptionMutation(),
}, },
getCreateEntity: data => ({ getCreateEntity: data => {
const lastOption = getMaxByLexorank(options);
const newLexorank = getNewLexorank(
lastOption ? LexoRank.parse(lastOption.lexorank) : null
);
return {
name: data.name!, name: data.name!,
selectId: data.selectId!, selectId: data.selectId!,
}), lexorank: newLexorank.toString(),
};
},
getUpdateEntity: (old, update) => ({ getUpdateEntity: (old, update) => ({
...old, ...old,
name: update.name ?? old.name, name: update.name ?? old.name,
lexorank: update.lexorank ?? old.lexorank,
}), }),
getDeleteConfirmTitle: () => "Удаление опции", getDeleteConfirmTitle: () => "Удаление опции",
}); });

View File

@ -4,6 +4,7 @@ import {
getAttrOptionsOptions, getAttrOptionsOptions,
getAttrOptionsQueryKey, getAttrOptionsQueryKey,
} from "@/lib/client/@tanstack/react-query.gen"; } from "@/lib/client/@tanstack/react-query.gen";
import { sortByLexorank } from "@/utils/lexorank/sort";
type Props = { type Props = {
selectId: number; selectId: number;
@ -27,7 +28,7 @@ const useAttrOptionsList = ({ selectId }: Props) => {
}; };
return { return {
options: data?.items ?? [], options: sortByLexorank(data?.items ?? []),
setOptions, setOptions,
refetch, refetch,
queryKey, queryKey,

View File

@ -1,16 +1,18 @@
import { Dispatch, SetStateAction, useState } from "react"; import { Dispatch, SetStateAction, useState } from "react";
import { useForm, UseFormReturnType } from "@mantine/form"; import { useForm, UseFormReturnType } from "@mantine/form";
import { useAttrOptionsCrud } from "@/app/attributes/modals/AttrSelectEditorModal/hooks/useAttrOptionsCrud";
import { import {
AttrOptionSchema, AttrOptionSchema,
AttrSelectSchema, AttrSelectSchema,
CreateAttrOptionSchema, CreateAttrOptionSchema,
UpdateAttrOptionSchema,
} from "@/lib/client"; } from "@/lib/client";
import { notifications } from "@/lib/notifications"; import { notifications } from "@/lib/notifications";
import { useAttrOptionsCrud } from "@/app/attributes/drawers/AttrSelectEditorDrawer/hooks/useAttrOptionsCrud";
type Props = { type Props = {
queryKey: any[]; queryKey: any[];
select: AttrSelectSchema; select: AttrSelectSchema;
options: AttrOptionSchema[];
}; };
export type OptionsActions = { export type OptionsActions = {
@ -22,10 +24,11 @@ export type OptionsActions = {
setEditingOptionsData: Dispatch<SetStateAction<Map<number, string>>>; setEditingOptionsData: Dispatch<SetStateAction<Map<number, string>>>;
onStartEditing: (option: AttrOptionSchema) => void; onStartEditing: (option: AttrOptionSchema) => void;
onFinishEditing: (option: AttrOptionSchema) => void; onFinishEditing: (option: AttrOptionSchema) => void;
onUpdate: (optionId: number, data: UpdateAttrOptionSchema) => void;
onDelete: (option: AttrOptionSchema) => void; onDelete: (option: AttrOptionSchema) => void;
}; };
const useOptionsActions = ({ queryKey, select }: Props) => { const useOptionsActions = ({ queryKey, select, options }: Props) => {
const [isCreatingOption, setIsCreatingOption] = useState<boolean>(false); const [isCreatingOption, setIsCreatingOption] = useState<boolean>(false);
const [editingOptionsData, setEditingOptionsData] = useState< const [editingOptionsData, setEditingOptionsData] = useState<
Map<number, string> Map<number, string>
@ -34,6 +37,7 @@ const useOptionsActions = ({ queryKey, select }: Props) => {
const createOptionForm = useForm<CreateAttrOptionSchema>({ const createOptionForm = useForm<CreateAttrOptionSchema>({
initialValues: { initialValues: {
name: "", name: "",
lexorank: "",
selectId: select.id, selectId: select.id,
}, },
validate: { validate: {
@ -41,7 +45,7 @@ const useOptionsActions = ({ queryKey, select }: Props) => {
}, },
}); });
const optionCrud = useAttrOptionsCrud({ queryKey }); const optionCrud = useAttrOptionsCrud({ queryKey, options });
const onStartCreating = () => { const onStartCreating = () => {
setIsCreatingOption(true); setIsCreatingOption(true);
@ -69,7 +73,7 @@ const useOptionsActions = ({ queryKey, select }: Props) => {
notifications.error({ message: "Название не может быть пустым" }); notifications.error({ message: "Название не может быть пустым" });
return; return;
} }
optionCrud.onUpdate(option.id, { name: newName }, () => { optionCrud.onUpdate(option.id, { ...option, name: newName }, () => {
notifications.success({ message: "Опция сохранена" }); notifications.success({ message: "Опция сохранена" });
setEditingOptionsData(prev => { setEditingOptionsData(prev => {
prev.delete(option.id); prev.delete(option.id);
@ -84,6 +88,8 @@ const useOptionsActions = ({ queryKey, select }: Props) => {
); );
}; };
const onUpdate = optionCrud.onUpdate;
return { return {
isCreatingOption, isCreatingOption,
createOptionForm, createOptionForm,
@ -94,6 +100,7 @@ const useOptionsActions = ({ queryKey, select }: Props) => {
onStartEditing, onStartEditing,
onFinishEditing, onFinishEditing,
onDelete, onDelete,
onUpdate,
}; };
}; };

View File

@ -1,4 +1,5 @@
import { modals } from "@mantine/modals"; import { modals } from "@mantine/modals";
import { useDrawersContext } from "@/drawers/DrawersContext";
import { useAttrSelectsCrud } from "@/hooks/cruds/useSelectsCrud"; import { useAttrSelectsCrud } from "@/hooks/cruds/useSelectsCrud";
import { AttrSelectSchema } from "@/lib/client"; import { AttrSelectSchema } from "@/lib/client";
@ -14,6 +15,7 @@ export type SelectsActions = {
const useSelectsActions = (props: Props): SelectsActions => { const useSelectsActions = (props: Props): SelectsActions => {
const attrSelectsCrud = useAttrSelectsCrud(props); const attrSelectsCrud = useAttrSelectsCrud(props);
const { openDrawer } = useDrawersContext();
const onCreate = () => { const onCreate = () => {
modals.openContextModal({ modals.openContextModal({
@ -26,10 +28,9 @@ const useSelectsActions = (props: Props): SelectsActions => {
}; };
const onUpdate = (select: AttrSelectSchema) => { const onUpdate = (select: AttrSelectSchema) => {
modals.openContextModal({ openDrawer({
modal: "attrSelectEditorModal", key: "attrSelectEditorDrawer",
title: "Редактирование справочника", props: {
innerProps: {
onSelectChange: (values, onSuccess) => onSelectChange: (values, onSuccess) =>
attrSelectsCrud.onUpdate(select.id, values, onSuccess), attrSelectsCrud.onUpdate(select.id, values, onSuccess),
select, select,

View File

@ -1,24 +0,0 @@
"use client";
import { ContextModalProps } from "@mantine/modals";
import EditorBody from "@/app/attributes/modals/AttrSelectEditorModal/components/EditorBody";
import { SelectEditorContextProvider } from "@/app/attributes/modals/AttrSelectEditorModal/contexts/SelectEditorContext";
import { AttrSelectSchema, UpdateAttrSelectSchema } from "@/lib/client";
type Props = {
select: AttrSelectSchema;
onSelectChange: (
values: UpdateAttrSelectSchema,
onSuccess: () => void
) => void;
};
const AttrSelectEditorModal = ({ innerProps }: ContextModalProps<Props>) => {
return (
<SelectEditorContextProvider {...innerProps}>
<EditorBody />
</SelectEditorContextProvider>
);
};
export default AttrSelectEditorModal;

View File

@ -1,20 +0,0 @@
import { Divider, Stack } from "@mantine/core";
import CommonInfoEditor from "@/app/attributes/modals/AttrSelectEditorModal/components/CommonInfoEditor";
import CreateOptionButton from "@/app/attributes/modals/AttrSelectEditorModal/components/CreateOptionButton";
import OptionsTable from "@/app/attributes/modals/AttrSelectEditorModal/components/OptionsTable";
const EditorBody = () => {
return (
<Stack gap={"xs"}>
<CommonInfoEditor />
<Divider
label={"Опции"}
my={"xs"}
/>
<CreateOptionButton />
<OptionsTable />
</Stack>
);
};
export default EditorBody;

View File

@ -1,38 +0,0 @@
import { IconMoodSad } from "@tabler/icons-react";
import { Group, Text } from "@mantine/core";
import { useSelectEditorContext } from "@/app/attributes/modals/AttrSelectEditorModal/contexts/SelectEditorContext";
import useOptionsTableColumns from "@/app/attributes/modals/AttrSelectEditorModal/hooks/useOptionsTableColumns";
import BaseTable from "@/components/ui/BaseTable/BaseTable";
import useIsMobile from "@/hooks/utils/useIsMobile";
const OptionsTable = () => {
const { options } = useSelectEditorContext();
const isMobile = useIsMobile();
const columns = useOptionsTableColumns();
return (
<BaseTable
withTableBorder
columns={columns}
records={options}
verticalSpacing={"md"}
emptyState={
<Group mt={options.length === 0 ? "xl" : 0}>
<Text>Нет опций</Text>
<IconMoodSad />
</Group>
}
groups={undefined}
styles={{
table: {
width: "100%",
minHeight: options.length === 0 ? "150px" : "auto",
},
header: { zIndex: 1 },
}}
mx={isMobile ? "xs" : 0}
/>
);
};
export default OptionsTable;

View File

@ -1,84 +0,0 @@
"use client";
import React, { useMemo } from "react";
import { IconCheck, IconEdit, IconTrash } from "@tabler/icons-react";
import { DataTableColumn } from "mantine-datatable";
import { Center, Flex, TextInput } from "@mantine/core";
import { useSelectEditorContext } from "@/app/attributes/modals/AttrSelectEditorModal/contexts/SelectEditorContext";
import ActionIconWithTip from "@/components/ui/ActionIconWithTip/ActionIconWithTip";
import useIsMobile from "@/hooks/utils/useIsMobile";
import { AttrOptionSchema } from "@/lib/client";
const useSelectsTableColumns = () => {
const isMobile = useIsMobile();
const {
optionsActions: {
onStartEditing,
onFinishEditing,
onDelete,
editingOptionsData,
setEditingOptionsData,
},
} = useSelectEditorContext();
const onChange = (
e: React.ChangeEvent<HTMLInputElement>,
optionId: number
) => {
setEditingOptionsData(prev => {
prev.set(optionId, e.currentTarget.value);
return new Map(prev);
});
};
return useMemo(
() =>
[
{
title: "Название опции",
accessor: "name",
render: option =>
editingOptionsData.has(option.id) ? (
<TextInput
value={editingOptionsData.get(option.id)}
onChange={e => onChange(e, option.id)}
/>
) : (
option.name
),
},
{
accessor: "actions",
title: <Center>Действия</Center>,
width: "0%",
render: option => (
<Flex gap={"xs"}>
{editingOptionsData.has(option.id) ? (
<ActionIconWithTip
onClick={() => onFinishEditing(option)}
tipLabel={"Сохранить"}>
<IconCheck />
</ActionIconWithTip>
) : (
<ActionIconWithTip
onClick={() => onStartEditing(option)}
tipLabel={"Редактировать"}>
<IconEdit />
</ActionIconWithTip>
)}
<ActionIconWithTip
color={"red"}
onClick={() => onDelete(option)}
tipLabel={"Удалить"}>
<IconTrash />
</ActionIconWithTip>
</Flex>
),
},
] as DataTableColumn<AttrOptionSchema>[],
[isMobile, editingOptionsData]
);
};
export default useSelectsTableColumns;

View File

@ -18,8 +18,8 @@ const BoardsMobileEditorDrawer: FC<DrawerProps<Props>> = ({
}) => { }) => {
return ( return (
<Drawer <Drawer
size={"100%"} size={"50%"}
position={"right"} position={"left"}
onClose={onClose} onClose={onClose}
removeScrollProps={{ allowPinchZoom: true }} removeScrollProps={{ allowPinchZoom: true }}
withCloseButton={false} withCloseButton={false}

View File

@ -39,7 +39,15 @@ const DraggableTableRow: FC<Props> = ({
rowProps.className, rowProps.className,
classes["draggable-row"] classes["draggable-row"]
)} )}
{...provided.draggableProps}> {...provided.draggableProps}
// style={{
// ...provided.draggableProps.style,
// left: "auto !important",
// top: "auto !important",
//
// // does not work after scroll
// }}
>
<TableTd maw={isMobile ? 2 : "auto"}> <TableTd maw={isMobile ? 2 : "auto"}>
<Center <Center
{...provided.dragHandleProps} {...provided.dragHandleProps}

View File

@ -4,6 +4,7 @@ import DealEditorDrawer from "@/app/deals/drawers/DealEditorDrawer";
import ProjectsMobileEditorDrawer from "@/app/deals/drawers/ProjectsMobileEditorDrawer"; import ProjectsMobileEditorDrawer from "@/app/deals/drawers/ProjectsMobileEditorDrawer";
import StatusesMobileEditorDrawer from "../app/deals/drawers/StatusesMobileEditorDrawer"; import StatusesMobileEditorDrawer from "../app/deals/drawers/StatusesMobileEditorDrawer";
import ProjectEditorDrawer from "./common/ProjectEditorDrawer"; import ProjectEditorDrawer from "./common/ProjectEditorDrawer";
import AttrSelectEditorDrawer from "@/app/attributes/drawers/AttrSelectEditorDrawer/AttrSelectEditorDrawer";
const drawerRegistry = { const drawerRegistry = {
projectsMobileEditorDrawer: ProjectsMobileEditorDrawer, projectsMobileEditorDrawer: ProjectsMobileEditorDrawer,
@ -12,6 +13,7 @@ const drawerRegistry = {
dealEditorDrawer: DealEditorDrawer, dealEditorDrawer: DealEditorDrawer,
projectEditorDrawer: ProjectEditorDrawer, projectEditorDrawer: ProjectEditorDrawer,
clientMarketplaceDrawer: ClientMarketplaceDrawer, clientMarketplaceDrawer: ClientMarketplaceDrawer,
attrSelectEditorDrawer: AttrSelectEditorDrawer,
}; };
export default drawerRegistry; export default drawerRegistry;

View File

@ -22,6 +22,10 @@ export type AttrOptionSchema = {
* Name * Name
*/ */
name: string; name: string;
/**
* Lexorank
*/
lexorank: string;
}; };
/** /**
@ -317,6 +321,10 @@ export type CreateAttrOptionSchema = {
* Name * Name
*/ */
name: string; name: string;
/**
* Lexorank
*/
lexorank: string;
/** /**
* Selectid * Selectid
*/ */
@ -2482,7 +2490,11 @@ export type UpdateAttrOptionSchema = {
/** /**
* Name * Name
*/ */
name: string; name?: string | null;
/**
* Lexorank
*/
lexorank?: string | null;
}; };
/** /**

View File

@ -15,6 +15,7 @@ export const zAddAttributeResponse = z.object({
export const zAttrOptionSchema = z.object({ export const zAttrOptionSchema = z.object({
id: z.int(), id: z.int(),
name: z.string(), name: z.string(),
lexorank: z.string(),
}); });
/** /**
@ -152,6 +153,7 @@ export const zClientSchema = z.object({
*/ */
export const zCreateAttrOptionSchema = z.object({ export const zCreateAttrOptionSchema = z.object({
name: z.string(), name: z.string(),
lexorank: z.string(),
selectId: z.int(), selectId: z.int(),
}); });
@ -1421,7 +1423,8 @@ export const zSwitchDealTagResponse = z.object({
* UpdateAttrOptionSchema * UpdateAttrOptionSchema
*/ */
export const zUpdateAttrOptionSchema = z.object({ export const zUpdateAttrOptionSchema = z.object({
name: z.string(), name: z.optional(z.union([z.string(), z.null()])),
lexorank: z.optional(z.union([z.string(), z.null()])),
}); });
/** /**

View File

@ -1,4 +1,3 @@
import AttrSelectEditorModal from "@/app/attributes/modals/AttrSelectEditorModal/AttrSelectEditorModal";
import BarcodeTemplateEditorModal from "@/app/barcode-templates/modals/BarcodeTemplateFormModal/BarcodeTemplateEditorModal"; import BarcodeTemplateEditorModal from "@/app/barcode-templates/modals/BarcodeTemplateFormModal/BarcodeTemplateEditorModal";
import MarketplaceEditorModal from "@/app/clients/drawers/ClientMarketplacesDrawer/modals/MarketplaceEditorModal"; import MarketplaceEditorModal from "@/app/clients/drawers/ClientMarketplacesDrawer/modals/MarketplaceEditorModal";
import ClientEditorModal from "@/app/clients/modals/ClientFormModal/ClientFormModal"; import ClientEditorModal from "@/app/clients/modals/ClientFormModal/ClientFormModal";
@ -47,5 +46,4 @@ export const modals = {
dealTagModal: DealTagModal, dealTagModal: DealTagModal,
attributeEditorModal: AttributeEditorModal, attributeEditorModal: AttributeEditorModal,
moduleCreatorModal: ModuleCreatorModal, moduleCreatorModal: ModuleCreatorModal,
attrSelectEditorModal: AttrSelectEditorModal,
}; };

View File

@ -6207,7 +6207,6 @@ __metadata:
postcss-simple-vars: "npm:^7.0.1" postcss-simple-vars: "npm:^7.0.1"
prettier: "npm:^3.5.3" prettier: "npm:^3.5.3"
react: "npm:19.1.0" react: "npm:19.1.0"
react-dom: "npm:19.1.0"
react-imask: "npm:^7.6.1" react-imask: "npm:^7.6.1"
react-redux: "npm:^9.2.0" react-redux: "npm:^9.2.0"
redux-persist: "npm:^6.0.0" redux-persist: "npm:^6.0.0"
@ -11754,17 +11753,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-dom@npm:19.1.0":
version: 19.1.0
resolution: "react-dom@npm:19.1.0"
dependencies:
scheduler: "npm:^0.26.0"
peerDependencies:
react: ^19.1.0
checksum: 10c0/3e26e89bb6c67c9a6aa86cb888c7a7f8258f2e347a6d2a15299c17eb16e04c19194e3452bc3255bd34000a61e45e2cb51e46292392340432f133e5a5d2dfb5fc
languageName: node
linkType: hard
"react-dropzone@npm:14.3.8": "react-dropzone@npm:14.3.8":
version: 14.3.8 version: 14.3.8
resolution: "react-dropzone@npm:14.3.8" resolution: "react-dropzone@npm:14.3.8"
@ -12417,13 +12405,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"scheduler@npm:^0.26.0":
version: 0.26.0
resolution: "scheduler@npm:0.26.0"
checksum: 10c0/5b8d5bfddaae3513410eda54f2268e98a376a429931921a81b5c3a2873aab7ca4d775a8caac5498f8cbc7d0daeab947cf923dbd8e215d61671f9f4e392d34356
languageName: node
linkType: hard
"schema-utils@npm:^3.1.1": "schema-utils@npm:^3.1.1":
version: 3.3.0 version: 3.3.0
resolution: "schema-utils@npm:3.3.0" resolution: "schema-utils@npm:3.3.0"