feat: attributes page
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { RefObject, useMemo, useRef } from "react";
|
import { RefObject, useMemo, useRef } from "react";
|
||||||
import { IconTag } from "@tabler/icons-react";
|
import { IconList, IconTag } from "@tabler/icons-react";
|
||||||
import { SimpleGrid, Stack } from "@mantine/core";
|
import { SimpleGrid, Stack } from "@mantine/core";
|
||||||
import Action from "@/app/actions/components/Action/Action";
|
import Action from "@/app/actions/components/Action/Action";
|
||||||
import mobileButtonsData from "@/app/actions/data/mobileButtonsData";
|
import mobileButtonsData from "@/app/actions/data/mobileButtonsData";
|
||||||
@ -23,6 +23,11 @@ const PageBody = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const commonActionsData: RefObject<BuiltInLinkData[]> = useRef([
|
const commonActionsData: RefObject<BuiltInLinkData[]> = useRef([
|
||||||
|
{
|
||||||
|
icon: IconList,
|
||||||
|
label: "Атрибуты",
|
||||||
|
href: "/attributes",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: IconTag,
|
icon: IconTag,
|
||||||
label: "Теги",
|
label: "Теги",
|
||||||
|
|||||||
37
src/app/attributes/components/AttributesHeader.tsx
Normal file
37
src/app/attributes/components/AttributesHeader.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { FC } from "react";
|
||||||
|
import { Group, TextInput } from "@mantine/core";
|
||||||
|
import { useAttributesContext } from "@/app/attributes/contexts/AttributesContext";
|
||||||
|
import InlineButton from "@/components/ui/InlineButton/InlineButton";
|
||||||
|
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||||
|
|
||||||
|
const AttributesHeader: FC = () => {
|
||||||
|
const {
|
||||||
|
attributesActions: { onCreate },
|
||||||
|
search,
|
||||||
|
setSearch,
|
||||||
|
} = useAttributesContext();
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group
|
||||||
|
wrap={"nowrap"}
|
||||||
|
mt={isMobile ? "xs" : ""}
|
||||||
|
mx={isMobile ? "xs" : ""}>
|
||||||
|
<InlineButton
|
||||||
|
onClick={onCreate}
|
||||||
|
w={isMobile ? "100%" : "auto"}>
|
||||||
|
Создать атрибут
|
||||||
|
</InlineButton>
|
||||||
|
<TextInput
|
||||||
|
value={search}
|
||||||
|
onChange={e => setSearch(e.currentTarget.value)}
|
||||||
|
w={isMobile ? "100%" : "auto"}
|
||||||
|
placeholder={"Поиск..."}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AttributesHeader;
|
||||||
40
src/app/attributes/components/AttributesTable.tsx
Normal file
40
src/app/attributes/components/AttributesTable.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { FC } from "react";
|
||||||
|
import { IconMoodSad } from "@tabler/icons-react";
|
||||||
|
import { Group, Text } from "@mantine/core";
|
||||||
|
import { useAttributesContext } from "@/app/attributes/contexts/AttributesContext";
|
||||||
|
import useAttributesTableColumns from "@/app/attributes/hooks/useAttributesTableColumns";
|
||||||
|
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
||||||
|
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||||
|
|
||||||
|
const AttributesTable: FC = () => {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
const { attributes } = useAttributesContext();
|
||||||
|
const columns = useAttributesTableColumns();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseTable
|
||||||
|
withTableBorder
|
||||||
|
columns={columns}
|
||||||
|
records={attributes}
|
||||||
|
verticalSpacing={"md"}
|
||||||
|
emptyState={
|
||||||
|
<Group mt={attributes.length === 0 ? "xl" : 0}>
|
||||||
|
<Text>Нет атрибутов</Text>
|
||||||
|
<IconMoodSad />
|
||||||
|
</Group>
|
||||||
|
}
|
||||||
|
groups={undefined}
|
||||||
|
styles={{
|
||||||
|
table: {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
header: { zIndex: 1 },
|
||||||
|
}}
|
||||||
|
mx={isMobile ? "xs" : 0}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AttributesTable;
|
||||||
26
src/app/attributes/components/PageBody.tsx
Normal file
26
src/app/attributes/components/PageBody.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import AttributesHeader from "@/app/attributes/components/AttributesHeader";
|
||||||
|
import AttributesTable from "@/app/attributes/components/AttributesTable";
|
||||||
|
import PageBlock from "@/components/layout/PageBlock/PageBlock";
|
||||||
|
|
||||||
|
const PageBody = () => (
|
||||||
|
<PageBlock
|
||||||
|
style={{ flex: 1, minHeight: 0 }}
|
||||||
|
fullScreenMobile>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: "var(--mantine-spacing-md)",
|
||||||
|
}}>
|
||||||
|
<AttributesHeader />
|
||||||
|
<div style={{ flex: 1, overflow: "auto" }}>
|
||||||
|
<AttributesTable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PageBlock>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default PageBody;
|
||||||
40
src/app/attributes/contexts/AttributesContext.tsx
Normal file
40
src/app/attributes/contexts/AttributesContext.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
import useFilteredAttributes from "@/app/attributes/hooks/useFilteredAttributes";
|
||||||
|
import useAttributesActions, {
|
||||||
|
AttributesActions,
|
||||||
|
} from "@/app/module-editor/[moduleId]/hooks/useAttributesActions";
|
||||||
|
import useAttributesList from "@/app/module-editor/[moduleId]/hooks/useAttributesList";
|
||||||
|
import { AttributeSchema } from "@/lib/client";
|
||||||
|
import makeContext from "@/lib/contextFactory/contextFactory";
|
||||||
|
|
||||||
|
type AttributesContextState = {
|
||||||
|
attributes: AttributeSchema[];
|
||||||
|
refetchAttributes: () => void;
|
||||||
|
attributesActions: AttributesActions;
|
||||||
|
search: string;
|
||||||
|
setSearch: Dispatch<SetStateAction<string>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useAttributesContextState = (): AttributesContextState => {
|
||||||
|
const { attributes, refetch } = useAttributesList();
|
||||||
|
const attributesActions = useAttributesActions({
|
||||||
|
refetchAttributes: refetch,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { search, setSearch, filteredAttributes } = useFilteredAttributes({
|
||||||
|
attributes,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
attributes: filteredAttributes,
|
||||||
|
refetchAttributes: refetch,
|
||||||
|
attributesActions,
|
||||||
|
search,
|
||||||
|
setSearch,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const [AttributesContextProvider, useAttributesContext] =
|
||||||
|
makeContext<AttributesContextState>(useAttributesContextState, "Attribute");
|
||||||
72
src/app/attributes/hooks/useAttributesTableColumns.tsx
Normal file
72
src/app/attributes/hooks/useAttributesTableColumns.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { IconCheck, IconX } from "@tabler/icons-react";
|
||||||
|
import { DataTableColumn } from "mantine-datatable";
|
||||||
|
import { Box, Center } from "@mantine/core";
|
||||||
|
import AttributeTableActions from "@/app/module-editor/[moduleId]/components/shared/AttributeTableActions/AttributeTableActions";
|
||||||
|
import AttributeDefaultValue from "@/components/ui/AttributeDefaultValue/AttributeDefaultValue";
|
||||||
|
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||||
|
import { AttributeSchema } from "@/lib/client";
|
||||||
|
import { useAttributesContext } from "../contexts/AttributesContext";
|
||||||
|
|
||||||
|
const useAttributesTableColumns = () => {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
const { attributesActions } = useAttributesContext();
|
||||||
|
const renderCheck = (value: boolean) => (value ? <IconCheck /> : <IconX />);
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() =>
|
||||||
|
[
|
||||||
|
{
|
||||||
|
title: "Название атрибута",
|
||||||
|
accessor: "label",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Тип",
|
||||||
|
accessor: "type.name",
|
||||||
|
render: attr =>
|
||||||
|
attr.type.type === "select"
|
||||||
|
? `Выбор "${attr.label}"`
|
||||||
|
: attr.type.name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Значение по умолчанию",
|
||||||
|
accessor: "defaultValue",
|
||||||
|
render: attr => <AttributeDefaultValue attribute={attr} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: isMobile
|
||||||
|
? "Синх. в группе"
|
||||||
|
: "Синхронизировано в группе",
|
||||||
|
accessor: "isApplicableToGroup",
|
||||||
|
render: attr => renderCheck(attr.isApplicableToGroup),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Может быть пустым",
|
||||||
|
accessor: "isNullable",
|
||||||
|
render: attr => renderCheck(attr.isNullable),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Описаниие",
|
||||||
|
accessor: "description",
|
||||||
|
render: attr => <Box>{attr.description}</Box>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: "actions",
|
||||||
|
title: <Center>Действия</Center>,
|
||||||
|
width: "0%",
|
||||||
|
render: attribute => (
|
||||||
|
<AttributeTableActions
|
||||||
|
attribute={attribute}
|
||||||
|
onUpdate={attributesActions.onUpdate}
|
||||||
|
onDelete={attributesActions.onDelete}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
] as DataTableColumn<AttributeSchema>[],
|
||||||
|
[isMobile]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useAttributesTableColumns;
|
||||||
29
src/app/attributes/hooks/useFilteredAttributes.ts
Normal file
29
src/app/attributes/hooks/useFilteredAttributes.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import { AttributeSchema } from "@/lib/client";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
attributes: AttributeSchema[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const useFilteredAttributes = ({ attributes }: Props) => {
|
||||||
|
const [search, setSearch] = useState<string>("");
|
||||||
|
|
||||||
|
const filteredAttributes = useMemo(
|
||||||
|
() =>
|
||||||
|
attributes.filter(
|
||||||
|
attr =>
|
||||||
|
attr.type.name.includes(search) ||
|
||||||
|
attr.label.includes(search) ||
|
||||||
|
attr.description.includes(search)
|
||||||
|
),
|
||||||
|
[attributes, search]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
search,
|
||||||
|
setSearch,
|
||||||
|
filteredAttributes,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useFilteredAttributes;
|
||||||
22
src/app/attributes/page.tsx
Normal file
22
src/app/attributes/page.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Suspense } from "react";
|
||||||
|
import { Center, Loader } from "@mantine/core";
|
||||||
|
import PageBody from "@/app/attributes/components/PageBody";
|
||||||
|
import { AttributesContextProvider } from "@/app/attributes/contexts/AttributesContext";
|
||||||
|
import PageContainer from "@/components/layout/PageContainer/PageContainer";
|
||||||
|
|
||||||
|
export default async function AttributesPage() {
|
||||||
|
return (
|
||||||
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<Center h="50vh">
|
||||||
|
<Loader size="lg" />
|
||||||
|
</Center>
|
||||||
|
}>
|
||||||
|
<PageContainer>
|
||||||
|
<AttributesContextProvider>
|
||||||
|
<PageBody />
|
||||||
|
</AttributesContextProvider>
|
||||||
|
</PageContainer>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,39 +1,44 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { IconArrowRight, IconEdit, IconTrash } from "@tabler/icons-react";
|
import { IconArrowRight, IconEdit, IconTrash } from "@tabler/icons-react";
|
||||||
import { Center, Flex } from "@mantine/core";
|
import { Center, Flex } from "@mantine/core";
|
||||||
import { useModuleEditorContext } from "@/app/module-editor/[moduleId]/contexts/ModuleEditorContext";
|
|
||||||
import ActionIconWithTip from "@/components/ui/ActionIconWithTip/ActionIconWithTip";
|
import ActionIconWithTip from "@/components/ui/ActionIconWithTip/ActionIconWithTip";
|
||||||
import { AttributeSchema } from "@/lib/client";
|
import { AttributeSchema } from "@/lib/client";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
attribute: AttributeSchema;
|
attribute: AttributeSchema;
|
||||||
|
onUpdate: (attribute: AttributeSchema) => void;
|
||||||
|
onDelete: (attribute: AttributeSchema) => void;
|
||||||
|
addAttributeToModule?: (attribute: AttributeSchema) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AttributeTableActions: FC<Props> = ({ attribute }) => {
|
const AttributeTableActions: FC<Props> = ({
|
||||||
const { attributeActions } = useModuleEditorContext();
|
attribute,
|
||||||
|
onUpdate,
|
||||||
|
onDelete,
|
||||||
|
addAttributeToModule,
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Center>
|
<Center>
|
||||||
<Flex gap={"xs"}>
|
<Flex gap={"xs"}>
|
||||||
<ActionIconWithTip
|
<ActionIconWithTip
|
||||||
onClick={() => attributeActions.onUpdate(attribute)}
|
onClick={() => onUpdate(attribute)}
|
||||||
disabled={attribute.isBuiltIn}
|
disabled={attribute.isBuiltIn}
|
||||||
tipLabel={"Редактировать"}>
|
tipLabel={"Редактировать"}>
|
||||||
<IconEdit />
|
<IconEdit />
|
||||||
</ActionIconWithTip>
|
</ActionIconWithTip>
|
||||||
<ActionIconWithTip
|
<ActionIconWithTip
|
||||||
onClick={() => attributeActions.onDelete(attribute)}
|
onClick={() => onDelete(attribute)}
|
||||||
disabled={attribute.isBuiltIn}
|
disabled={attribute.isBuiltIn}
|
||||||
tipLabel={"Удалить"}>
|
tipLabel={"Удалить"}>
|
||||||
<IconTrash />
|
<IconTrash />
|
||||||
</ActionIconWithTip>
|
</ActionIconWithTip>
|
||||||
<ActionIconWithTip
|
{addAttributeToModule && (
|
||||||
onClick={() =>
|
<ActionIconWithTip
|
||||||
attributeActions.addAttributeToModule(attribute)
|
onClick={() => addAttributeToModule(attribute)}
|
||||||
}
|
tipLabel={"Добавить в модуль"}>
|
||||||
tipLabel={"Добавить в модуль"}>
|
<IconArrowRight />
|
||||||
<IconArrowRight />
|
</ActionIconWithTip>
|
||||||
</ActionIconWithTip>
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Center>
|
</Center>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,12 +3,14 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { DataTableColumn } from "mantine-datatable";
|
import { DataTableColumn } from "mantine-datatable";
|
||||||
import { Center } from "@mantine/core";
|
import { Center } from "@mantine/core";
|
||||||
|
import { useAttributesContext } from "@/app/attributes/contexts/AttributesContext";
|
||||||
import AttributeTableActions from "@/app/module-editor/[moduleId]/components/shared/AttributeTableActions/AttributeTableActions";
|
import AttributeTableActions from "@/app/module-editor/[moduleId]/components/shared/AttributeTableActions/AttributeTableActions";
|
||||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||||
import { AttributeSchema } from "@/lib/client";
|
import { AttributeSchema } from "@/lib/client";
|
||||||
|
|
||||||
const useAttributesTableColumns = () => {
|
const useAttributesTableColumns = () => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
const { attributesActions } = useAttributesContext();
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -30,7 +32,10 @@ const useAttributesTableColumns = () => {
|
|||||||
title: <Center>Действия</Center>,
|
title: <Center>Действия</Center>,
|
||||||
width: "0%",
|
width: "0%",
|
||||||
render: attribute => (
|
render: attribute => (
|
||||||
<AttributeTableActions attribute={attribute} />
|
<AttributeTableActions
|
||||||
|
attribute={attribute}
|
||||||
|
{...attributesActions}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
] as DataTableColumn<AttributeSchema>[],
|
] as DataTableColumn<AttributeSchema>[],
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
import { IconEditCircle, IconX } from "@tabler/icons-react";
|
||||||
import { Card, Group, Stack, Text, Title } from "@mantine/core";
|
import { Card, Group, Stack, Text, Title } from "@mantine/core";
|
||||||
import { useModuleEditorContext } from "@/app/module-editor/[moduleId]/contexts/ModuleEditorContext";
|
import { useModuleEditorContext } from "@/app/module-editor/[moduleId]/contexts/ModuleEditorContext";
|
||||||
import styles from "@/app/module-editor/[moduleId]/ModuleEditor.module.css";
|
import styles from "@/app/module-editor/[moduleId]/ModuleEditor.module.css";
|
||||||
@ -55,7 +55,7 @@ const ModuleAttribute: FC<Props> = ({ attribute }) => {
|
|||||||
"Редактировать название (только для данного модуля)"
|
"Редактировать название (только для данного модуля)"
|
||||||
}
|
}
|
||||||
onClick={() => onEditAttributeLabel(attribute)}>
|
onClick={() => onEditAttributeLabel(attribute)}>
|
||||||
<IconEdit />
|
<IconEditCircle />
|
||||||
</ActionIconWithTip>
|
</ActionIconWithTip>
|
||||||
<ActionIconWithTip
|
<ActionIconWithTip
|
||||||
tipLabel={"Удалить из модуля"}
|
tipLabel={"Удалить из модуля"}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export type AttributesActions = {
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
module?: ModuleSchemaOutput;
|
module?: ModuleSchemaOutput;
|
||||||
refetchModule: () => void;
|
refetchModule?: () => void;
|
||||||
refetchAttributes: () => void;
|
refetchAttributes: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ const useAttributesActions = (props: Props): AttributesActions => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onEditAttributeLabel = (attribute: AttributeSchema) => {
|
const onEditAttributeLabel = (attribute: AttributeSchema) => {
|
||||||
if (!module) return;
|
if (!props.module) return;
|
||||||
|
|
||||||
modals.openContextModal({
|
modals.openContextModal({
|
||||||
modal: "enterNameModal",
|
modal: "enterNameModal",
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import { notifications } from "@/lib/notifications";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
module?: ModuleSchemaOutput;
|
module?: ModuleSchemaOutput;
|
||||||
refetchModule: () => void;
|
refetchModule?: () => void;
|
||||||
refetchAttributes: () => void;
|
refetchAttributes: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ const useAttributesCrud = ({
|
|||||||
{
|
{
|
||||||
onSuccess: ({ message }) => {
|
onSuccess: ({ message }) => {
|
||||||
notifications.success({ message });
|
notifications.success({ message });
|
||||||
refetchModule();
|
refetchModule && refetchModule();
|
||||||
refetchAttributes();
|
refetchAttributes();
|
||||||
removeGetDealModuleAttrQuery();
|
removeGetDealModuleAttrQuery();
|
||||||
},
|
},
|
||||||
@ -139,8 +139,8 @@ const useAttributesCrud = ({
|
|||||||
...updateAttributeMutation(),
|
...updateAttributeMutation(),
|
||||||
onError,
|
onError,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
refetchModule && refetchModule();
|
||||||
refetchAttributes();
|
refetchAttributes();
|
||||||
refetchModule();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ const useAttributesCrud = ({
|
|||||||
...deleteAttributeMutation(),
|
...deleteAttributeMutation(),
|
||||||
onError,
|
onError,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
refetchModule();
|
refetchModule && refetchModule();
|
||||||
refetchAttributes();
|
refetchAttributes();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -4,13 +4,9 @@ import { useMemo } from "react";
|
|||||||
import { IconCheck, IconX } from "@tabler/icons-react";
|
import { IconCheck, IconX } from "@tabler/icons-react";
|
||||||
import { DataTableColumn } from "mantine-datatable";
|
import { DataTableColumn } from "mantine-datatable";
|
||||||
import { Box } from "@mantine/core";
|
import { Box } from "@mantine/core";
|
||||||
|
import AttributeDefaultValue from "@/components/ui/AttributeDefaultValue/AttributeDefaultValue";
|
||||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||||
import { ModuleAttributeSchema } from "@/lib/client";
|
import { ModuleAttributeSchema } from "@/lib/client";
|
||||||
import {
|
|
||||||
naiveDateTimeStringToUtc,
|
|
||||||
utcDateTimeToLocalString,
|
|
||||||
utcDateToLocalString,
|
|
||||||
} from "@/utils/datetime";
|
|
||||||
|
|
||||||
const useAttributesInnerTableColumns = () => {
|
const useAttributesInnerTableColumns = () => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
@ -35,26 +31,7 @@ const useAttributesInnerTableColumns = () => {
|
|||||||
{
|
{
|
||||||
title: "Значение по умолчанию",
|
title: "Значение по умолчанию",
|
||||||
accessor: "defaultValue",
|
accessor: "defaultValue",
|
||||||
render: attr => {
|
render: attr => <AttributeDefaultValue attribute={attr} />,
|
||||||
if (!attr.defaultValue) return <>-</>;
|
|
||||||
const value = attr.defaultValue;
|
|
||||||
if (value === null) return <>-</>;
|
|
||||||
|
|
||||||
const type = attr.type.type;
|
|
||||||
if (type === "datetime") {
|
|
||||||
return utcDateTimeToLocalString(
|
|
||||||
naiveDateTimeStringToUtc(value as string)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (type === "date") {
|
|
||||||
return utcDateToLocalString(value as string);
|
|
||||||
}
|
|
||||||
if (type === "bool") {
|
|
||||||
return value ? <IconCheck /> : <IconX />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>{value}</>;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Синхронизировано в группе",
|
title: "Синхронизировано в группе",
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {
|
|||||||
IconColumns,
|
IconColumns,
|
||||||
IconFileBarcode,
|
IconFileBarcode,
|
||||||
IconLayoutKanban,
|
IconLayoutKanban,
|
||||||
|
IconList,
|
||||||
IconUsers,
|
IconUsers,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import { ModuleNames } from "@/modules/modules";
|
import { ModuleNames } from "@/modules/modules";
|
||||||
@ -22,6 +23,12 @@ const linksData: LinkData[] = [
|
|||||||
href: "/modules",
|
href: "/modules",
|
||||||
moduleName: undefined,
|
moduleName: undefined,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: IconList,
|
||||||
|
label: "Атрибуты",
|
||||||
|
href: "/attributes",
|
||||||
|
moduleName: undefined,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: IconUsers,
|
icon: IconUsers,
|
||||||
label: "Клиенты",
|
label: "Клиенты",
|
||||||
|
|||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { IconCheck, IconX } from "@tabler/icons-react";
|
||||||
|
import { AttributeSchema } from "@/lib/client";
|
||||||
|
import {
|
||||||
|
naiveDateTimeStringToUtc,
|
||||||
|
utcDateTimeToLocalString,
|
||||||
|
utcDateToLocalString,
|
||||||
|
} from "@/utils/datetime";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
attribute: AttributeSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AttributeDefaultValue: FC<Props> = ({ attribute }) => {
|
||||||
|
if (!attribute.defaultValue) return <>-</>;
|
||||||
|
const value = attribute.defaultValue;
|
||||||
|
if (value === null) return <>-</>;
|
||||||
|
|
||||||
|
const type = attribute.type.type;
|
||||||
|
if (type === "datetime") {
|
||||||
|
return utcDateTimeToLocalString(
|
||||||
|
naiveDateTimeStringToUtc(value as string)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (type === "date") {
|
||||||
|
return utcDateToLocalString(value as string);
|
||||||
|
}
|
||||||
|
if (type === "bool") {
|
||||||
|
return value ? <IconCheck /> : <IconX />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>{value}</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AttributeDefaultValue;
|
||||||
Reference in New Issue
Block a user