feat: dnd for options in select editor
This commit is contained in:
@ -43,7 +43,6 @@
|
||||
"next": "15.4.7",
|
||||
"phone": "^3.1.67",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-imask": "^7.6.1",
|
||||
"react-redux": "^9.2.0",
|
||||
"redux-persist": "^6.0.0",
|
||||
|
||||
@ -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;
|
||||
@ -1,6 +1,6 @@
|
||||
import { Button, Flex, TextInput } from "@mantine/core";
|
||||
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";
|
||||
|
||||
const CommonInfoEditor = () => {
|
||||
@ -1,6 +1,6 @@
|
||||
import { IconCheck } from "@tabler/icons-react";
|
||||
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 InlineButton from "@/components/ui/InlineButton/InlineButton";
|
||||
|
||||
@ -16,14 +16,20 @@ const CreateOptionButton = () => {
|
||||
|
||||
if (!isCreatingOption) {
|
||||
return (
|
||||
<InlineButton onClick={onStartCreating}>
|
||||
<Flex flex={1}>
|
||||
<InlineButton
|
||||
fullWidth
|
||||
onClick={onStartCreating}>
|
||||
Добавить опцию
|
||||
</InlineButton>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex gap={"xs"}>
|
||||
<Flex
|
||||
gap={"xs"}
|
||||
flex={1}>
|
||||
<TextInput
|
||||
{...createOptionForm.getInputProps("name")}
|
||||
flex={1}
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import useAttrOptionsList from "@/app/attributes/modals/AttrSelectEditorModal/hooks/useAttrOptionsList";
|
||||
import useAttrOptionsList from "@/app/attributes/drawers/AttrSelectEditorDrawer/hooks/useAttrOptionsList";
|
||||
import {
|
||||
AttrOptionSchema,
|
||||
AttrSelectSchema,
|
||||
@ -15,6 +15,7 @@ type SelectEditorContextState = {
|
||||
onSelectChange: (values: UpdateAttrSelectSchema) => void;
|
||||
options: AttrOptionSchema[];
|
||||
optionsActions: OptionsActions;
|
||||
onDragEnd: (itemId: number, newLexorank: string) => void;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
@ -31,7 +32,7 @@ const useSelectEditorContextState = ({
|
||||
}: Props): SelectEditorContextState => {
|
||||
const { options, queryKey } = useAttrOptionsList({ selectId: select.id });
|
||||
|
||||
const optionsActions = useOptionsActions({ queryKey, select });
|
||||
const optionsActions = useOptionsActions({ queryKey, select, options });
|
||||
|
||||
const onSelectChangeWithMsg = (values: UpdateAttrSelectSchema) => {
|
||||
onSelectChange(values, () => {
|
||||
@ -41,11 +42,16 @@ const useSelectEditorContextState = ({
|
||||
});
|
||||
};
|
||||
|
||||
const onDragEnd = (itemId: number, newLexorank: string) => {
|
||||
optionsActions.onUpdate(itemId, { lexorank: newLexorank });
|
||||
};
|
||||
|
||||
return {
|
||||
select,
|
||||
onSelectChange: onSelectChangeWithMsg,
|
||||
options,
|
||||
optionsActions,
|
||||
onDragEnd,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { LexoRank } from "lexorank";
|
||||
import { useCrudOperations } from "@/hooks/cruds/baseCrud";
|
||||
import {
|
||||
AttrOptionSchema,
|
||||
@ -9,9 +10,12 @@ import {
|
||||
deleteAttrOptionMutation,
|
||||
updateAttrOptionMutation,
|
||||
} from "@/lib/client/@tanstack/react-query.gen";
|
||||
import { getNewLexorank } from "@/utils/lexorank/generation";
|
||||
import { getMaxByLexorank } from "@/utils/lexorank/max";
|
||||
|
||||
type Props = {
|
||||
queryKey: any[];
|
||||
options: AttrOptionSchema[];
|
||||
};
|
||||
|
||||
export type AttrOptionsCrud = {
|
||||
@ -27,7 +31,10 @@ export type AttrOptionsCrud = {
|
||||
onDelete: (option: AttrOptionSchema, onSuccess?: () => void) => void;
|
||||
};
|
||||
|
||||
export const useAttrOptionsCrud = ({ queryKey }: Props): AttrOptionsCrud => {
|
||||
export const useAttrOptionsCrud = ({
|
||||
queryKey,
|
||||
options,
|
||||
}: Props): AttrOptionsCrud => {
|
||||
return useCrudOperations<
|
||||
AttrOptionSchema,
|
||||
UpdateAttrOptionSchema,
|
||||
@ -40,13 +47,21 @@ export const useAttrOptionsCrud = ({ queryKey }: Props): AttrOptionsCrud => {
|
||||
update: updateAttrOptionMutation(),
|
||||
delete: deleteAttrOptionMutation(),
|
||||
},
|
||||
getCreateEntity: data => ({
|
||||
getCreateEntity: data => {
|
||||
const lastOption = getMaxByLexorank(options);
|
||||
const newLexorank = getNewLexorank(
|
||||
lastOption ? LexoRank.parse(lastOption.lexorank) : null
|
||||
);
|
||||
return {
|
||||
name: data.name!,
|
||||
selectId: data.selectId!,
|
||||
}),
|
||||
lexorank: newLexorank.toString(),
|
||||
};
|
||||
},
|
||||
getUpdateEntity: (old, update) => ({
|
||||
...old,
|
||||
name: update.name ?? old.name,
|
||||
lexorank: update.lexorank ?? old.lexorank,
|
||||
}),
|
||||
getDeleteConfirmTitle: () => "Удаление опции",
|
||||
});
|
||||
@ -4,6 +4,7 @@ import {
|
||||
getAttrOptionsOptions,
|
||||
getAttrOptionsQueryKey,
|
||||
} from "@/lib/client/@tanstack/react-query.gen";
|
||||
import { sortByLexorank } from "@/utils/lexorank/sort";
|
||||
|
||||
type Props = {
|
||||
selectId: number;
|
||||
@ -27,7 +28,7 @@ const useAttrOptionsList = ({ selectId }: Props) => {
|
||||
};
|
||||
|
||||
return {
|
||||
options: data?.items ?? [],
|
||||
options: sortByLexorank(data?.items ?? []),
|
||||
setOptions,
|
||||
refetch,
|
||||
queryKey,
|
||||
@ -1,16 +1,18 @@
|
||||
import { Dispatch, SetStateAction, useState } from "react";
|
||||
import { useForm, UseFormReturnType } from "@mantine/form";
|
||||
import { useAttrOptionsCrud } from "@/app/attributes/modals/AttrSelectEditorModal/hooks/useAttrOptionsCrud";
|
||||
import {
|
||||
AttrOptionSchema,
|
||||
AttrSelectSchema,
|
||||
CreateAttrOptionSchema,
|
||||
UpdateAttrOptionSchema,
|
||||
} from "@/lib/client";
|
||||
import { notifications } from "@/lib/notifications";
|
||||
import { useAttrOptionsCrud } from "@/app/attributes/drawers/AttrSelectEditorDrawer/hooks/useAttrOptionsCrud";
|
||||
|
||||
type Props = {
|
||||
queryKey: any[];
|
||||
select: AttrSelectSchema;
|
||||
options: AttrOptionSchema[];
|
||||
};
|
||||
|
||||
export type OptionsActions = {
|
||||
@ -22,10 +24,11 @@ export type OptionsActions = {
|
||||
setEditingOptionsData: Dispatch<SetStateAction<Map<number, string>>>;
|
||||
onStartEditing: (option: AttrOptionSchema) => void;
|
||||
onFinishEditing: (option: AttrOptionSchema) => void;
|
||||
onUpdate: (optionId: number, data: UpdateAttrOptionSchema) => void;
|
||||
onDelete: (option: AttrOptionSchema) => void;
|
||||
};
|
||||
|
||||
const useOptionsActions = ({ queryKey, select }: Props) => {
|
||||
const useOptionsActions = ({ queryKey, select, options }: Props) => {
|
||||
const [isCreatingOption, setIsCreatingOption] = useState<boolean>(false);
|
||||
const [editingOptionsData, setEditingOptionsData] = useState<
|
||||
Map<number, string>
|
||||
@ -34,6 +37,7 @@ const useOptionsActions = ({ queryKey, select }: Props) => {
|
||||
const createOptionForm = useForm<CreateAttrOptionSchema>({
|
||||
initialValues: {
|
||||
name: "",
|
||||
lexorank: "",
|
||||
selectId: select.id,
|
||||
},
|
||||
validate: {
|
||||
@ -41,7 +45,7 @@ const useOptionsActions = ({ queryKey, select }: Props) => {
|
||||
},
|
||||
});
|
||||
|
||||
const optionCrud = useAttrOptionsCrud({ queryKey });
|
||||
const optionCrud = useAttrOptionsCrud({ queryKey, options });
|
||||
|
||||
const onStartCreating = () => {
|
||||
setIsCreatingOption(true);
|
||||
@ -69,7 +73,7 @@ const useOptionsActions = ({ queryKey, select }: Props) => {
|
||||
notifications.error({ message: "Название не может быть пустым" });
|
||||
return;
|
||||
}
|
||||
optionCrud.onUpdate(option.id, { name: newName }, () => {
|
||||
optionCrud.onUpdate(option.id, { ...option, name: newName }, () => {
|
||||
notifications.success({ message: "Опция сохранена" });
|
||||
setEditingOptionsData(prev => {
|
||||
prev.delete(option.id);
|
||||
@ -84,6 +88,8 @@ const useOptionsActions = ({ queryKey, select }: Props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const onUpdate = optionCrud.onUpdate;
|
||||
|
||||
return {
|
||||
isCreatingOption,
|
||||
createOptionForm,
|
||||
@ -94,6 +100,7 @@ const useOptionsActions = ({ queryKey, select }: Props) => {
|
||||
onStartEditing,
|
||||
onFinishEditing,
|
||||
onDelete,
|
||||
onUpdate,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { modals } from "@mantine/modals";
|
||||
import { useDrawersContext } from "@/drawers/DrawersContext";
|
||||
import { useAttrSelectsCrud } from "@/hooks/cruds/useSelectsCrud";
|
||||
import { AttrSelectSchema } from "@/lib/client";
|
||||
|
||||
@ -14,6 +15,7 @@ export type SelectsActions = {
|
||||
|
||||
const useSelectsActions = (props: Props): SelectsActions => {
|
||||
const attrSelectsCrud = useAttrSelectsCrud(props);
|
||||
const { openDrawer } = useDrawersContext();
|
||||
|
||||
const onCreate = () => {
|
||||
modals.openContextModal({
|
||||
@ -26,10 +28,9 @@ const useSelectsActions = (props: Props): SelectsActions => {
|
||||
};
|
||||
|
||||
const onUpdate = (select: AttrSelectSchema) => {
|
||||
modals.openContextModal({
|
||||
modal: "attrSelectEditorModal",
|
||||
title: "Редактирование справочника",
|
||||
innerProps: {
|
||||
openDrawer({
|
||||
key: "attrSelectEditorDrawer",
|
||||
props: {
|
||||
onSelectChange: (values, onSuccess) =>
|
||||
attrSelectsCrud.onUpdate(select.id, values, onSuccess),
|
||||
select,
|
||||
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -18,8 +18,8 @@ const BoardsMobileEditorDrawer: FC<DrawerProps<Props>> = ({
|
||||
}) => {
|
||||
return (
|
||||
<Drawer
|
||||
size={"100%"}
|
||||
position={"right"}
|
||||
size={"50%"}
|
||||
position={"left"}
|
||||
onClose={onClose}
|
||||
removeScrollProps={{ allowPinchZoom: true }}
|
||||
withCloseButton={false}
|
||||
|
||||
@ -39,7 +39,15 @@ const DraggableTableRow: FC<Props> = ({
|
||||
rowProps.className,
|
||||
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"}>
|
||||
<Center
|
||||
{...provided.dragHandleProps}
|
||||
|
||||
@ -4,6 +4,7 @@ import DealEditorDrawer from "@/app/deals/drawers/DealEditorDrawer";
|
||||
import ProjectsMobileEditorDrawer from "@/app/deals/drawers/ProjectsMobileEditorDrawer";
|
||||
import StatusesMobileEditorDrawer from "../app/deals/drawers/StatusesMobileEditorDrawer";
|
||||
import ProjectEditorDrawer from "./common/ProjectEditorDrawer";
|
||||
import AttrSelectEditorDrawer from "@/app/attributes/drawers/AttrSelectEditorDrawer/AttrSelectEditorDrawer";
|
||||
|
||||
const drawerRegistry = {
|
||||
projectsMobileEditorDrawer: ProjectsMobileEditorDrawer,
|
||||
@ -12,6 +13,7 @@ const drawerRegistry = {
|
||||
dealEditorDrawer: DealEditorDrawer,
|
||||
projectEditorDrawer: ProjectEditorDrawer,
|
||||
clientMarketplaceDrawer: ClientMarketplaceDrawer,
|
||||
attrSelectEditorDrawer: AttrSelectEditorDrawer,
|
||||
};
|
||||
|
||||
export default drawerRegistry;
|
||||
|
||||
@ -22,6 +22,10 @@ export type AttrOptionSchema = {
|
||||
* Name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Lexorank
|
||||
*/
|
||||
lexorank: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -317,6 +321,10 @@ export type CreateAttrOptionSchema = {
|
||||
* Name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Lexorank
|
||||
*/
|
||||
lexorank: string;
|
||||
/**
|
||||
* Selectid
|
||||
*/
|
||||
@ -2482,7 +2490,11 @@ export type UpdateAttrOptionSchema = {
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
name: string;
|
||||
name?: string | null;
|
||||
/**
|
||||
* Lexorank
|
||||
*/
|
||||
lexorank?: string | null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -15,6 +15,7 @@ export const zAddAttributeResponse = z.object({
|
||||
export const zAttrOptionSchema = z.object({
|
||||
id: z.int(),
|
||||
name: z.string(),
|
||||
lexorank: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
@ -152,6 +153,7 @@ export const zClientSchema = z.object({
|
||||
*/
|
||||
export const zCreateAttrOptionSchema = z.object({
|
||||
name: z.string(),
|
||||
lexorank: z.string(),
|
||||
selectId: z.int(),
|
||||
});
|
||||
|
||||
@ -1421,7 +1423,8 @@ export const zSwitchDealTagResponse = z.object({
|
||||
* UpdateAttrOptionSchema
|
||||
*/
|
||||
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()])),
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import AttrSelectEditorModal from "@/app/attributes/modals/AttrSelectEditorModal/AttrSelectEditorModal";
|
||||
import BarcodeTemplateEditorModal from "@/app/barcode-templates/modals/BarcodeTemplateFormModal/BarcodeTemplateEditorModal";
|
||||
import MarketplaceEditorModal from "@/app/clients/drawers/ClientMarketplacesDrawer/modals/MarketplaceEditorModal";
|
||||
import ClientEditorModal from "@/app/clients/modals/ClientFormModal/ClientFormModal";
|
||||
@ -47,5 +46,4 @@ export const modals = {
|
||||
dealTagModal: DealTagModal,
|
||||
attributeEditorModal: AttributeEditorModal,
|
||||
moduleCreatorModal: ModuleCreatorModal,
|
||||
attrSelectEditorModal: AttrSelectEditorModal,
|
||||
};
|
||||
|
||||
19
yarn.lock
19
yarn.lock
@ -6207,7 +6207,6 @@ __metadata:
|
||||
postcss-simple-vars: "npm:^7.0.1"
|
||||
prettier: "npm:^3.5.3"
|
||||
react: "npm:19.1.0"
|
||||
react-dom: "npm:19.1.0"
|
||||
react-imask: "npm:^7.6.1"
|
||||
react-redux: "npm:^9.2.0"
|
||||
redux-persist: "npm:^6.0.0"
|
||||
@ -11754,17 +11753,6 @@ __metadata:
|
||||
languageName: node
|
||||
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":
|
||||
version: 14.3.8
|
||||
resolution: "react-dropzone@npm:14.3.8"
|
||||
@ -12417,13 +12405,6 @@ __metadata:
|
||||
languageName: node
|
||||
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":
|
||||
version: 3.3.0
|
||||
resolution: "schema-utils@npm:3.3.0"
|
||||
|
||||
Reference in New Issue
Block a user