feat: modules, products, services, services kits
This commit is contained in:
@ -7,7 +7,8 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"generate-client": "openapi-ts && prettier --write ./src/lib/client/**/*.ts && git add ./src/lib/client"
|
"generate-client": "openapi-ts && prettier --write ./src/lib/client/**/*.ts && git add ./src/lib/client",
|
||||||
|
"generate-modules": "sudo npx tsc ./src/modules/modulesFileGen/modulesFileGen.ts && mv -f ./src/modules/modulesFileGen/modulesFileGen.js ./src/modules/modulesFileGen/modulesFileGen.cjs && sudo node ./src/modules/modulesFileGen/modulesFileGen.cjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
@ -15,6 +16,7 @@
|
|||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@mantine/core": "8.1.2",
|
"@mantine/core": "8.1.2",
|
||||||
"@mantine/dates": "^8.2.7",
|
"@mantine/dates": "^8.2.7",
|
||||||
|
"@mantine/dropzone": "^8.3.1",
|
||||||
"@mantine/form": "^8.1.3",
|
"@mantine/form": "^8.1.3",
|
||||||
"@mantine/hooks": "8.1.2",
|
"@mantine/hooks": "8.1.2",
|
||||||
"@mantine/modals": "^8.2.1",
|
"@mantine/modals": "^8.2.1",
|
||||||
@ -31,6 +33,7 @@
|
|||||||
"date-fns-tz": "^3.2.0",
|
"date-fns-tz": "^3.2.0",
|
||||||
"dayjs": "^1.11.15",
|
"dayjs": "^1.11.15",
|
||||||
"framer-motion": "^12.23.7",
|
"framer-motion": "^12.23.7",
|
||||||
|
"handlebars": "^4.7.8",
|
||||||
"i18n-iso-countries": "^7.14.0",
|
"i18n-iso-countries": "^7.14.0",
|
||||||
"lexorank": "^1.0.5",
|
"lexorank": "^1.0.5",
|
||||||
"libphonenumber-js": "^1.12.10",
|
"libphonenumber-js": "^1.12.10",
|
||||||
|
|||||||
@ -3,11 +3,13 @@ import { IconMoodSad } from "@tabler/icons-react";
|
|||||||
import { Group, Pagination, Stack, Text } from "@mantine/core";
|
import { Group, Pagination, Stack, Text } from "@mantine/core";
|
||||||
import useDealsTableColumns from "@/app/deals/components/desktop/DealsTable/useDealsTableColumns";
|
import useDealsTableColumns from "@/app/deals/components/desktop/DealsTable/useDealsTableColumns";
|
||||||
import { useDealsContext } from "@/app/deals/contexts/DealsContext";
|
import { useDealsContext } from "@/app/deals/contexts/DealsContext";
|
||||||
|
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
||||||
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
||||||
import { useDrawersContext } from "@/drawers/DrawersContext";
|
import { useDrawersContext } from "@/drawers/DrawersContext";
|
||||||
import { DealSchema } from "@/lib/client";
|
import { DealSchema } from "@/lib/client";
|
||||||
|
|
||||||
const DealsTable: FC = () => {
|
const DealsTable: FC = () => {
|
||||||
|
const { selectedProject } = useProjectsContext();
|
||||||
const { deals, paginationInfo, page, setPage, sortingForm, dealsCrud } =
|
const { deals, paginationInfo, page, setPage, sortingForm, dealsCrud } =
|
||||||
useDealsContext();
|
useDealsContext();
|
||||||
const { openDrawer } = useDrawersContext();
|
const { openDrawer } = useDrawersContext();
|
||||||
@ -20,6 +22,7 @@ const DealsTable: FC = () => {
|
|||||||
value: deal,
|
value: deal,
|
||||||
onChange: deal => dealsCrud.onUpdate(deal.id, deal),
|
onChange: deal => dealsCrud.onUpdate(deal.id, deal),
|
||||||
onDelete: dealsCrud.onDelete,
|
onDelete: dealsCrud.onDelete,
|
||||||
|
project: selectedProject,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@ -35,7 +35,7 @@ const TopToolPanel: FC<Props> = ({ view, setView }) => {
|
|||||||
title: "Создание проекта",
|
title: "Создание проекта",
|
||||||
withCloseButton: true,
|
withCloseButton: true,
|
||||||
innerProps: {
|
innerProps: {
|
||||||
onChange: values => projectsCrud.onCreate(values.name),
|
onChange: projectsCrud.onCreate,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,7 +11,7 @@ const CreateBoardButton = () => {
|
|||||||
<Flex style={{ borderBottom: "2px solid gray" }}>
|
<Flex style={{ borderBottom: "2px solid gray" }}>
|
||||||
<InPlaceInput
|
<InPlaceInput
|
||||||
placeholder={"Название доски"}
|
placeholder={"Название доски"}
|
||||||
onChange={boardsCrud.onCreate}
|
onChange={name => boardsCrud.onCreate({ name })}
|
||||||
getChildren={startEditing => (
|
getChildren={startEditing => (
|
||||||
<Box
|
<Box
|
||||||
onClick={startEditing}
|
onClick={startEditing}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ const CreateCardButton = () => {
|
|||||||
const { dealsCrud } = useDealsContext();
|
const { dealsCrud } = useDealsContext();
|
||||||
|
|
||||||
const onSubmit = (values: CreateDealForm) => {
|
const onSubmit = (values: CreateDealForm) => {
|
||||||
dealsCrud.onCreate(values.name);
|
dealsCrud.onCreate(values);
|
||||||
setIsCreating(prevState => !prevState);
|
setIsCreating(prevState => !prevState);
|
||||||
setIsTransitionEnded(false);
|
setIsTransitionEnded(false);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const CreateStatusButton = () => {
|
|||||||
className={styles["inner-container"]}>
|
className={styles["inner-container"]}>
|
||||||
<InPlaceInput
|
<InPlaceInput
|
||||||
placeholder={"Название колонки"}
|
placeholder={"Название колонки"}
|
||||||
onChange={statusesCrud.onCreate}
|
onChange={name => statusesCrud.onCreate({ name })}
|
||||||
getChildren={startEditing => (
|
getChildren={startEditing => (
|
||||||
<Center
|
<Center
|
||||||
p={"sm"}
|
p={"sm"}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Box, Card, Group, Pill, Stack, Text } from "@mantine/core";
|
import { Box, Card, Group, Pill, Stack, Text } from "@mantine/core";
|
||||||
import { useDealsContext } from "@/app/deals/contexts/DealsContext";
|
import { useDealsContext } from "@/app/deals/contexts/DealsContext";
|
||||||
|
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
||||||
import { useDrawersContext } from "@/drawers/DrawersContext";
|
import { useDrawersContext } from "@/drawers/DrawersContext";
|
||||||
import { DealSchema } from "@/lib/client";
|
import { DealSchema } from "@/lib/client";
|
||||||
import styles from "./DealCard.module.css";
|
import styles from "./DealCard.module.css";
|
||||||
@ -9,6 +10,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DealCard = ({ deal }: Props) => {
|
const DealCard = ({ deal }: Props) => {
|
||||||
|
const { selectedProject } = useProjectsContext();
|
||||||
const { dealsCrud } = useDealsContext();
|
const { dealsCrud } = useDealsContext();
|
||||||
const { openDrawer } = useDrawersContext();
|
const { openDrawer } = useDrawersContext();
|
||||||
|
|
||||||
@ -19,6 +21,7 @@ const DealCard = ({ deal }: Props) => {
|
|||||||
value: deal,
|
value: deal,
|
||||||
onChange: deal => dealsCrud.onUpdate(deal.id, deal),
|
onChange: deal => dealsCrud.onUpdate(deal.id, deal),
|
||||||
onDelete: dealsCrud.onDelete,
|
onDelete: dealsCrud.onDelete,
|
||||||
|
project: selectedProject,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,9 +2,10 @@ import { FC } from "react";
|
|||||||
import { IconPlus } from "@tabler/icons-react";
|
import { IconPlus } from "@tabler/icons-react";
|
||||||
import { Box, Group, Text } from "@mantine/core";
|
import { Box, Group, Text } from "@mantine/core";
|
||||||
import { modals } from "@mantine/modals";
|
import { modals } from "@mantine/modals";
|
||||||
|
import { CreateBoardSchema } from "@/lib/client";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onCreateBoard: (name: string) => void;
|
onCreateBoard: (data: Partial<CreateBoardSchema>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CreateBoardButton: FC<Props> = ({ onCreateBoard }) => {
|
const CreateBoardButton: FC<Props> = ({ onCreateBoard }) => {
|
||||||
@ -14,7 +15,7 @@ const CreateBoardButton: FC<Props> = ({ onCreateBoard }) => {
|
|||||||
title: "Создание доски",
|
title: "Создание доски",
|
||||||
withCloseButton: true,
|
withCloseButton: true,
|
||||||
innerProps: {
|
innerProps: {
|
||||||
onChange: values => onCreateBoard(values.name),
|
onChange: values => onCreateBoard(values),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,12 +6,13 @@ import DealEditorBody from "@/app/deals/drawers/DealEditorDrawer/components/Deal
|
|||||||
import Header from "@/app/deals/drawers/DealEditorDrawer/components/Header";
|
import Header from "@/app/deals/drawers/DealEditorDrawer/components/Header";
|
||||||
import { DrawerProps } from "@/drawers/types";
|
import { DrawerProps } from "@/drawers/types";
|
||||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||||
import { DealSchema } from "@/lib/client";
|
import { DealSchema, ProjectSchema } from "@/lib/client";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value: DealSchema;
|
value: DealSchema;
|
||||||
onChange: (deal: DealSchema) => void;
|
onChange: (deal: DealSchema) => void;
|
||||||
onDelete: (deal: DealSchema, onSuccess: () => void) => void;
|
onDelete: (deal: DealSchema, onSuccess: () => void) => void;
|
||||||
|
project: ProjectSchema | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DealEditorDrawer: FC<DrawerProps<Props>> = ({
|
const DealEditorDrawer: FC<DrawerProps<Props>> = ({
|
||||||
@ -24,7 +25,7 @@ const DealEditorDrawer: FC<DrawerProps<Props>> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
size={isMobile ? "100%" : "40%"}
|
size={isMobile ? "100%" : "60%"}
|
||||||
position={"right"}
|
position={"right"}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
removeScrollProps={{ allowPinchZoom: true }}
|
removeScrollProps={{ allowPinchZoom: true }}
|
||||||
@ -49,6 +50,7 @@ const DealEditorDrawer: FC<DrawerProps<Props>> = ({
|
|||||||
props.onChange(deal);
|
props.onChange(deal);
|
||||||
}}
|
}}
|
||||||
onDelete={value => props.onDelete(value, onClose)}
|
onDelete={value => props.onDelete(value, onClose)}
|
||||||
|
project={props.project}
|
||||||
/>
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,17 +1,55 @@
|
|||||||
import React, { FC } from "react";
|
import React, { FC, ReactNode } from "react";
|
||||||
import { IconCircleDotted, IconEdit } from "@tabler/icons-react";
|
import { IconEdit } from "@tabler/icons-react";
|
||||||
import { Tabs, Text } from "@mantine/core";
|
import { motion } from "framer-motion";
|
||||||
|
import { Box, Tabs } from "@mantine/core";
|
||||||
import GeneralTab from "@/app/deals/drawers/DealEditorDrawer/tabs/GeneralTab/GeneralTab";
|
import GeneralTab from "@/app/deals/drawers/DealEditorDrawer/tabs/GeneralTab/GeneralTab";
|
||||||
import { DealSchema } from "@/lib/client";
|
import { DealSchema, ProjectSchema } from "@/lib/client";
|
||||||
|
import { MODULES } from "@/modules/modules";
|
||||||
import styles from "../DealEditorDrawer.module.css";
|
import styles from "../DealEditorDrawer.module.css";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value: DealSchema;
|
value: DealSchema;
|
||||||
onChange: (deal: DealSchema) => void;
|
onChange: (deal: DealSchema) => void;
|
||||||
onDelete: (deal: DealSchema) => void;
|
onDelete: (deal: DealSchema) => void;
|
||||||
|
project: ProjectSchema | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DealEditorBody: FC<Props> = props => {
|
const DealEditorBody: FC<Props> = props => {
|
||||||
|
const getTabPanel = (value: string, component: ReactNode): ReactNode => (
|
||||||
|
<Tabs.Panel
|
||||||
|
key={value}
|
||||||
|
value={value}>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ duration: 0.2 }}>
|
||||||
|
<Box
|
||||||
|
h={"100%"}
|
||||||
|
w={"100%"}>
|
||||||
|
{component}
|
||||||
|
</Box>
|
||||||
|
</motion.div>
|
||||||
|
</Tabs.Panel>
|
||||||
|
);
|
||||||
|
|
||||||
|
const getModuleTabs = () =>
|
||||||
|
props.project?.builtInModules.map(module => {
|
||||||
|
const moduleRender = MODULES[module.key].renderInfo;
|
||||||
|
return (
|
||||||
|
<Tabs.Tab
|
||||||
|
key={moduleRender.key}
|
||||||
|
value={moduleRender.key}
|
||||||
|
leftSection={moduleRender.icon}>
|
||||||
|
{moduleRender.label}
|
||||||
|
</Tabs.Tab>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const getModuleTabPanels = () =>
|
||||||
|
props.project?.builtInModules.map(module =>
|
||||||
|
getTabPanel(module.key, MODULES[module.key]?.getTab?.(props))
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultValue="general"
|
defaultValue="general"
|
||||||
@ -22,19 +60,11 @@ const DealEditorBody: FC<Props> = props => {
|
|||||||
leftSection={<IconEdit />}>
|
leftSection={<IconEdit />}>
|
||||||
Общая информация
|
Общая информация
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
<Tabs.Tab
|
{getModuleTabs()}
|
||||||
value="mock"
|
|
||||||
leftSection={<IconCircleDotted />}>
|
|
||||||
Mock
|
|
||||||
</Tabs.Tab>
|
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
|
|
||||||
<Tabs.Panel value="general">
|
{getTabPanel("general", <GeneralTab {...props} />)}
|
||||||
<GeneralTab {...props} />
|
{getModuleTabPanels()}
|
||||||
</Tabs.Panel>
|
|
||||||
<Tabs.Panel value="mock">
|
|
||||||
<Text>mock</Text>
|
|
||||||
</Tabs.Panel>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { Stack, Text, TextInput } from "@mantine/core";
|
import { Stack, Text, TextInput } from "@mantine/core";
|
||||||
import { useForm } from "@mantine/form";
|
import { useForm } from "@mantine/form";
|
||||||
import Footer from "@/app/deals/drawers/DealEditorDrawer/tabs/GeneralTab/Footer";
|
import Footer from "@/app/deals/drawers/DealEditorDrawer/tabs/GeneralTab/components/Footer";
|
||||||
import BoardSelect from "@/components/selects/BoardSelect/BoardSelect";
|
import BoardSelect from "@/components/selects/BoardSelect/BoardSelect";
|
||||||
import StatusSelect from "@/components/selects/StatusSelect/StatusSelect";
|
import StatusSelect from "@/components/selects/StatusSelect/StatusSelect";
|
||||||
import { BoardSchema, DealSchema, StatusSchema } from "@/lib/client";
|
import { BoardSchema, DealSchema, StatusSchema } from "@/lib/client";
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { IconEdit } from "@tabler/icons-react";
|
import { IconBlocks, IconEdit } from "@tabler/icons-react";
|
||||||
import { Tabs } from "@mantine/core";
|
import { Tabs } from "@mantine/core";
|
||||||
import GeneralTab from "@/app/deals/drawers/ProjectEditorDrawer/tabs/GeneralTab/GeneralTab";
|
import {
|
||||||
|
GeneralTab,
|
||||||
|
ModulesTab,
|
||||||
|
} from "@/app/deals/drawers/ProjectEditorDrawer/tabs";
|
||||||
import { ProjectSchema } from "@/lib/client";
|
import { ProjectSchema } from "@/lib/client";
|
||||||
import styles from "../ProjectEditorDrawer.module.css";
|
import styles from "../ProjectEditorDrawer.module.css";
|
||||||
|
|
||||||
@ -22,10 +25,18 @@ const ProjectEditorBody: FC<Props> = props => {
|
|||||||
leftSection={<IconEdit />}>
|
leftSection={<IconEdit />}>
|
||||||
Общая информация
|
Общая информация
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
|
<Tabs.Tab
|
||||||
|
value="modules"
|
||||||
|
leftSection={<IconBlocks />}>
|
||||||
|
Модули
|
||||||
|
</Tabs.Tab>
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
<Tabs.Panel value="general">
|
<Tabs.Panel value="general">
|
||||||
<GeneralTab {...props} />
|
<GeneralTab {...props} />
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
|
<Tabs.Panel value="modules">
|
||||||
|
<ModulesTab {...props} />
|
||||||
|
</Tabs.Panel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { Stack, TextInput } from "@mantine/core";
|
import { Stack, TextInput } from "@mantine/core";
|
||||||
import { useForm } from "@mantine/form";
|
import { useForm } from "@mantine/form";
|
||||||
import Footer from "@/app/deals/drawers/ProjectEditorDrawer/tabs/GeneralTab/Footer";
|
import Footer from "@/app/deals/drawers/ProjectEditorDrawer/tabs/GeneralTab/components/Footer";
|
||||||
import { ProjectSchema } from "@/lib/client";
|
import { ProjectSchema } from "@/lib/client";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -10,7 +10,7 @@ type Props = {
|
|||||||
onDelete: (value: ProjectSchema) => void;
|
onDelete: (value: ProjectSchema) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const GeneralTab: FC<Props> = ({ value, onChange, onDelete }) => {
|
export const GeneralTab: FC<Props> = ({ value, onChange, onDelete }) => {
|
||||||
const form = useForm<ProjectSchema>({
|
const form = useForm<ProjectSchema>({
|
||||||
initialValues: value,
|
initialValues: value,
|
||||||
validate: {
|
validate: {
|
||||||
@ -38,5 +38,3 @@ const GeneralTab: FC<Props> = ({ value, onChange, onDelete }) => {
|
|||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default GeneralTab;
|
|
||||||
|
|||||||
@ -0,0 +1,41 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { isEqual } from "lodash";
|
||||||
|
import { Button, Stack } from "@mantine/core";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import { ProjectSchema } from "@/lib/client";
|
||||||
|
import ModulesTable from "./components/ModulesTable";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value: ProjectSchema;
|
||||||
|
onChange: (value: ProjectSchema) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ModulesTab: FC<Props> = ({ value, onChange }) => {
|
||||||
|
const form = useForm<ProjectSchema>({
|
||||||
|
initialValues: value,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = (values: ProjectSchema) => {
|
||||||
|
onChange(values);
|
||||||
|
form.setInitialValues(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={form.onSubmit(onSubmit)}>
|
||||||
|
<Stack p={"md"}>
|
||||||
|
<ModulesTable
|
||||||
|
selectedRecords={form.values.builtInModules}
|
||||||
|
onSelectedRecordsChange={modules =>
|
||||||
|
form.setFieldValue("builtInModules", modules)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type={"submit"}
|
||||||
|
variant={"default"}
|
||||||
|
disabled={isEqual(value, form.values)}>
|
||||||
|
Сохранить
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { FC, useRef } from "react";
|
||||||
|
import { Divider, Stack } from "@mantine/core";
|
||||||
|
import useModulesTableColumns from "@/app/deals/drawers/ProjectEditorDrawer/tabs/ModulesTab/hooks/useModulesTableColumns";
|
||||||
|
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
||||||
|
import { BuiltInModuleSchema } from "@/lib/client";
|
||||||
|
import { MODULES } from "@/modules/modules";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
selectedRecords: BuiltInModuleSchema[];
|
||||||
|
onSelectedRecordsChange: (records: BuiltInModuleSchema[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModulesTable: FC<Props> = props => {
|
||||||
|
const columns = useModulesTableColumns();
|
||||||
|
const modules = useRef(
|
||||||
|
Object.values(MODULES).map(module => module.modelData)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap={0}>
|
||||||
|
<Divider />
|
||||||
|
<BaseTable
|
||||||
|
records={modules.current}
|
||||||
|
columns={columns}
|
||||||
|
verticalSpacing={"md"}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModulesTable;
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
|
import { DataTableColumn } from "mantine-datatable";
|
||||||
|
import { BuiltInModuleSchema } from "@/lib/client";
|
||||||
|
|
||||||
|
const useModulesTableColumns = () => {
|
||||||
|
return useMemo(
|
||||||
|
() =>
|
||||||
|
[
|
||||||
|
{
|
||||||
|
accessor: "label",
|
||||||
|
title: "Название",
|
||||||
|
width: "30%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Описание",
|
||||||
|
accessor: "description",
|
||||||
|
width: "70%",
|
||||||
|
},
|
||||||
|
] as DataTableColumn<BuiltInModuleSchema>[],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useModulesTableColumns;
|
||||||
2
src/app/deals/drawers/ProjectEditorDrawer/tabs/index.ts
Normal file
2
src/app/deals/drawers/ProjectEditorDrawer/tabs/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { GeneralTab } from "./GeneralTab/GeneralTab";
|
||||||
|
export { ModulesTab } from "./ModulesTab/ModulesTab";
|
||||||
@ -13,7 +13,7 @@ const CreateProjectButton: FC = () => {
|
|||||||
title: "Создание проекта",
|
title: "Создание проекта",
|
||||||
withCloseButton: true,
|
withCloseButton: true,
|
||||||
innerProps: {
|
innerProps: {
|
||||||
onChange: values => projectsCrud.onCreate(values.name),
|
onChange: projectsCrud.onCreate,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const CreateStatusButton: FC = () => {
|
|||||||
title: "Создание колонки",
|
title: "Создание колонки",
|
||||||
withCloseButton: true,
|
withCloseButton: true,
|
||||||
innerProps: {
|
innerProps: {
|
||||||
onChange: values => statusesCrud.onCreate(values.name),
|
onChange: statusesCrud.onCreate,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
108
src/components/selects/ObjectMultiSelect/ObjectMultiSelect.tsx
Normal file
108
src/components/selects/ObjectMultiSelect/ObjectMultiSelect.tsx
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import { groupBy, omit } from "lodash";
|
||||||
|
import { MultiSelect, MultiSelectProps } from "@mantine/core";
|
||||||
|
|
||||||
|
interface ObjectWithIdAndName {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MultiselectObjectType<T> = T;
|
||||||
|
|
||||||
|
type ControlledValueProps<T> = {
|
||||||
|
value: MultiselectObjectType<T>[];
|
||||||
|
onChange: (value: MultiselectObjectType<T>[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CustomLabelAndKeyProps<T> = {
|
||||||
|
getLabelFn: (item: MultiselectObjectType<T>) => string;
|
||||||
|
getValueFn: (item: MultiselectObjectType<T>) => string;
|
||||||
|
};
|
||||||
|
type RestProps<T> = {
|
||||||
|
defaultValue?: MultiselectObjectType<T>[];
|
||||||
|
onChange: (value: MultiselectObjectType<T>[]) => void;
|
||||||
|
data: MultiselectObjectType<T>[];
|
||||||
|
groupBy?: (item: MultiselectObjectType<T>) => string;
|
||||||
|
filterBy?: (item: MultiselectObjectType<T>) => boolean;
|
||||||
|
};
|
||||||
|
const defaultGetLabelFn = <T extends { name: string }>(item: T): string => {
|
||||||
|
return item.name;
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
|
||||||
|
return item.id.toString();
|
||||||
|
};
|
||||||
|
export type ObjectMultiSelectProps<T> = (RestProps<T> &
|
||||||
|
Partial<ControlledValueProps<T>>) &
|
||||||
|
Omit<MultiSelectProps, "value" | "onChange" | "data"> &
|
||||||
|
(T extends ObjectWithIdAndName
|
||||||
|
? Partial<CustomLabelAndKeyProps<T>>
|
||||||
|
: CustomLabelAndKeyProps<T>);
|
||||||
|
|
||||||
|
const ObjectMultiSelect = <T,>(props: ObjectMultiSelectProps<T>) => {
|
||||||
|
const isControlled = "value" in props;
|
||||||
|
const haveGetValueFn = "getValueFn" in props;
|
||||||
|
const haveGetLabelFn = "getLabelFn" in props;
|
||||||
|
|
||||||
|
const [internalValue, setInternalValue] = useState<
|
||||||
|
MultiselectObjectType<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[]) => {
|
||||||
|
const objects = props.data.filter(item =>
|
||||||
|
event.includes(getValueFn(item))
|
||||||
|
);
|
||||||
|
if (isControlled) {
|
||||||
|
props.onChange(objects);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setInternalValue(objects);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isControlled || !internalValue) return;
|
||||||
|
props.onChange(internalValue);
|
||||||
|
}, [internalValue]);
|
||||||
|
|
||||||
|
const restProps = omit(props, "getValueFn", "getLabelFn", "filterBy");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MultiSelect
|
||||||
|
{...restProps}
|
||||||
|
value={value.map(item => getValueFn(item))}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
data={data}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ObjectMultiSelect;
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { groupBy, omit } from "lodash";
|
import { groupBy, omit } from "lodash";
|
||||||
import { Select, SelectProps } from "@mantine/core";
|
import { Select, SelectProps } from "@mantine/core";
|
||||||
@ -33,6 +35,7 @@ const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
|
|||||||
if (!item) return item;
|
if (!item) return item;
|
||||||
return item.id.toString();
|
return item.id.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ObjectSelectProps<T> = (RestProps<T> &
|
export type ObjectSelectProps<T> = (RestProps<T> &
|
||||||
Partial<ControlledValueProps<T>>) &
|
Partial<ControlledValueProps<T>>) &
|
||||||
Omit<SelectProps, "value" | "onChange" | "data"> &
|
Omit<SelectProps, "value" | "onChange" | "data"> &
|
||||||
|
|||||||
@ -4,11 +4,10 @@ import { DataTable, DataTableProps } from "mantine-datatable";
|
|||||||
function BaseTable<T>(props: DataTableProps<T>) {
|
function BaseTable<T>(props: DataTableProps<T>) {
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<DataTable
|
||||||
withTableBorder={false}
|
withTableBorder
|
||||||
withRowBorders
|
withRowBorders
|
||||||
striped={false}
|
striped={false}
|
||||||
verticalAlign={"center"}
|
verticalAlign={"center"}
|
||||||
borderRadius={"lg"}
|
|
||||||
backgroundColor={"transparent"}
|
backgroundColor={"transparent"}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|||||||
119
src/components/ui/ImageDropzone/ImageDropzone.tsx
Normal file
119
src/components/ui/ImageDropzone/ImageDropzone.tsx
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { IconPhoto, IconUpload, IconX } from "@tabler/icons-react";
|
||||||
|
import { omit } from "lodash";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Fieldset,
|
||||||
|
Flex,
|
||||||
|
Group,
|
||||||
|
Image,
|
||||||
|
Loader,
|
||||||
|
rem,
|
||||||
|
Text,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { Dropzone, DropzoneProps, FileWithPath } from "@mantine/dropzone";
|
||||||
|
import UseImageDropzone from "./types";
|
||||||
|
|
||||||
|
interface RestProps {
|
||||||
|
imageDropzone: UseImageDropzone;
|
||||||
|
onDrop: (files: FileWithPath[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = Omit<DropzoneProps, "onDrop"> & RestProps;
|
||||||
|
|
||||||
|
const ImageDropzone: FC<Props> = (props: Props) => {
|
||||||
|
const { showDropzone, setShowDropzone, isLoading, imageUrlInputProps } =
|
||||||
|
props.imageDropzone;
|
||||||
|
|
||||||
|
const restProps = omit(props, ["imageDropzone"]);
|
||||||
|
|
||||||
|
const getDropzone = () => (
|
||||||
|
<Dropzone
|
||||||
|
{...restProps}
|
||||||
|
accept={[
|
||||||
|
"image/png",
|
||||||
|
"image/jpeg",
|
||||||
|
"image/gif",
|
||||||
|
"image/bmp",
|
||||||
|
"image/tiff",
|
||||||
|
"image/x-icon",
|
||||||
|
"image/webp",
|
||||||
|
"image/svg+xml",
|
||||||
|
"image/heic",
|
||||||
|
]}
|
||||||
|
multiple={false}
|
||||||
|
onDrop={props.onDrop}>
|
||||||
|
<Group
|
||||||
|
justify="center"
|
||||||
|
gap="xl"
|
||||||
|
style={{ pointerEvents: "none" }}>
|
||||||
|
<Dropzone.Accept>
|
||||||
|
<IconUpload
|
||||||
|
style={{
|
||||||
|
width: rem(52),
|
||||||
|
height: rem(52),
|
||||||
|
color: "var(--mantine-color-blue-6)",
|
||||||
|
}}
|
||||||
|
stroke={1.5}
|
||||||
|
/>
|
||||||
|
</Dropzone.Accept>
|
||||||
|
<Dropzone.Reject>
|
||||||
|
<IconX
|
||||||
|
style={{
|
||||||
|
width: rem(52),
|
||||||
|
height: rem(52),
|
||||||
|
color: "var(--mantine-color-red-6)",
|
||||||
|
}}
|
||||||
|
stroke={1.5}
|
||||||
|
/>
|
||||||
|
</Dropzone.Reject>
|
||||||
|
<Dropzone.Idle>
|
||||||
|
<IconPhoto
|
||||||
|
style={{
|
||||||
|
width: rem(52),
|
||||||
|
height: rem(52),
|
||||||
|
color: "var(--mantine-color-dimmed)",
|
||||||
|
}}
|
||||||
|
stroke={1.5}
|
||||||
|
/>
|
||||||
|
</Dropzone.Idle>
|
||||||
|
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<Text
|
||||||
|
size="xl"
|
||||||
|
inline>
|
||||||
|
Перенесите изображение или нажмите чтоб выбрать файл
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Group>
|
||||||
|
</Dropzone>
|
||||||
|
);
|
||||||
|
|
||||||
|
const getBody = () => {
|
||||||
|
if (imageUrlInputProps?.value && !showDropzone) {
|
||||||
|
return <Image src={imageUrlInputProps.value} />;
|
||||||
|
}
|
||||||
|
return getDropzone();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
gap={rem(10)}
|
||||||
|
direction={"column"}>
|
||||||
|
<Fieldset legend={"Изображение"}>
|
||||||
|
<Flex justify={"center"}>
|
||||||
|
{isLoading ? <Loader /> : getBody()}
|
||||||
|
</Flex>
|
||||||
|
</Fieldset>
|
||||||
|
{!showDropzone && (
|
||||||
|
<Button
|
||||||
|
onClick={() => setShowDropzone(true)}
|
||||||
|
variant={"default"}>
|
||||||
|
Заменить изображение {}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImageDropzone;
|
||||||
12
src/components/ui/ImageDropzone/types.ts
Normal file
12
src/components/ui/ImageDropzone/types.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
import BaseFormInputProps from "@/utils/baseFormInputProps";
|
||||||
|
|
||||||
|
type UseImageDropzone = {
|
||||||
|
showDropzone: boolean;
|
||||||
|
setShowDropzone: Dispatch<SetStateAction<boolean>>;
|
||||||
|
isLoading: boolean;
|
||||||
|
setIsLoading: Dispatch<SetStateAction<boolean>>;
|
||||||
|
imageUrlInputProps?: BaseFormInputProps<string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UseImageDropzone;
|
||||||
38
src/hooks/cruds/baseCrud/getCommonQueryClient.ts
Normal file
38
src/hooks/cruds/baseCrud/getCommonQueryClient.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
import { HttpValidationError } from "@/lib/client";
|
||||||
|
import { notifications } from "@/lib/notifications";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
key: string;
|
||||||
|
queryKey: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCommonQueryClient = ({ key, queryKey }: Props) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const onError = (
|
||||||
|
error: AxiosError<HttpValidationError>,
|
||||||
|
_: any,
|
||||||
|
context: any
|
||||||
|
) => {
|
||||||
|
console.error(error);
|
||||||
|
notifications.error({
|
||||||
|
message: error.response?.data?.detail as string | undefined,
|
||||||
|
});
|
||||||
|
if (context?.previous) {
|
||||||
|
queryClient.setQueryData(queryKey, context.previous);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSettled = () => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
predicate: (query: { queryKey: any }) =>
|
||||||
|
query.queryKey[0]?._id === key,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return { queryClient, onError, onSettled };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getCommonQueryClient;
|
||||||
@ -1,14 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import { useMutation, UseMutationOptions } from "@tanstack/react-query";
|
||||||
useMutation,
|
|
||||||
UseMutationOptions,
|
|
||||||
useQueryClient,
|
|
||||||
} from "@tanstack/react-query";
|
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { Text } from "@mantine/core";
|
import { Text } from "@mantine/core";
|
||||||
import { modals } from "@mantine/modals";
|
import { modals } from "@mantine/modals";
|
||||||
|
import getCommonQueryClient from "@/hooks/cruds/baseCrud/getCommonQueryClient";
|
||||||
import { HttpValidationError } from "@/lib/client";
|
import { HttpValidationError } from "@/lib/client";
|
||||||
import { notifications } from "@/lib/notifications";
|
|
||||||
import { sortByLexorank } from "@/utils/lexorank";
|
import { sortByLexorank } from "@/utils/lexorank";
|
||||||
import {
|
import {
|
||||||
BaseEntity,
|
BaseEntity,
|
||||||
@ -17,10 +13,17 @@ import {
|
|||||||
UpdateMutationOptions,
|
UpdateMutationOptions,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
type CrudOperations<TEntity, TUpdate> = {
|
type CrudOperations<
|
||||||
onCreate: (name: string) => void;
|
TEntity,
|
||||||
onUpdate: (id: number, update: TUpdate) => void;
|
TUpdate,
|
||||||
onDelete: (entity: TEntity) => void;
|
TCreate,
|
||||||
|
HasGetCreateEntity extends boolean,
|
||||||
|
> = {
|
||||||
|
onCreate: HasGetCreateEntity extends true
|
||||||
|
? (data: Partial<TCreate>, onSuccess?: () => void) => void
|
||||||
|
: (data: TCreate, onSuccess?: () => void) => void;
|
||||||
|
onUpdate: (id: number, update: TUpdate, onSuccess?: () => void) => void;
|
||||||
|
onDelete: (entity: TEntity, onSuccess?: () => void) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UseEntityOperationsProps<TEntity extends BaseEntity, TUpdate, TCreate> = {
|
type UseEntityOperationsProps<TEntity extends BaseEntity, TUpdate, TCreate> = {
|
||||||
@ -43,12 +46,32 @@ type UseEntityOperationsProps<TEntity extends BaseEntity, TUpdate, TCreate> = {
|
|||||||
DeleteMutationOptions
|
DeleteMutationOptions
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
getCreateEntity: (name: string) => TCreate | null;
|
getCreateEntity?: (data: Partial<TCreate>) => TCreate | null;
|
||||||
getUpdateEntity: (oldEntity: TEntity, update: TUpdate) => TEntity;
|
getUpdateEntity: (oldEntity: TEntity, update: TUpdate) => TEntity;
|
||||||
getDeleteConfirmTitle: (entity: TEntity) => string;
|
getDeleteConfirmTitle: (entity: TEntity) => string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useCrudOperations = <
|
function useCrudOperations<
|
||||||
|
TEntity extends BaseEntity,
|
||||||
|
TUpdate extends object,
|
||||||
|
TCreate extends object,
|
||||||
|
>(
|
||||||
|
props: UseEntityOperationsProps<TEntity, TUpdate, TCreate> & {
|
||||||
|
getCreateEntity: (data: Partial<TCreate>) => TCreate | null;
|
||||||
|
}
|
||||||
|
): CrudOperations<TEntity, TUpdate, TCreate, true>;
|
||||||
|
|
||||||
|
function useCrudOperations<
|
||||||
|
TEntity extends BaseEntity,
|
||||||
|
TUpdate extends object,
|
||||||
|
TCreate extends object,
|
||||||
|
>(
|
||||||
|
props: UseEntityOperationsProps<TEntity, TUpdate, TCreate> & {
|
||||||
|
getCreateEntity?: undefined;
|
||||||
|
}
|
||||||
|
): CrudOperations<TEntity, TUpdate, TCreate, false>;
|
||||||
|
|
||||||
|
function useCrudOperations<
|
||||||
TEntity extends BaseEntity,
|
TEntity extends BaseEntity,
|
||||||
TUpdate extends object,
|
TUpdate extends object,
|
||||||
TCreate extends object,
|
TCreate extends object,
|
||||||
@ -61,30 +84,14 @@ const useCrudOperations = <
|
|||||||
getDeleteConfirmTitle,
|
getDeleteConfirmTitle,
|
||||||
}: UseEntityOperationsProps<TEntity, TUpdate, TCreate>): CrudOperations<
|
}: UseEntityOperationsProps<TEntity, TUpdate, TCreate>): CrudOperations<
|
||||||
TEntity,
|
TEntity,
|
||||||
TUpdate
|
TUpdate,
|
||||||
> => {
|
TCreate,
|
||||||
const queryClient = useQueryClient();
|
boolean
|
||||||
|
> {
|
||||||
const onError = (
|
const { queryClient, onError, onSettled } = getCommonQueryClient({
|
||||||
error: AxiosError<HttpValidationError>,
|
queryKey,
|
||||||
_: any,
|
key,
|
||||||
context: any
|
});
|
||||||
) => {
|
|
||||||
console.error(error);
|
|
||||||
notifications.error({
|
|
||||||
message: error.response?.data?.detail as string | undefined,
|
|
||||||
});
|
|
||||||
if (context?.previous) {
|
|
||||||
queryClient.setQueryData(queryKey, context.previous);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSettled = () => {
|
|
||||||
queryClient.invalidateQueries({
|
|
||||||
predicate: (query: { queryKey: any }) =>
|
|
||||||
query.queryKey[0]?._id === key,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const createMutation = useMutation({
|
const createMutation = useMutation({
|
||||||
...mutations.create,
|
...mutations.create,
|
||||||
@ -102,6 +109,7 @@ const useCrudOperations = <
|
|||||||
const previous = queryClient.getQueryData(queryKey);
|
const previous = queryClient.getQueryData(queryKey);
|
||||||
|
|
||||||
queryClient.setQueryData(queryKey, (old: { items: TEntity[] }) => {
|
queryClient.setQueryData(queryKey, (old: { items: TEntity[] }) => {
|
||||||
|
if (!old) return;
|
||||||
let updated = old.items.map((entity: TEntity) =>
|
let updated = old.items.map((entity: TEntity) =>
|
||||||
entity.id === update.id
|
entity.id === update.id
|
||||||
? getUpdateEntity(entity, update)
|
? getUpdateEntity(entity, update)
|
||||||
@ -145,26 +153,43 @@ const useCrudOperations = <
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const onCreate = (name: string) => {
|
const onCreate = (data: any, onSuccess?: () => void) => {
|
||||||
const entity = getCreateEntity(name);
|
let entity: TCreate;
|
||||||
if (!entity) return;
|
if (getCreateEntity) {
|
||||||
createMutation.mutate({
|
const result = getCreateEntity(data);
|
||||||
body: {
|
if (!result) return;
|
||||||
entity,
|
entity = result;
|
||||||
|
} else {
|
||||||
|
entity = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
createMutation.mutate(
|
||||||
|
{
|
||||||
|
body: {
|
||||||
|
entity,
|
||||||
|
},
|
||||||
|
path: undefined,
|
||||||
|
query: undefined,
|
||||||
},
|
},
|
||||||
path: undefined,
|
{
|
||||||
query: undefined,
|
onSuccess,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onUpdate = async (id: number, update: TUpdate) => {
|
const onUpdate = (id: number, update: TUpdate, onSuccess?: () => void) => {
|
||||||
updateMutation.mutate({
|
updateMutation.mutate(
|
||||||
body: {
|
{
|
||||||
entity: update,
|
body: {
|
||||||
|
entity: update,
|
||||||
|
},
|
||||||
|
path: { pk: id },
|
||||||
|
query: undefined,
|
||||||
},
|
},
|
||||||
path: { pk: id },
|
{
|
||||||
query: undefined,
|
onSuccess,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDelete = (entity: TEntity, onSuccess?: () => void) => {
|
const onDelete = (entity: TEntity, onSuccess?: () => void) => {
|
||||||
@ -175,13 +200,14 @@ const useCrudOperations = <
|
|||||||
),
|
),
|
||||||
confirmProps: { color: "red" },
|
confirmProps: { color: "red" },
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
deleteMutation.mutate({ path: { pk: entity.id } } as any);
|
deleteMutation.mutate({ path: { pk: entity.id } } as any, {
|
||||||
onSuccess && onSuccess();
|
onSuccess,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return { onCreate, onUpdate, onDelete };
|
return { onCreate, onUpdate, onDelete };
|
||||||
};
|
}
|
||||||
|
|
||||||
export default useCrudOperations;
|
export default useCrudOperations;
|
||||||
|
|||||||
@ -19,7 +19,7 @@ type UseBoardsOperationsProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type BoardsCrud = {
|
export type BoardsCrud = {
|
||||||
onCreate: (name: string) => void;
|
onCreate: (data: Partial<CreateBoardSchema>) => void;
|
||||||
onUpdate: (boardId: number, board: UpdateBoardSchema) => void;
|
onUpdate: (boardId: number, board: UpdateBoardSchema) => void;
|
||||||
onDelete: (board: BoardSchema) => void;
|
onDelete: (board: BoardSchema) => void;
|
||||||
};
|
};
|
||||||
@ -38,14 +38,14 @@ export const useBoardsCrud = ({
|
|||||||
update: updateBoardMutation(),
|
update: updateBoardMutation(),
|
||||||
delete: deleteBoardMutation(),
|
delete: deleteBoardMutation(),
|
||||||
},
|
},
|
||||||
getCreateEntity: name => {
|
getCreateEntity: data => {
|
||||||
if (!projectId) return null;
|
if (!projectId) return null;
|
||||||
const lastBoard = getMaxByLexorank(boards);
|
const lastBoard = getMaxByLexorank(boards);
|
||||||
const newLexorank = getNewLexorank(
|
const newLexorank = getNewLexorank(
|
||||||
lastBoard ? LexoRank.parse(lastBoard.lexorank) : null
|
lastBoard ? LexoRank.parse(lastBoard.lexorank) : null
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
name,
|
name: data.name!,
|
||||||
projectId,
|
projectId,
|
||||||
lexorank: newLexorank.toString(),
|
lexorank: newLexorank.toString(),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -21,7 +21,7 @@ type UseDealsOperationsProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type DealsCrud = {
|
export type DealsCrud = {
|
||||||
onCreate: (name: string) => void;
|
onCreate: (entity: Partial<CreateDealSchema>) => void;
|
||||||
onUpdate: (dealId: number, deal: UpdateDealSchema) => void;
|
onUpdate: (dealId: number, deal: UpdateDealSchema) => void;
|
||||||
onDelete: (deal: DealSchema, onSuccess?: () => void) => void;
|
onDelete: (deal: DealSchema, onSuccess?: () => void) => void;
|
||||||
};
|
};
|
||||||
@ -40,7 +40,7 @@ export const useDealsCrud = ({
|
|||||||
update: updateDealMutation(),
|
update: updateDealMutation(),
|
||||||
delete: deleteDealMutation(),
|
delete: deleteDealMutation(),
|
||||||
},
|
},
|
||||||
getCreateEntity: name => {
|
getCreateEntity: data => {
|
||||||
if (!boardId || statuses.length === 0) return null;
|
if (!boardId || statuses.length === 0) return null;
|
||||||
const firstStatus = statuses[0];
|
const firstStatus = statuses[0];
|
||||||
const filteredDeals = deals.filter(
|
const filteredDeals = deals.filter(
|
||||||
@ -55,7 +55,7 @@ export const useDealsCrud = ({
|
|||||||
firstDeal ? LexoRank.parse(firstDeal.lexorank) : null
|
firstDeal ? LexoRank.parse(firstDeal.lexorank) : null
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
name,
|
name: data.name!,
|
||||||
boardId,
|
boardId,
|
||||||
statusId: firstStatus.id,
|
statusId: firstStatus.id,
|
||||||
lexorank: newLexorank.toString(),
|
lexorank: newLexorank.toString(),
|
||||||
|
|||||||
@ -15,7 +15,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ProjectsCrud = {
|
export type ProjectsCrud = {
|
||||||
onCreate: (name: string) => void;
|
onCreate: (data: Partial<CreateProjectSchema>) => void;
|
||||||
onUpdate: (projectId: number, project: UpdateProjectSchema) => void;
|
onUpdate: (projectId: number, project: UpdateProjectSchema) => void;
|
||||||
onDelete: (project: ProjectSchema, onSuccess?: () => void) => void;
|
onDelete: (project: ProjectSchema, onSuccess?: () => void) => void;
|
||||||
};
|
};
|
||||||
@ -33,7 +33,7 @@ export const useProjectsCrud = ({ queryKey }: Props): ProjectsCrud => {
|
|||||||
update: updateProjectMutation(),
|
update: updateProjectMutation(),
|
||||||
delete: deleteProjectMutation(),
|
delete: deleteProjectMutation(),
|
||||||
},
|
},
|
||||||
getCreateEntity: name => ({ name }),
|
getCreateEntity: data => ({ name: data.name! }),
|
||||||
getUpdateEntity: (old, update) => ({
|
getUpdateEntity: (old, update) => ({
|
||||||
...old,
|
...old,
|
||||||
name: update.name ?? old.name,
|
name: update.name ?? old.name,
|
||||||
|
|||||||
@ -19,7 +19,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type StatusesCrud = {
|
export type StatusesCrud = {
|
||||||
onCreate: (name: string) => void;
|
onCreate: (entity: Partial<CreateStatusSchema>) => void;
|
||||||
onUpdate: (statusId: number, status: UpdateStatusSchema) => void;
|
onUpdate: (statusId: number, status: UpdateStatusSchema) => void;
|
||||||
onDelete: (status: StatusSchema) => void;
|
onDelete: (status: StatusSchema) => void;
|
||||||
};
|
};
|
||||||
@ -41,14 +41,14 @@ export const useStatusesCrud = ({
|
|||||||
update: updateStatusMutation(),
|
update: updateStatusMutation(),
|
||||||
delete: deleteStatusMutation(),
|
delete: deleteStatusMutation(),
|
||||||
},
|
},
|
||||||
getCreateEntity: name => {
|
getCreateEntity: data => {
|
||||||
if (!boardId) return null;
|
if (!boardId) return null;
|
||||||
const lastBoard = getMaxByLexorank(statuses);
|
const lastBoard = getMaxByLexorank(statuses);
|
||||||
const newLexorank = getNewLexorank(
|
const newLexorank = getNewLexorank(
|
||||||
lastBoard ? LexoRank.parse(lastBoard.lexorank) : null
|
lastBoard ? LexoRank.parse(lastBoard.lexorank) : null
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
name,
|
name: data.name!,
|
||||||
boardId,
|
boardId,
|
||||||
lexorank: newLexorank.toString(),
|
lexorank: newLexorank.toString(),
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -3,15 +3,36 @@
|
|||||||
import type { Client, Options as ClientOptions, TDataShape } from "./client";
|
import type { Client, Options as ClientOptions, TDataShape } from "./client";
|
||||||
import { client as _heyApiClient } from "./client.gen";
|
import { client as _heyApiClient } from "./client.gen";
|
||||||
import type {
|
import type {
|
||||||
|
AddKitToDealProductData,
|
||||||
|
AddKitToDealProductErrors,
|
||||||
|
AddKitToDealProductResponses,
|
||||||
CreateBoardData,
|
CreateBoardData,
|
||||||
CreateBoardErrors,
|
CreateBoardErrors,
|
||||||
CreateBoardResponses,
|
CreateBoardResponses,
|
||||||
CreateDealData,
|
CreateDealData,
|
||||||
CreateDealErrors,
|
CreateDealErrors,
|
||||||
|
CreateDealProductData,
|
||||||
|
CreateDealProductErrors,
|
||||||
|
CreateDealProductResponses,
|
||||||
|
CreateDealProductServiceData,
|
||||||
|
CreateDealProductServiceErrors,
|
||||||
|
CreateDealProductServiceResponses,
|
||||||
CreateDealResponses,
|
CreateDealResponses,
|
||||||
|
CreateDealServiceData,
|
||||||
|
CreateDealServiceErrors,
|
||||||
|
CreateDealServiceResponses,
|
||||||
|
CreateProductData,
|
||||||
|
CreateProductErrors,
|
||||||
|
CreateProductResponses,
|
||||||
CreateProjectData,
|
CreateProjectData,
|
||||||
CreateProjectErrors,
|
CreateProjectErrors,
|
||||||
CreateProjectResponses,
|
CreateProjectResponses,
|
||||||
|
CreateServiceData,
|
||||||
|
CreateServiceErrors,
|
||||||
|
CreateServiceResponses,
|
||||||
|
CreateServicesKitData,
|
||||||
|
CreateServicesKitErrors,
|
||||||
|
CreateServicesKitResponses,
|
||||||
CreateStatusData,
|
CreateStatusData,
|
||||||
CreateStatusErrors,
|
CreateStatusErrors,
|
||||||
CreateStatusResponses,
|
CreateStatusResponses,
|
||||||
@ -20,21 +41,57 @@ import type {
|
|||||||
DeleteBoardResponses,
|
DeleteBoardResponses,
|
||||||
DeleteDealData,
|
DeleteDealData,
|
||||||
DeleteDealErrors,
|
DeleteDealErrors,
|
||||||
|
DeleteDealProductData,
|
||||||
|
DeleteDealProductErrors,
|
||||||
|
DeleteDealProductResponses,
|
||||||
|
DeleteDealProductServiceData,
|
||||||
|
DeleteDealProductServiceErrors,
|
||||||
|
DeleteDealProductServiceResponses,
|
||||||
DeleteDealResponses,
|
DeleteDealResponses,
|
||||||
|
DeleteDealServiceData,
|
||||||
|
DeleteDealServiceErrors,
|
||||||
|
DeleteDealServiceResponses,
|
||||||
|
DeleteProductData,
|
||||||
|
DeleteProductErrors,
|
||||||
|
DeleteProductResponses,
|
||||||
DeleteProjectData,
|
DeleteProjectData,
|
||||||
DeleteProjectErrors,
|
DeleteProjectErrors,
|
||||||
DeleteProjectResponses,
|
DeleteProjectResponses,
|
||||||
|
DeleteServiceData,
|
||||||
|
DeleteServiceErrors,
|
||||||
|
DeleteServiceResponses,
|
||||||
|
DeleteServicesKitData,
|
||||||
|
DeleteServicesKitErrors,
|
||||||
|
DeleteServicesKitResponses,
|
||||||
DeleteStatusData,
|
DeleteStatusData,
|
||||||
DeleteStatusErrors,
|
DeleteStatusErrors,
|
||||||
DeleteStatusResponses,
|
DeleteStatusResponses,
|
||||||
|
DuplicateProductServicesData,
|
||||||
|
DuplicateProductServicesErrors,
|
||||||
|
DuplicateProductServicesResponses,
|
||||||
GetBoardsData,
|
GetBoardsData,
|
||||||
GetBoardsErrors,
|
GetBoardsErrors,
|
||||||
GetBoardsResponses,
|
GetBoardsResponses,
|
||||||
|
GetBuiltInModulesData,
|
||||||
|
GetBuiltInModulesResponses,
|
||||||
|
GetDealProductsData,
|
||||||
|
GetDealProductsErrors,
|
||||||
|
GetDealProductsResponses,
|
||||||
GetDealsData,
|
GetDealsData,
|
||||||
GetDealsErrors,
|
GetDealsErrors,
|
||||||
|
GetDealServicesData,
|
||||||
|
GetDealServicesErrors,
|
||||||
|
GetDealServicesResponses,
|
||||||
GetDealsResponses,
|
GetDealsResponses,
|
||||||
|
GetProductsData,
|
||||||
|
GetProductsErrors,
|
||||||
|
GetProductsResponses,
|
||||||
GetProjectsData,
|
GetProjectsData,
|
||||||
GetProjectsResponses,
|
GetProjectsResponses,
|
||||||
|
GetServicesData,
|
||||||
|
GetServicesKitsData,
|
||||||
|
GetServicesKitsResponses,
|
||||||
|
GetServicesResponses,
|
||||||
GetStatusesData,
|
GetStatusesData,
|
||||||
GetStatusesErrors,
|
GetStatusesErrors,
|
||||||
GetStatusesResponses,
|
GetStatusesResponses,
|
||||||
@ -43,45 +100,115 @@ import type {
|
|||||||
UpdateBoardResponses,
|
UpdateBoardResponses,
|
||||||
UpdateDealData,
|
UpdateDealData,
|
||||||
UpdateDealErrors,
|
UpdateDealErrors,
|
||||||
|
UpdateDealProductData,
|
||||||
|
UpdateDealProductErrors,
|
||||||
|
UpdateDealProductResponses,
|
||||||
|
UpdateDealProductServiceData,
|
||||||
|
UpdateDealProductServiceErrors,
|
||||||
|
UpdateDealProductServiceResponses,
|
||||||
UpdateDealResponses,
|
UpdateDealResponses,
|
||||||
|
UpdateDealServiceData,
|
||||||
|
UpdateDealServiceErrors,
|
||||||
|
UpdateDealServiceResponses,
|
||||||
|
UpdateProductData,
|
||||||
|
UpdateProductErrors,
|
||||||
|
UpdateProductResponses,
|
||||||
UpdateProjectData,
|
UpdateProjectData,
|
||||||
UpdateProjectErrors,
|
UpdateProjectErrors,
|
||||||
UpdateProjectResponses,
|
UpdateProjectResponses,
|
||||||
|
UpdateServiceData,
|
||||||
|
UpdateServiceErrors,
|
||||||
|
UpdateServiceResponses,
|
||||||
|
UpdateServicesKitData,
|
||||||
|
UpdateServicesKitErrors,
|
||||||
|
UpdateServicesKitResponses,
|
||||||
UpdateStatusData,
|
UpdateStatusData,
|
||||||
UpdateStatusErrors,
|
UpdateStatusErrors,
|
||||||
UpdateStatusResponses,
|
UpdateStatusResponses,
|
||||||
} from "./types.gen";
|
} from "./types.gen";
|
||||||
import {
|
import {
|
||||||
|
zAddKitToDealProductData,
|
||||||
|
zAddKitToDealProductResponse,
|
||||||
zCreateBoardData,
|
zCreateBoardData,
|
||||||
zCreateBoardResponse2,
|
zCreateBoardResponse2,
|
||||||
zCreateDealData,
|
zCreateDealData,
|
||||||
|
zCreateDealProductData,
|
||||||
|
zCreateDealProductResponse2,
|
||||||
|
zCreateDealProductServiceData,
|
||||||
|
zCreateDealProductServiceResponse,
|
||||||
zCreateDealResponse2,
|
zCreateDealResponse2,
|
||||||
|
zCreateDealServiceData,
|
||||||
|
zCreateDealServiceResponse2,
|
||||||
|
zCreateProductData,
|
||||||
|
zCreateProductResponse2,
|
||||||
zCreateProjectData,
|
zCreateProjectData,
|
||||||
zCreateProjectResponse2,
|
zCreateProjectResponse2,
|
||||||
|
zCreateServiceData,
|
||||||
|
zCreateServiceResponse2,
|
||||||
|
zCreateServicesKitData,
|
||||||
|
zCreateServicesKitResponse2,
|
||||||
zCreateStatusData,
|
zCreateStatusData,
|
||||||
zCreateStatusResponse2,
|
zCreateStatusResponse2,
|
||||||
zDeleteBoardData,
|
zDeleteBoardData,
|
||||||
zDeleteBoardResponse2,
|
zDeleteBoardResponse2,
|
||||||
zDeleteDealData,
|
zDeleteDealData,
|
||||||
|
zDeleteDealProductData,
|
||||||
|
zDeleteDealProductResponse2,
|
||||||
|
zDeleteDealProductServiceData,
|
||||||
|
zDeleteDealProductServiceResponse,
|
||||||
zDeleteDealResponse2,
|
zDeleteDealResponse2,
|
||||||
|
zDeleteDealServiceData,
|
||||||
|
zDeleteDealServiceResponse2,
|
||||||
|
zDeleteProductData,
|
||||||
|
zDeleteProductResponse2,
|
||||||
zDeleteProjectData,
|
zDeleteProjectData,
|
||||||
zDeleteProjectResponse2,
|
zDeleteProjectResponse2,
|
||||||
|
zDeleteServiceData,
|
||||||
|
zDeleteServiceResponse2,
|
||||||
|
zDeleteServicesKitData,
|
||||||
|
zDeleteServicesKitResponse2,
|
||||||
zDeleteStatusData,
|
zDeleteStatusData,
|
||||||
zDeleteStatusResponse2,
|
zDeleteStatusResponse2,
|
||||||
|
zDuplicateProductServicesData,
|
||||||
|
zDuplicateProductServicesResponse,
|
||||||
zGetBoardsData,
|
zGetBoardsData,
|
||||||
zGetBoardsResponse2,
|
zGetBoardsResponse2,
|
||||||
|
zGetBuiltInModulesData,
|
||||||
|
zGetBuiltInModulesResponse,
|
||||||
|
zGetDealProductsData,
|
||||||
|
zGetDealProductsResponse2,
|
||||||
zGetDealsData,
|
zGetDealsData,
|
||||||
|
zGetDealServicesData,
|
||||||
|
zGetDealServicesResponse2,
|
||||||
zGetDealsResponse2,
|
zGetDealsResponse2,
|
||||||
|
zGetProductsData,
|
||||||
|
zGetProductsResponse2,
|
||||||
zGetProjectsData,
|
zGetProjectsData,
|
||||||
zGetProjectsResponse2,
|
zGetProjectsResponse2,
|
||||||
|
zGetServicesData,
|
||||||
|
zGetServicesKitsData,
|
||||||
|
zGetServicesKitsResponse,
|
||||||
|
zGetServicesResponse2,
|
||||||
zGetStatusesData,
|
zGetStatusesData,
|
||||||
zGetStatusesResponse2,
|
zGetStatusesResponse2,
|
||||||
zUpdateBoardData,
|
zUpdateBoardData,
|
||||||
zUpdateBoardResponse2,
|
zUpdateBoardResponse2,
|
||||||
zUpdateDealData,
|
zUpdateDealData,
|
||||||
|
zUpdateDealProductData,
|
||||||
|
zUpdateDealProductResponse2,
|
||||||
|
zUpdateDealProductServiceData,
|
||||||
|
zUpdateDealProductServiceResponse,
|
||||||
zUpdateDealResponse2,
|
zUpdateDealResponse2,
|
||||||
|
zUpdateDealServiceData,
|
||||||
|
zUpdateDealServiceResponse2,
|
||||||
|
zUpdateProductData,
|
||||||
|
zUpdateProductResponse2,
|
||||||
zUpdateProjectData,
|
zUpdateProjectData,
|
||||||
zUpdateProjectResponse2,
|
zUpdateProjectResponse2,
|
||||||
|
zUpdateServiceData,
|
||||||
|
zUpdateServiceResponse2,
|
||||||
|
zUpdateServicesKitData,
|
||||||
|
zUpdateServicesKitResponse2,
|
||||||
zUpdateStatusData,
|
zUpdateStatusData,
|
||||||
zUpdateStatusResponse2,
|
zUpdateStatusResponse2,
|
||||||
} from "./zod.gen";
|
} from "./zod.gen";
|
||||||
@ -303,6 +430,29 @@ export const updateDeal = <ThrowOnError extends boolean = false>(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Built In Modules
|
||||||
|
*/
|
||||||
|
export const getBuiltInModules = <ThrowOnError extends boolean = false>(
|
||||||
|
options?: Options<GetBuiltInModulesData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options?.client ?? _heyApiClient).get<
|
||||||
|
GetBuiltInModulesResponses,
|
||||||
|
unknown,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zGetBuiltInModulesData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zGetBuiltInModulesResponse.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/module/built-in/",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Projects
|
* Get Projects
|
||||||
*/
|
*/
|
||||||
@ -502,3 +652,634 @@ export const updateStatus = <ThrowOnError extends boolean = false>(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Deal Products
|
||||||
|
*/
|
||||||
|
export const getDealProducts = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<GetDealProductsData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).get<
|
||||||
|
GetDealProductsResponses,
|
||||||
|
GetDealProductsErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zGetDealProductsData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zGetDealProductsResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-product/{dealId}",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Deal Product
|
||||||
|
*/
|
||||||
|
export const createDealProduct = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<CreateDealProductData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).post<
|
||||||
|
CreateDealProductResponses,
|
||||||
|
CreateDealProductErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zCreateDealProductData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zCreateDealProductResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-product/",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Deal Product
|
||||||
|
*/
|
||||||
|
export const deleteDealProduct = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<DeleteDealProductData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).delete<
|
||||||
|
DeleteDealProductResponses,
|
||||||
|
DeleteDealProductErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zDeleteDealProductData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zDeleteDealProductResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-product/{dealId}/product/{productId}",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Deal Product
|
||||||
|
*/
|
||||||
|
export const updateDealProduct = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<UpdateDealProductData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).patch<
|
||||||
|
UpdateDealProductResponses,
|
||||||
|
UpdateDealProductErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zUpdateDealProductData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zUpdateDealProductResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-product/{dealId}/product/{productId}",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Kit To Deal Product
|
||||||
|
*/
|
||||||
|
export const addKitToDealProduct = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<AddKitToDealProductData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).post<
|
||||||
|
AddKitToDealProductResponses,
|
||||||
|
AddKitToDealProductErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zAddKitToDealProductData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zAddKitToDealProductResponse.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-product/add-services-kit",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Deal Product Service
|
||||||
|
*/
|
||||||
|
export const createDealProductService = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<CreateDealProductServiceData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).post<
|
||||||
|
CreateDealProductServiceResponses,
|
||||||
|
CreateDealProductServiceErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zCreateDealProductServiceData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zCreateDealProductServiceResponse.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-product/service",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Deal Product Service
|
||||||
|
*/
|
||||||
|
export const deleteDealProductService = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<DeleteDealProductServiceData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).delete<
|
||||||
|
DeleteDealProductServiceResponses,
|
||||||
|
DeleteDealProductServiceErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zDeleteDealProductServiceData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zDeleteDealProductServiceResponse.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-product/{dealId}/product/{productId}/service/{serviceId}",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Deal Product Service
|
||||||
|
*/
|
||||||
|
export const updateDealProductService = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<UpdateDealProductServiceData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).patch<
|
||||||
|
UpdateDealProductServiceResponses,
|
||||||
|
UpdateDealProductServiceErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zUpdateDealProductServiceData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zUpdateDealProductServiceResponse.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-product/{dealId}/product/{productId}/service/{serviceId}",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy Product Services
|
||||||
|
*/
|
||||||
|
export const duplicateProductServices = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<DuplicateProductServicesData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).post<
|
||||||
|
DuplicateProductServicesResponses,
|
||||||
|
DuplicateProductServicesErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zDuplicateProductServicesData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zDuplicateProductServicesResponse.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-product/services/duplicate",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Deal Services
|
||||||
|
*/
|
||||||
|
export const getDealServices = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<GetDealServicesData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).get<
|
||||||
|
GetDealServicesResponses,
|
||||||
|
GetDealServicesErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zGetDealServicesData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zGetDealServicesResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-service/{dealId}",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Deal Service
|
||||||
|
*/
|
||||||
|
export const createDealService = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<CreateDealServiceData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).post<
|
||||||
|
CreateDealServiceResponses,
|
||||||
|
CreateDealServiceErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zCreateDealServiceData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zCreateDealServiceResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-service/",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Deal Service
|
||||||
|
*/
|
||||||
|
export const deleteDealService = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<DeleteDealServiceData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).delete<
|
||||||
|
DeleteDealServiceResponses,
|
||||||
|
DeleteDealServiceErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zDeleteDealServiceData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zDeleteDealServiceResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-service/{dealId}/service/{serviceId}",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Deal Service
|
||||||
|
*/
|
||||||
|
export const updateDealService = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<UpdateDealServiceData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).patch<
|
||||||
|
UpdateDealServiceResponses,
|
||||||
|
UpdateDealServiceErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zUpdateDealServiceData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zUpdateDealServiceResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/deal-service/{dealId}/service/{serviceId}",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Products
|
||||||
|
*/
|
||||||
|
export const getProducts = <ThrowOnError extends boolean = false>(
|
||||||
|
options?: Options<GetProductsData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options?.client ?? _heyApiClient).get<
|
||||||
|
GetProductsResponses,
|
||||||
|
GetProductsErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zGetProductsData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zGetProductsResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/product/",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Product
|
||||||
|
*/
|
||||||
|
export const createProduct = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<CreateProductData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).post<
|
||||||
|
CreateProductResponses,
|
||||||
|
CreateProductErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zCreateProductData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zCreateProductResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/product/",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Product
|
||||||
|
*/
|
||||||
|
export const deleteProduct = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<DeleteProductData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).delete<
|
||||||
|
DeleteProductResponses,
|
||||||
|
DeleteProductErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zDeleteProductData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zDeleteProductResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/product/{pk}",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Product
|
||||||
|
*/
|
||||||
|
export const updateProduct = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<UpdateProductData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).patch<
|
||||||
|
UpdateProductResponses,
|
||||||
|
UpdateProductErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zUpdateProductData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zUpdateProductResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/product/{pk}",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Services
|
||||||
|
*/
|
||||||
|
export const getServices = <ThrowOnError extends boolean = false>(
|
||||||
|
options?: Options<GetServicesData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options?.client ?? _heyApiClient).get<
|
||||||
|
GetServicesResponses,
|
||||||
|
unknown,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zGetServicesData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zGetServicesResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/service/",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Service
|
||||||
|
*/
|
||||||
|
export const createService = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<CreateServiceData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).post<
|
||||||
|
CreateServiceResponses,
|
||||||
|
CreateServiceErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zCreateServiceData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zCreateServiceResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/service/",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Service
|
||||||
|
*/
|
||||||
|
export const deleteService = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<DeleteServiceData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).delete<
|
||||||
|
DeleteServiceResponses,
|
||||||
|
DeleteServiceErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zDeleteServiceData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zDeleteServiceResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/service/{pk}",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Service
|
||||||
|
*/
|
||||||
|
export const updateService = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<UpdateServiceData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).patch<
|
||||||
|
UpdateServiceResponses,
|
||||||
|
UpdateServiceErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zUpdateServiceData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zUpdateServiceResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/service/{pk}",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Services Kits
|
||||||
|
*/
|
||||||
|
export const getServicesKits = <ThrowOnError extends boolean = false>(
|
||||||
|
options?: Options<GetServicesKitsData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options?.client ?? _heyApiClient).get<
|
||||||
|
GetServicesKitsResponses,
|
||||||
|
unknown,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zGetServicesKitsData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zGetServicesKitsResponse.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/services-kit/",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Services Kit
|
||||||
|
*/
|
||||||
|
export const createServicesKit = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<CreateServicesKitData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).post<
|
||||||
|
CreateServicesKitResponses,
|
||||||
|
CreateServicesKitErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zCreateServicesKitData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zCreateServicesKitResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/services-kit/",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Services Kit
|
||||||
|
*/
|
||||||
|
export const deleteServicesKit = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<DeleteServicesKitData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).delete<
|
||||||
|
DeleteServicesKitResponses,
|
||||||
|
DeleteServicesKitErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zDeleteServicesKitData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zDeleteServicesKitResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/services-kit/{pk}",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Services Kit
|
||||||
|
*/
|
||||||
|
export const updateServicesKit = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<UpdateServicesKitData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).patch<
|
||||||
|
UpdateServicesKitResponses,
|
||||||
|
UpdateServicesKitErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
requestValidator: async data => {
|
||||||
|
return await zUpdateServicesKitData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: "json",
|
||||||
|
responseValidator: async data => {
|
||||||
|
return await zUpdateServicesKitResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: "/modules/fulfillment-base/services-kit/{pk}",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,17 @@ export const zBoardSchema = z.object({
|
|||||||
projectId: z.int(),
|
projectId: z.int(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BuiltInModuleSchema
|
||||||
|
*/
|
||||||
|
export const zBuiltInModuleSchema = z.object({
|
||||||
|
id: z.int(),
|
||||||
|
key: z.string(),
|
||||||
|
label: z.string(),
|
||||||
|
iconName: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CreateBoardSchema
|
* CreateBoardSchema
|
||||||
*/
|
*/
|
||||||
@ -36,6 +47,104 @@ export const zCreateBoardResponse = z.object({
|
|||||||
entity: zBoardSchema,
|
entity: zBoardSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateDealProductSchema
|
||||||
|
*/
|
||||||
|
export const zCreateDealProductSchema = z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
productId: z.int(),
|
||||||
|
quantity: z.int(),
|
||||||
|
comment: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateDealProductRequest
|
||||||
|
*/
|
||||||
|
export const zCreateDealProductRequest = z.object({
|
||||||
|
entity: zCreateDealProductSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProductSchema
|
||||||
|
*/
|
||||||
|
export const zProductSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
article: z.string(),
|
||||||
|
factoryArticle: z.string(),
|
||||||
|
brand: z.union([z.string(), z.null()]),
|
||||||
|
color: z.union([z.string(), z.null()]),
|
||||||
|
composition: z.union([z.string(), z.null()]),
|
||||||
|
size: z.union([z.string(), z.null()]),
|
||||||
|
additionalInfo: z.union([z.string(), z.null()]),
|
||||||
|
id: z.int(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServiceCategorySchema
|
||||||
|
*/
|
||||||
|
export const zServiceCategorySchema = z.object({
|
||||||
|
id: z.int(),
|
||||||
|
name: z.string(),
|
||||||
|
dealServiceRank: z.string(),
|
||||||
|
productServiceRank: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServicePriceRangeSchema
|
||||||
|
*/
|
||||||
|
export const zServicePriceRangeSchema = z.object({
|
||||||
|
id: z.union([z.int(), z.null()]),
|
||||||
|
fromQuantity: z.int(),
|
||||||
|
toQuantity: z.int(),
|
||||||
|
price: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServiceSchema
|
||||||
|
*/
|
||||||
|
export const zServiceSchema = z.object({
|
||||||
|
id: z.int(),
|
||||||
|
name: z.string(),
|
||||||
|
category: zServiceCategorySchema,
|
||||||
|
price: z.number(),
|
||||||
|
serviceType: z.int(),
|
||||||
|
priceRanges: z.array(zServicePriceRangeSchema),
|
||||||
|
cost: z.union([z.number(), z.null()]),
|
||||||
|
lexorank: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProductServiceSchema
|
||||||
|
*/
|
||||||
|
export const zProductServiceSchema = z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
productId: z.int(),
|
||||||
|
serviceId: z.int(),
|
||||||
|
service: zServiceSchema,
|
||||||
|
price: z.number(),
|
||||||
|
isFixedPrice: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DealProductSchema
|
||||||
|
*/
|
||||||
|
export const zDealProductSchema = z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
productId: z.int(),
|
||||||
|
product: zProductSchema,
|
||||||
|
quantity: z.int(),
|
||||||
|
comment: z.string(),
|
||||||
|
productServices: z.array(zProductServiceSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateDealProductResponse
|
||||||
|
*/
|
||||||
|
export const zCreateDealProductResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
entity: zDealProductSchema,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CreateDealSchema
|
* CreateDealSchema
|
||||||
*/
|
*/
|
||||||
@ -84,6 +193,97 @@ export const zCreateDealResponse = z.object({
|
|||||||
entity: zDealSchema,
|
entity: zDealSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateDealServiceSchema
|
||||||
|
*/
|
||||||
|
export const zCreateDealServiceSchema = z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
serviceId: z.int(),
|
||||||
|
quantity: z.int(),
|
||||||
|
price: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateDealServiceRequest
|
||||||
|
*/
|
||||||
|
export const zCreateDealServiceRequest = z.object({
|
||||||
|
entity: zCreateDealServiceSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DealServiceSchema
|
||||||
|
*/
|
||||||
|
export const zDealServiceSchema = z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
serviceId: z.int(),
|
||||||
|
service: zServiceSchema,
|
||||||
|
quantity: z.int(),
|
||||||
|
price: z.number(),
|
||||||
|
isFixedPrice: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateDealServiceResponse
|
||||||
|
*/
|
||||||
|
export const zCreateDealServiceResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
entity: zDealServiceSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateProductSchema
|
||||||
|
*/
|
||||||
|
export const zCreateProductSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
article: z.string(),
|
||||||
|
factoryArticle: z.string(),
|
||||||
|
brand: z.union([z.string(), z.null()]),
|
||||||
|
color: z.union([z.string(), z.null()]),
|
||||||
|
composition: z.union([z.string(), z.null()]),
|
||||||
|
size: z.union([z.string(), z.null()]),
|
||||||
|
additionalInfo: z.union([z.string(), z.null()]),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateProductRequest
|
||||||
|
*/
|
||||||
|
export const zCreateProductRequest = z.object({
|
||||||
|
entity: zCreateProductSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateProductResponse
|
||||||
|
*/
|
||||||
|
export const zCreateProductResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
entity: zProductSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateProductServiceSchema
|
||||||
|
*/
|
||||||
|
export const zCreateProductServiceSchema = z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
productId: z.int(),
|
||||||
|
serviceId: z.int(),
|
||||||
|
price: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateProductServiceRequest
|
||||||
|
*/
|
||||||
|
export const zCreateProductServiceRequest = z.object({
|
||||||
|
entity: zCreateProductServiceSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateProductServiceResponse
|
||||||
|
*/
|
||||||
|
export const zCreateProductServiceResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
entity: zProductServiceSchema,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CreateProjectSchema
|
* CreateProjectSchema
|
||||||
*/
|
*/
|
||||||
@ -104,6 +304,7 @@ export const zCreateProjectRequest = z.object({
|
|||||||
export const zProjectSchema = z.object({
|
export const zProjectSchema = z.object({
|
||||||
id: z.int(),
|
id: z.int(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
|
builtInModules: z.array(zBuiltInModuleSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,6 +315,69 @@ export const zCreateProjectResponse = z.object({
|
|||||||
entity: zProjectSchema,
|
entity: zProjectSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateServiceSchema
|
||||||
|
*/
|
||||||
|
export const zCreateServiceSchema = z.object({
|
||||||
|
id: z.int(),
|
||||||
|
name: z.string(),
|
||||||
|
category: zServiceCategorySchema,
|
||||||
|
price: z.number(),
|
||||||
|
serviceType: z.int(),
|
||||||
|
priceRanges: z.array(zServicePriceRangeSchema),
|
||||||
|
cost: z.union([z.number(), z.null()]),
|
||||||
|
lexorank: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateServiceRequest
|
||||||
|
*/
|
||||||
|
export const zCreateServiceRequest = z.object({
|
||||||
|
entity: zCreateServiceSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateServiceResponse
|
||||||
|
*/
|
||||||
|
export const zCreateServiceResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
entity: zServiceSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateServicesKitSchema
|
||||||
|
*/
|
||||||
|
export const zCreateServicesKitSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
serviceType: z.int(),
|
||||||
|
servicesIds: z.array(z.int()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateServicesKitRequest
|
||||||
|
*/
|
||||||
|
export const zCreateServicesKitRequest = z.object({
|
||||||
|
entity: zCreateServicesKitSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ServicesKitSchema
|
||||||
|
*/
|
||||||
|
export const zServicesKitSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
serviceType: z.int(),
|
||||||
|
id: z.int(),
|
||||||
|
services: z.array(zServiceSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateServicesKitResponse
|
||||||
|
*/
|
||||||
|
export const zCreateServicesKitResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
entity: zServicesKitSchema,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CreateStatusSchema
|
* CreateStatusSchema
|
||||||
*/
|
*/
|
||||||
@ -138,6 +402,22 @@ export const zCreateStatusResponse = z.object({
|
|||||||
entity: zStatusSchema,
|
entity: zStatusSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DealProductAddKitRequest
|
||||||
|
*/
|
||||||
|
export const zDealProductAddKitRequest = z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
productId: z.int(),
|
||||||
|
kitId: z.int(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DealProductAddKitResponse
|
||||||
|
*/
|
||||||
|
export const zDealProductAddKitResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DeleteBoardResponse
|
* DeleteBoardResponse
|
||||||
*/
|
*/
|
||||||
@ -145,6 +425,13 @@ export const zDeleteBoardResponse = z.object({
|
|||||||
message: z.string(),
|
message: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeleteDealProductResponse
|
||||||
|
*/
|
||||||
|
export const zDeleteDealProductResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DeleteDealResponse
|
* DeleteDealResponse
|
||||||
*/
|
*/
|
||||||
@ -152,6 +439,27 @@ export const zDeleteDealResponse = z.object({
|
|||||||
message: z.string(),
|
message: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeleteDealServiceResponse
|
||||||
|
*/
|
||||||
|
export const zDeleteDealServiceResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeleteProductResponse
|
||||||
|
*/
|
||||||
|
export const zDeleteProductResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeleteProductServiceResponse
|
||||||
|
*/
|
||||||
|
export const zDeleteProductServiceResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DeleteProjectResponse
|
* DeleteProjectResponse
|
||||||
*/
|
*/
|
||||||
@ -159,6 +467,20 @@ export const zDeleteProjectResponse = z.object({
|
|||||||
message: z.string(),
|
message: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeleteServiceResponse
|
||||||
|
*/
|
||||||
|
export const zDeleteServiceResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeleteServicesKitResponse
|
||||||
|
*/
|
||||||
|
export const zDeleteServicesKitResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DeleteStatusResponse
|
* DeleteStatusResponse
|
||||||
*/
|
*/
|
||||||
@ -166,6 +488,13 @@ export const zDeleteStatusResponse = z.object({
|
|||||||
message: z.string(),
|
message: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetAllBuiltInModulesResponse
|
||||||
|
*/
|
||||||
|
export const zGetAllBuiltInModulesResponse = z.object({
|
||||||
|
items: z.array(zBuiltInModuleSchema),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetBoardsResponse
|
* GetBoardsResponse
|
||||||
*/
|
*/
|
||||||
@ -173,6 +502,20 @@ export const zGetBoardsResponse = z.object({
|
|||||||
items: z.array(zBoardSchema),
|
items: z.array(zBoardSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetDealProductsResponse
|
||||||
|
*/
|
||||||
|
export const zGetDealProductsResponse = z.object({
|
||||||
|
items: z.array(zDealProductSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetDealServicesResponse
|
||||||
|
*/
|
||||||
|
export const zGetDealServicesResponse = z.object({
|
||||||
|
items: z.array(zDealServiceSchema),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PaginationInfoSchema
|
* PaginationInfoSchema
|
||||||
*/
|
*/
|
||||||
@ -189,6 +532,13 @@ export const zGetDealsResponse = z.object({
|
|||||||
paginationInfo: zPaginationInfoSchema,
|
paginationInfo: zPaginationInfoSchema,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetProductsResponse
|
||||||
|
*/
|
||||||
|
export const zGetProductsResponse = z.object({
|
||||||
|
items: z.array(zProductSchema),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetProjectsResponse
|
* GetProjectsResponse
|
||||||
*/
|
*/
|
||||||
@ -196,6 +546,20 @@ export const zGetProjectsResponse = z.object({
|
|||||||
items: z.array(zProjectSchema),
|
items: z.array(zProjectSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetServicesKitResponse
|
||||||
|
*/
|
||||||
|
export const zGetServicesKitResponse = z.object({
|
||||||
|
items: z.array(zServicesKitSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetServicesResponse
|
||||||
|
*/
|
||||||
|
export const zGetServicesResponse = z.object({
|
||||||
|
items: z.array(zServiceSchema),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetStatusesResponse
|
* GetStatusesResponse
|
||||||
*/
|
*/
|
||||||
@ -219,6 +583,31 @@ export const zHttpValidationError = z.object({
|
|||||||
detail: z.optional(z.array(zValidationError)),
|
detail: z.optional(z.array(zValidationError)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProductImageSchema
|
||||||
|
*/
|
||||||
|
export const zProductImageSchema = z.object({
|
||||||
|
id: z.int(),
|
||||||
|
productId: z.int(),
|
||||||
|
imageUrl: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProductServicesDuplicateRequest
|
||||||
|
*/
|
||||||
|
export const zProductServicesDuplicateRequest = z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
sourceDealProductId: z.int(),
|
||||||
|
targetDealProductIds: z.array(z.int()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProductServicesDuplicateResponse
|
||||||
|
*/
|
||||||
|
export const zProductServicesDuplicateResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
export const zSortDir = z.enum(["asc", "desc"]);
|
export const zSortDir = z.enum(["asc", "desc"]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -243,6 +632,28 @@ export const zUpdateBoardResponse = z.object({
|
|||||||
message: z.string(),
|
message: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealProductSchema
|
||||||
|
*/
|
||||||
|
export const zUpdateDealProductSchema = z.object({
|
||||||
|
quantity: z.int(),
|
||||||
|
comment: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealProductRequest
|
||||||
|
*/
|
||||||
|
export const zUpdateDealProductRequest = z.object({
|
||||||
|
entity: zUpdateDealProductSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealProductResponse
|
||||||
|
*/
|
||||||
|
export const zUpdateDealProductResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateDealSchema
|
* UpdateDealSchema
|
||||||
*/
|
*/
|
||||||
@ -267,11 +678,86 @@ export const zUpdateDealResponse = z.object({
|
|||||||
message: z.string(),
|
message: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealServiceSchema
|
||||||
|
*/
|
||||||
|
export const zUpdateDealServiceSchema = z.object({
|
||||||
|
quantity: z.int(),
|
||||||
|
price: z.number(),
|
||||||
|
isFixedPrice: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealServiceRequest
|
||||||
|
*/
|
||||||
|
export const zUpdateDealServiceRequest = z.object({
|
||||||
|
entity: zUpdateDealServiceSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealServiceResponse
|
||||||
|
*/
|
||||||
|
export const zUpdateDealServiceResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateProductSchema
|
||||||
|
*/
|
||||||
|
export const zUpdateProductSchema = z.object({
|
||||||
|
name: z.optional(z.union([z.string(), z.null()])),
|
||||||
|
article: z.optional(z.union([z.string(), z.null()])),
|
||||||
|
factoryArticle: z.optional(z.union([z.string(), z.null()])),
|
||||||
|
brand: z.optional(z.union([z.string(), z.null()])),
|
||||||
|
color: z.optional(z.union([z.string(), z.null()])),
|
||||||
|
composition: z.optional(z.union([z.string(), z.null()])),
|
||||||
|
size: z.optional(z.union([z.string(), z.null()])),
|
||||||
|
additionalInfo: z.optional(z.union([z.string(), z.null()])),
|
||||||
|
images: z.optional(z.union([z.array(zProductImageSchema), z.null()])),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateProductRequest
|
||||||
|
*/
|
||||||
|
export const zUpdateProductRequest = z.object({
|
||||||
|
entity: zUpdateProductSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateProductResponse
|
||||||
|
*/
|
||||||
|
export const zUpdateProductResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateProductServiceSchema
|
||||||
|
*/
|
||||||
|
export const zUpdateProductServiceSchema = z.object({
|
||||||
|
price: z.number(),
|
||||||
|
isFixedPrice: z.boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateProductServiceRequest
|
||||||
|
*/
|
||||||
|
export const zUpdateProductServiceRequest = z.object({
|
||||||
|
entity: zUpdateProductServiceSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateProductServiceResponse
|
||||||
|
*/
|
||||||
|
export const zUpdateProductServiceResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateProjectSchema
|
* UpdateProjectSchema
|
||||||
*/
|
*/
|
||||||
export const zUpdateProjectSchema = z.object({
|
export const zUpdateProjectSchema = z.object({
|
||||||
name: z.optional(z.union([z.string(), z.null()])),
|
name: z.optional(z.union([z.string(), z.null()])),
|
||||||
|
builtInModules: z.optional(z.array(zBuiltInModuleSchema)),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -288,6 +774,57 @@ export const zUpdateProjectResponse = z.object({
|
|||||||
message: z.string(),
|
message: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateServiceSchema
|
||||||
|
*/
|
||||||
|
export const zUpdateServiceSchema = z.object({
|
||||||
|
id: z.int(),
|
||||||
|
name: z.string(),
|
||||||
|
category: zServiceCategorySchema,
|
||||||
|
price: z.number(),
|
||||||
|
serviceType: z.int(),
|
||||||
|
priceRanges: z.array(zServicePriceRangeSchema),
|
||||||
|
cost: z.union([z.number(), z.null()]),
|
||||||
|
lexorank: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateServiceRequest
|
||||||
|
*/
|
||||||
|
export const zUpdateServiceRequest = z.object({
|
||||||
|
entity: zUpdateServiceSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateServiceResponse
|
||||||
|
*/
|
||||||
|
export const zUpdateServiceResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateServicesKitSchema
|
||||||
|
*/
|
||||||
|
export const zUpdateServicesKitSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
serviceType: z.int(),
|
||||||
|
servicesIds: z.array(z.int()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateServicesKitRequest
|
||||||
|
*/
|
||||||
|
export const zUpdateServicesKitRequest = z.object({
|
||||||
|
entity: zUpdateServicesKitSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateServicesKitResponse
|
||||||
|
*/
|
||||||
|
export const zUpdateServicesKitResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UpdateStatusSchema
|
* UpdateStatusSchema
|
||||||
*/
|
*/
|
||||||
@ -420,6 +957,17 @@ export const zUpdateDealData = z.object({
|
|||||||
*/
|
*/
|
||||||
export const zUpdateDealResponse2 = zUpdateDealResponse;
|
export const zUpdateDealResponse2 = zUpdateDealResponse;
|
||||||
|
|
||||||
|
export const zGetBuiltInModulesData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zGetBuiltInModulesResponse = zGetAllBuiltInModulesResponse;
|
||||||
|
|
||||||
export const zGetProjectsData = z.object({
|
export const zGetProjectsData = z.object({
|
||||||
body: z.optional(z.never()),
|
body: z.optional(z.never()),
|
||||||
path: z.optional(z.never()),
|
path: z.optional(z.never()),
|
||||||
@ -517,3 +1065,321 @@ export const zUpdateStatusData = z.object({
|
|||||||
* Successful Response
|
* Successful Response
|
||||||
*/
|
*/
|
||||||
export const zUpdateStatusResponse2 = zUpdateStatusResponse;
|
export const zUpdateStatusResponse2 = zUpdateStatusResponse;
|
||||||
|
|
||||||
|
export const zGetDealProductsData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zGetDealProductsResponse2 = zGetDealProductsResponse;
|
||||||
|
|
||||||
|
export const zCreateDealProductData = z.object({
|
||||||
|
body: zCreateDealProductRequest,
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zCreateDealProductResponse2 = zCreateDealProductResponse;
|
||||||
|
|
||||||
|
export const zDeleteDealProductData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
productId: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zDeleteDealProductResponse2 = zDeleteDealProductResponse;
|
||||||
|
|
||||||
|
export const zUpdateDealProductData = z.object({
|
||||||
|
body: zUpdateDealProductRequest,
|
||||||
|
path: z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
productId: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zUpdateDealProductResponse2 = zUpdateDealProductResponse;
|
||||||
|
|
||||||
|
export const zAddKitToDealProductData = z.object({
|
||||||
|
body: zDealProductAddKitRequest,
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zAddKitToDealProductResponse = zDealProductAddKitResponse;
|
||||||
|
|
||||||
|
export const zCreateDealProductServiceData = z.object({
|
||||||
|
body: zCreateProductServiceRequest,
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zCreateDealProductServiceResponse = zCreateProductServiceResponse;
|
||||||
|
|
||||||
|
export const zDeleteDealProductServiceData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
productId: z.int(),
|
||||||
|
serviceId: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zDeleteDealProductServiceResponse = zDeleteProductServiceResponse;
|
||||||
|
|
||||||
|
export const zUpdateDealProductServiceData = z.object({
|
||||||
|
body: zUpdateProductServiceRequest,
|
||||||
|
path: z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
productId: z.int(),
|
||||||
|
serviceId: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zUpdateDealProductServiceResponse = zUpdateProductServiceResponse;
|
||||||
|
|
||||||
|
export const zDuplicateProductServicesData = z.object({
|
||||||
|
body: zProductServicesDuplicateRequest,
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zDuplicateProductServicesResponse =
|
||||||
|
zProductServicesDuplicateResponse;
|
||||||
|
|
||||||
|
export const zGetDealServicesData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zGetDealServicesResponse2 = zGetDealServicesResponse;
|
||||||
|
|
||||||
|
export const zCreateDealServiceData = z.object({
|
||||||
|
body: zCreateDealServiceRequest,
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zCreateDealServiceResponse2 = zCreateDealServiceResponse;
|
||||||
|
|
||||||
|
export const zDeleteDealServiceData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
serviceId: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zDeleteDealServiceResponse2 = zDeleteDealServiceResponse;
|
||||||
|
|
||||||
|
export const zUpdateDealServiceData = z.object({
|
||||||
|
body: zUpdateDealServiceRequest,
|
||||||
|
path: z.object({
|
||||||
|
dealId: z.int(),
|
||||||
|
serviceId: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zUpdateDealServiceResponse2 = zUpdateDealServiceResponse;
|
||||||
|
|
||||||
|
export const zGetProductsData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(
|
||||||
|
z.object({
|
||||||
|
searchInput: z.optional(z.union([z.string(), z.null()])),
|
||||||
|
page: z.optional(z.union([z.int(), z.null()])),
|
||||||
|
itemsPerPage: z.optional(z.union([z.int(), z.null()])),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zGetProductsResponse2 = zGetProductsResponse;
|
||||||
|
|
||||||
|
export const zCreateProductData = z.object({
|
||||||
|
body: zCreateProductRequest,
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zCreateProductResponse2 = zCreateProductResponse;
|
||||||
|
|
||||||
|
export const zDeleteProductData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.object({
|
||||||
|
pk: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zDeleteProductResponse2 = zDeleteProductResponse;
|
||||||
|
|
||||||
|
export const zUpdateProductData = z.object({
|
||||||
|
body: zUpdateProductRequest,
|
||||||
|
path: z.object({
|
||||||
|
pk: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zUpdateProductResponse2 = zUpdateProductResponse;
|
||||||
|
|
||||||
|
export const zGetServicesData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zGetServicesResponse2 = zGetServicesResponse;
|
||||||
|
|
||||||
|
export const zCreateServiceData = z.object({
|
||||||
|
body: zCreateServiceRequest,
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zCreateServiceResponse2 = zCreateServiceResponse;
|
||||||
|
|
||||||
|
export const zDeleteServiceData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.object({
|
||||||
|
pk: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zDeleteServiceResponse2 = zDeleteServiceResponse;
|
||||||
|
|
||||||
|
export const zUpdateServiceData = z.object({
|
||||||
|
body: zUpdateServiceRequest,
|
||||||
|
path: z.object({
|
||||||
|
pk: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zUpdateServiceResponse2 = zUpdateServiceResponse;
|
||||||
|
|
||||||
|
export const zGetServicesKitsData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zGetServicesKitsResponse = zGetServicesKitResponse;
|
||||||
|
|
||||||
|
export const zCreateServicesKitData = z.object({
|
||||||
|
body: zCreateServicesKitRequest,
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zCreateServicesKitResponse2 = zCreateServicesKitResponse;
|
||||||
|
|
||||||
|
export const zDeleteServicesKitData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.object({
|
||||||
|
pk: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zDeleteServicesKitResponse2 = zDeleteServicesKitResponse;
|
||||||
|
|
||||||
|
export const zUpdateServicesKitData = z.object({
|
||||||
|
body: zUpdateServicesKitRequest,
|
||||||
|
path: z.object({
|
||||||
|
pk: z.int(),
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never()),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zUpdateServicesKitResponse2 = zUpdateServicesKitResponse;
|
||||||
|
|||||||
63
src/modals/base/BaseFormModal/BaseFormModal.tsx
Normal file
63
src/modals/base/BaseFormModal/BaseFormModal.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
import { Flex } from "@mantine/core";
|
||||||
|
import { UseFormReturnType } from "@mantine/form";
|
||||||
|
import BaseFormModalActions from "@/modals/base/BaseFormModal/BaseFormModalActions";
|
||||||
|
|
||||||
|
export type CreateProps<TCreate> = {
|
||||||
|
onCreate: (values: TCreate) => void;
|
||||||
|
isEditing: false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EditProps<TEntity, TUpdate> = {
|
||||||
|
onChange: (values: TUpdate) => void;
|
||||||
|
entity: TEntity;
|
||||||
|
isEditing: true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreateEditFormProps<
|
||||||
|
TCreate,
|
||||||
|
TUpdate = TCreate,
|
||||||
|
TEntity = TUpdate,
|
||||||
|
> = CreateProps<TCreate> | EditProps<TEntity, TUpdate>;
|
||||||
|
|
||||||
|
export type BaseFormProps<T> = {
|
||||||
|
form: UseFormReturnType<Partial<T>>;
|
||||||
|
onClose: () => void;
|
||||||
|
closeOnSubmit?: boolean;
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props<TCreate, TUpdate, TEntity> = BaseFormProps<TEntity> &
|
||||||
|
CreateEditFormProps<TCreate, TUpdate, TEntity>;
|
||||||
|
|
||||||
|
const BaseFormModal = <TCreate, TUpdate = TCreate, TEntity = TUpdate>(
|
||||||
|
props: Props<TCreate, TUpdate, TEntity>
|
||||||
|
) => {
|
||||||
|
const { closeOnSubmit = false } = props;
|
||||||
|
|
||||||
|
const onSubmit = (values: Partial<TEntity>) => {
|
||||||
|
if (props.isEditing) {
|
||||||
|
props.onChange({ ...props.entity, ...values } as TUpdate);
|
||||||
|
} else {
|
||||||
|
props.onCreate(values as TCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closeOnSubmit) {
|
||||||
|
props.form.reset();
|
||||||
|
props.onClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={props.form.onSubmit(values => onSubmit(values))}>
|
||||||
|
<Flex
|
||||||
|
gap={"xs"}
|
||||||
|
direction={"column"}>
|
||||||
|
{props.children}
|
||||||
|
<BaseFormModalActions {...props} />
|
||||||
|
</Flex>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BaseFormModal;
|
||||||
25
src/modals/base/BaseFormModal/BaseFormModalActions.tsx
Normal file
25
src/modals/base/BaseFormModal/BaseFormModalActions.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { Button, Flex } from "@mantine/core";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BaseFormModalActions: FC<Props> = ({ onClose }) => (
|
||||||
|
<Flex
|
||||||
|
justify={"flex-end"}
|
||||||
|
gap={"xs"}>
|
||||||
|
<Button
|
||||||
|
variant={"subtle"}
|
||||||
|
onClick={onClose}>
|
||||||
|
Отменить
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type={"submit"}
|
||||||
|
variant={"default"}>
|
||||||
|
Сохранить
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default BaseFormModalActions;
|
||||||
@ -2,10 +2,22 @@ import DealsBoardFiltersModal from "@/app/deals/modals/DealsBoardFiltersModal/De
|
|||||||
import DealsScheduleFiltersModal from "@/app/deals/modals/DealsScheduleFiltersModal/DealsScheduleFiltersModal";
|
import DealsScheduleFiltersModal from "@/app/deals/modals/DealsScheduleFiltersModal/DealsScheduleFiltersModal";
|
||||||
import DealsTableFiltersModal from "@/app/deals/modals/DealsTableFiltersModal/DealsTableFiltersModal";
|
import DealsTableFiltersModal from "@/app/deals/modals/DealsTableFiltersModal/DealsTableFiltersModal";
|
||||||
import EnterNameModal from "@/modals/EnterNameModal/EnterNameModal";
|
import EnterNameModal from "@/modals/EnterNameModal/EnterNameModal";
|
||||||
|
import DealProductEditorModal from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/modals/DealProductEditorModal/DealProductEditorModal";
|
||||||
|
import DealServiceEditorModal from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/modals/DealServiceEditorModal/DealServiceEditorModal";
|
||||||
|
import DuplicateServicesModal from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/modals/DuplicateServicesModal/DuplicateServicesModal";
|
||||||
|
import ProductEditorModal from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/modals/ProductEditorModal/ProductEditorModal";
|
||||||
|
import ProductServiceEditorModal from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/modals/ProductServiceEditorModal/ProductServiceEditorModal";
|
||||||
|
import ServicesKitSelectModal from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/modals/ServicesKitSelectModal/ServicesKitSelectModal";
|
||||||
|
|
||||||
export const modals = {
|
export const modals = {
|
||||||
enterNameModal: EnterNameModal,
|
enterNameModal: EnterNameModal,
|
||||||
dealsTableFiltersModal: DealsTableFiltersModal,
|
dealsTableFiltersModal: DealsTableFiltersModal,
|
||||||
dealsBoardFiltersModal: DealsBoardFiltersModal,
|
dealsBoardFiltersModal: DealsBoardFiltersModal,
|
||||||
dealsScheduleFiltersModal: DealsScheduleFiltersModal,
|
dealsScheduleFiltersModal: DealsScheduleFiltersModal,
|
||||||
|
productEditorModal: ProductEditorModal,
|
||||||
|
dealProductEditorModal: DealProductEditorModal,
|
||||||
|
dealServiceEditorModal: DealServiceEditorModal,
|
||||||
|
productServiceEditorModal: ProductServiceEditorModal,
|
||||||
|
duplicateServicesModal: DuplicateServicesModal,
|
||||||
|
servicesKitSelectModal: ServicesKitSelectModal,
|
||||||
};
|
};
|
||||||
|
|||||||
13
src/modules/connectModules.tsx
Normal file
13
src/modules/connectModules.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import FulfillmentBaseTab from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/FulfillmentBaseTab";
|
||||||
|
import { ModuleNames } from "./modules";
|
||||||
|
import ModulesType from "./types";
|
||||||
|
|
||||||
|
const connectModules = (modules: ModulesType) => {
|
||||||
|
modules[ModuleNames.FULFILLMENT_BASE].getTab = props => (
|
||||||
|
<FulfillmentBaseTab {...props} />
|
||||||
|
);
|
||||||
|
|
||||||
|
return modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connectModules;
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
//flex-direction: column;
|
||||||
|
gap: rem(10);
|
||||||
|
max-height: 95vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-disabled {
|
||||||
|
}
|
||||||
|
|
||||||
|
.products-list {
|
||||||
|
width: 52%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: rem(10);
|
||||||
|
flex: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: rem(10);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-container-wrapper {
|
||||||
|
border: dashed var(--item-border-size) var(--mantine-color-default-border);
|
||||||
|
border-radius: var(--item-border-radius);
|
||||||
|
padding: rem(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-container-buttons {
|
||||||
|
gap: rem(10);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { DealSchema } from "@/lib/client";
|
||||||
|
import FulfillmentBaseTabBody from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/FulfillmentBaseTabBody/FulfillmentBaseTabBody";
|
||||||
|
import { FulfillmentBaseContextProvider } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/contexts/FulfillmentBaseContext";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value: DealSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FulfillmentBaseTab: FC<Props> = ({ value }) => {
|
||||||
|
return (
|
||||||
|
<FulfillmentBaseContextProvider deal={value}>
|
||||||
|
<FulfillmentBaseTabBody />
|
||||||
|
</FulfillmentBaseContextProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FulfillmentBaseTab;
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { Flex, rem } from "@mantine/core";
|
||||||
|
import DealServiceRow from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/DealServicesTable/components/DealServiceRow";
|
||||||
|
import DealServicesTitle from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/DealServicesTable/components/DealServicesTitle";
|
||||||
|
import DealServicesTotalLabel from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/DealServicesTable/components/DealServicesTotalLabel";
|
||||||
|
import ServicesActions from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/DealServicesTable/components/ServicesActions";
|
||||||
|
import { useFulfillmentBaseContext } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/contexts/FulfillmentBaseContext";
|
||||||
|
|
||||||
|
const DealServicesTable: FC = () => {
|
||||||
|
const { dealServicesList, dealServicesCrud } = useFulfillmentBaseContext();
|
||||||
|
|
||||||
|
// const isLocked = isDealLocked(deal); // TODO bills
|
||||||
|
|
||||||
|
// const [currentService, setCurrentService] = useState<
|
||||||
|
// DealServiceSchema | undefined
|
||||||
|
// >();
|
||||||
|
// const [employeesModalVisible, setEmployeesModalVisible] = useState(false);
|
||||||
|
|
||||||
|
// const onEmployeeClick = (item: CardServiceSchema) => {
|
||||||
|
// if (!onChange) return;
|
||||||
|
// setCurrentService(item);
|
||||||
|
// setEmployeesModalVisible(true);
|
||||||
|
// };
|
||||||
|
// const onEmployeeModalClose = () => {
|
||||||
|
// setEmployeesModalVisible(false);
|
||||||
|
// setCurrentService(undefined);
|
||||||
|
// };
|
||||||
|
// const getCurrentEmployees = (): UserSchema[] => {
|
||||||
|
// if (!currentService) return [];
|
||||||
|
// const item = items.find(
|
||||||
|
// i => i.service.id === currentService.service.id
|
||||||
|
// );
|
||||||
|
// if (!item) return [];
|
||||||
|
// return item.employees;
|
||||||
|
// };
|
||||||
|
// const onEmployeesChange = (items: UserSchema[]) => {
|
||||||
|
// if (!currentService || !onChange) return;
|
||||||
|
// debouncedOnChange({
|
||||||
|
// ...currentService,
|
||||||
|
// employees: items,
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
direction={"column"}
|
||||||
|
gap={rem(10)}
|
||||||
|
h={"100%"}>
|
||||||
|
<Flex
|
||||||
|
h={"100%"}
|
||||||
|
direction={"column"}>
|
||||||
|
<DealServicesTitle />
|
||||||
|
<Flex
|
||||||
|
direction={"column"}
|
||||||
|
gap={rem(10)}>
|
||||||
|
{dealServicesList.dealServices.map(dealService => (
|
||||||
|
<DealServiceRow
|
||||||
|
key={dealService.service.id}
|
||||||
|
value={dealService}
|
||||||
|
onDelete={dealServicesCrud.onDelete}
|
||||||
|
onChange={dealServicesCrud.onUpdate}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<DealServicesTotalLabel />
|
||||||
|
<ServicesActions />
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default DealServicesTable;
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { IconTrash } from "@tabler/icons-react";
|
||||||
|
import { isNumber } from "lodash";
|
||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Divider,
|
||||||
|
Group,
|
||||||
|
NumberInput,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Tooltip,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { useDebouncedCallback } from "@mantine/hooks";
|
||||||
|
import { DealServiceSchema } from "@/lib/client";
|
||||||
|
import LockCheckbox from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/LockCheckbox/LockCheckbox";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value: DealServiceSchema;
|
||||||
|
onChange: (
|
||||||
|
dealId: number,
|
||||||
|
serviceId: number,
|
||||||
|
value: DealServiceSchema
|
||||||
|
) => void;
|
||||||
|
onDelete: (value: DealServiceSchema) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DealServiceRow: FC<Props> = ({ value, onChange, onDelete }) => {
|
||||||
|
const debouncedOnChange = useDebouncedCallback(
|
||||||
|
async (item: DealServiceSchema) => {
|
||||||
|
onChange(item.dealId, item.serviceId, item);
|
||||||
|
},
|
||||||
|
200
|
||||||
|
);
|
||||||
|
|
||||||
|
const onQuantityChange = (item: DealServiceSchema, quantity: number) => {
|
||||||
|
debouncedOnChange({ ...item, quantity });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPriceChange = (item: DealServiceSchema, price: number) => {
|
||||||
|
debouncedOnChange({
|
||||||
|
...item,
|
||||||
|
price,
|
||||||
|
isFixedPrice: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
w={"100%"}
|
||||||
|
gap={"xs"}>
|
||||||
|
<Divider />
|
||||||
|
<Text>{value.service.name}</Text>
|
||||||
|
<Group>
|
||||||
|
<Tooltip
|
||||||
|
onClick={() => onDelete(value)}
|
||||||
|
label="Удалить услугу">
|
||||||
|
<ActionIcon variant={"default"}>
|
||||||
|
<IconTrash />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
{/*<Tooltip label="Сотрудники">*/}
|
||||||
|
{/* <ActionIcon*/}
|
||||||
|
{/* onClick={() => onEmployeeClick(service)}*/}
|
||||||
|
{/* variant={"default"}>*/}
|
||||||
|
{/* <IconUsersGroup />*/}
|
||||||
|
{/* </ActionIcon>*/}
|
||||||
|
{/*</Tooltip>*/}
|
||||||
|
<NumberInput
|
||||||
|
flex={1}
|
||||||
|
suffix={" шт."}
|
||||||
|
onChange={event =>
|
||||||
|
isNumber(event) && onQuantityChange(value, event)
|
||||||
|
}
|
||||||
|
value={value.quantity}
|
||||||
|
min={1}
|
||||||
|
allowNegative={false}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
flex={1}
|
||||||
|
onChange={event =>
|
||||||
|
isNumber(event) && onPriceChange(value, event)
|
||||||
|
}
|
||||||
|
suffix={"₽"}
|
||||||
|
value={value.price}
|
||||||
|
disabled={value.isFixedPrice}
|
||||||
|
min={1}
|
||||||
|
allowNegative={false}
|
||||||
|
rightSectionProps={{
|
||||||
|
style: {
|
||||||
|
display: "flex",
|
||||||
|
cursor: "pointer",
|
||||||
|
pointerEvents: "auto",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
rightSection={
|
||||||
|
<LockCheckbox
|
||||||
|
label={"Зафиксировать цену"}
|
||||||
|
variant={"default"}
|
||||||
|
value={value.isFixedPrice}
|
||||||
|
onChange={isFixedPrice =>
|
||||||
|
debouncedOnChange({
|
||||||
|
...value,
|
||||||
|
isFixedPrice,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DealServiceRow;
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { rem, Title } from "@mantine/core";
|
||||||
|
|
||||||
|
const DealServicesTitle = () => (
|
||||||
|
<Title
|
||||||
|
order={3}
|
||||||
|
w={"100%"}
|
||||||
|
mb={rem(10)}>
|
||||||
|
Общие услуги
|
||||||
|
</Title>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default DealServicesTitle;
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
|
import { rem, Title } from "@mantine/core";
|
||||||
|
import { useFulfillmentBaseContext } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/contexts/FulfillmentBaseContext";
|
||||||
|
|
||||||
|
const DealServicesTotalLabel = () => {
|
||||||
|
const { dealServicesList } = useFulfillmentBaseContext();
|
||||||
|
const total = useMemo(
|
||||||
|
() =>
|
||||||
|
dealServicesList.dealServices.reduce(
|
||||||
|
(acc, item) => acc + item.price * item.quantity,
|
||||||
|
0
|
||||||
|
),
|
||||||
|
[dealServicesList.dealServices]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Title
|
||||||
|
style={{ textAlign: "end" }}
|
||||||
|
mt={rem(10)}
|
||||||
|
order={3}>
|
||||||
|
Итог: {total}₽
|
||||||
|
</Title>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DealServicesTotalLabel;
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
import { Button, Flex, rem } from "@mantine/core";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import { useFulfillmentBaseContext } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/contexts/FulfillmentBaseContext";
|
||||||
|
|
||||||
|
const ServicesActions = () => {
|
||||||
|
const { dealServicesList, dealServicesCrud, deal } =
|
||||||
|
useFulfillmentBaseContext();
|
||||||
|
|
||||||
|
const onCreateClick = () => {
|
||||||
|
const serviceIdsToExclude = dealServicesList.dealServices.map(
|
||||||
|
service => service.service.id
|
||||||
|
);
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "dealServiceEditorModal",
|
||||||
|
innerProps: {
|
||||||
|
onCreate: values =>
|
||||||
|
dealServicesCrud.onCreate({ ...values, dealId: deal.id }),
|
||||||
|
serviceIdsToExclude,
|
||||||
|
isEditing: false,
|
||||||
|
},
|
||||||
|
withCloseButton: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// const onAddKitClick = () => {
|
||||||
|
// if (!onKitAdd) return;
|
||||||
|
// modals.openContextModal({
|
||||||
|
// modal: "servicesKitSelectModal",
|
||||||
|
// innerProps: {
|
||||||
|
// onSelect: onKitAdd,
|
||||||
|
// serviceType: ServiceType.DEAL_SERVICE,
|
||||||
|
// },
|
||||||
|
// withCloseButton: false,
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
direction={"column"}
|
||||||
|
gap={rem(10)}
|
||||||
|
mt={"auto"}>
|
||||||
|
<Button
|
||||||
|
onClick={onCreateClick}
|
||||||
|
fullWidth
|
||||||
|
variant={"default"}>
|
||||||
|
Добавить услугу
|
||||||
|
</Button>
|
||||||
|
{/*<Button*/}
|
||||||
|
{/* onClick={onAddKitClick}*/}
|
||||||
|
{/* fullWidth*/}
|
||||||
|
{/* variant={"default"}>*/}
|
||||||
|
{/* Добавить набор услуг*/}
|
||||||
|
{/*</Button>*/}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServicesActions;
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
import { Flex, ScrollArea, Stack } from "@mantine/core";
|
||||||
|
import DealServicesTable from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/DealServicesTable/DealServicesTable";
|
||||||
|
import ProductsActions from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/ProductsActions/ProductsActions";
|
||||||
|
import ProductView from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/ProductView/ProductView";
|
||||||
|
import { useFulfillmentBaseContext } from "../../contexts/FulfillmentBaseContext";
|
||||||
|
|
||||||
|
const FulfillmentBaseTabBody = () => {
|
||||||
|
const { dealProductsList } = useFulfillmentBaseContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
p={"md"}
|
||||||
|
gap={"xs"}>
|
||||||
|
<ScrollArea
|
||||||
|
offsetScrollbars={"y"}
|
||||||
|
mah={"91vh"}>
|
||||||
|
<Stack>
|
||||||
|
{dealProductsList.dealProducts.map((dealProduct, index) => (
|
||||||
|
<ProductView
|
||||||
|
dealProduct={dealProduct}
|
||||||
|
key={index}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</ScrollArea>
|
||||||
|
<Stack>
|
||||||
|
<DealServicesTable />
|
||||||
|
<ProductsActions />
|
||||||
|
</Stack>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FulfillmentBaseTabBody;
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
import ShippingWarehouseAutocomplete
|
||||||
|
from "../../../../../../components/Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
|
||||||
|
import { CardService, ShippingWarehouseSchema } from "../../../../../../client";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import { useCardPageContext } from "../../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
||||||
|
import { Button, Checkbox, Stack } from "@mantine/core";
|
||||||
|
import { notifications } from "../../../../../../shared/lib/notifications.ts";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { isEqual } from "lodash";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "../../../../../../redux/store.ts";
|
||||||
|
|
||||||
|
|
||||||
|
type GeneralDataFormType = {
|
||||||
|
shippingWarehouse?: ShippingWarehouseSchema | null | string;
|
||||||
|
isServicesProfitAccounted: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GeneralDataForm = () => {
|
||||||
|
const { selectedCard: card, refetchCard } = useCardPageContext();
|
||||||
|
const { isDealsViewer } = useSelector((state: RootState) => state.auth);
|
||||||
|
if (!card) return <></>;
|
||||||
|
|
||||||
|
const [initialValues, setInitialValues] = useState<GeneralDataFormType>(card);
|
||||||
|
|
||||||
|
const form = useForm<GeneralDataFormType>({
|
||||||
|
initialValues,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const data = card ?? {};
|
||||||
|
setInitialValues(data);
|
||||||
|
form.setValues(data);
|
||||||
|
}, [card]);
|
||||||
|
|
||||||
|
const isShippingWarehouse = (
|
||||||
|
value: ShippingWarehouseSchema | string | null | undefined,
|
||||||
|
): value is ShippingWarehouseSchema => {
|
||||||
|
return !!value && !["string"].includes(typeof value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = (values: GeneralDataFormType) => {
|
||||||
|
if (!card) return;
|
||||||
|
|
||||||
|
const shippingWarehouse = isShippingWarehouse(values.shippingWarehouse)
|
||||||
|
? values.shippingWarehouse.name
|
||||||
|
: values.shippingWarehouse;
|
||||||
|
|
||||||
|
CardService.updateProductsAndServicesGeneralInfo({
|
||||||
|
requestBody: {
|
||||||
|
cardId: card.id,
|
||||||
|
data: {
|
||||||
|
...values,
|
||||||
|
shippingWarehouse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ ok, message }) => {
|
||||||
|
if (!ok) {
|
||||||
|
notifications.error({ message });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refetchCard();
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={form.onSubmit(values => onSubmit(values))}>
|
||||||
|
<Stack>
|
||||||
|
<ShippingWarehouseAutocomplete
|
||||||
|
placeholder={isDealsViewer ? "" : "Введите склад отгрузки"}
|
||||||
|
label={"Склад отгрузки"}
|
||||||
|
value={
|
||||||
|
isShippingWarehouse(
|
||||||
|
form.values.shippingWarehouse,
|
||||||
|
)
|
||||||
|
? form.values.shippingWarehouse
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={event => {
|
||||||
|
if (isShippingWarehouse(event)) {
|
||||||
|
form.getInputProps(
|
||||||
|
"shippingWarehouse",
|
||||||
|
).onChange(event.name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
form.getInputProps(
|
||||||
|
"shippingWarehouse",
|
||||||
|
).onChange(event);
|
||||||
|
}}
|
||||||
|
readOnly={isDealsViewer}
|
||||||
|
/>
|
||||||
|
{!isDealsViewer && (
|
||||||
|
<>
|
||||||
|
<Checkbox
|
||||||
|
label={"Учет выручки в статистике"}
|
||||||
|
{...form.getInputProps("isServicesProfitAccounted", { type: "checkbox" })}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type={"submit"}
|
||||||
|
variant={"default"}
|
||||||
|
disabled={isEqual(initialValues, form.values)}
|
||||||
|
>
|
||||||
|
Сохранить
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GeneralDataForm;
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { IconLock, IconLockOpen } from "@tabler/icons-react";
|
||||||
|
import { ActionIcon, CheckboxProps, Tooltip } from "@mantine/core";
|
||||||
|
|
||||||
|
type RestProps = {
|
||||||
|
value: boolean;
|
||||||
|
onChange: (value: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = Omit<CheckboxProps, "value" | "onChange"> & RestProps;
|
||||||
|
|
||||||
|
const LockCheckbox: FC<Props> = props => {
|
||||||
|
const getIcon = () => (props.value ? <IconLock /> : <IconLockOpen />);
|
||||||
|
|
||||||
|
const handleChange = () => props.onChange(!props.value);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip label={props.label}>
|
||||||
|
<ActionIcon
|
||||||
|
onClick={handleChange}
|
||||||
|
variant={props.variant}>
|
||||||
|
{getIcon()}
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LockCheckbox;
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
import { CardSchema } from "../../../../../../client";
|
||||||
|
import ButtonCopy from "../../../../../../components/ButtonCopy/ButtonCopy.tsx";
|
||||||
|
import { ButtonCopyControlled } from "../../../../../../components/ButtonCopyControlled/ButtonCopyControlled.tsx";
|
||||||
|
import { getCurrentDateTimeForFilename } from "../../../../../../shared/lib/date.ts";
|
||||||
|
import FileSaver from "file-saver";
|
||||||
|
import { Button, Popover, Stack } from "@mantine/core";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
card: CardSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PaymentLinkButton = ({ card }: Props) => {
|
||||||
|
if ((!card.billRequests || card.billRequests.length === 0) && (!card?.group?.billRequests || card?.group?.billRequests.length === 0)) {
|
||||||
|
return (
|
||||||
|
<ButtonCopyControlled
|
||||||
|
onCopyClick={() => {
|
||||||
|
const date =
|
||||||
|
getCurrentDateTimeForFilename();
|
||||||
|
FileSaver.saveAs(
|
||||||
|
`${import.meta.env.VITE_API_URL}/card/billing-document/${card.id}`,
|
||||||
|
`bill_${card.id}_${date}.pdf`,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
copied={false}
|
||||||
|
onCopiedLabel={"Ссылка скопирована в буфер обмена"}
|
||||||
|
>
|
||||||
|
Ссылка на оплату (PDF)
|
||||||
|
</ButtonCopyControlled>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const requests = (card?.group ? card?.group?.billRequests : card.billRequests) ?? [];
|
||||||
|
const urls = requests.map(request => request.pdfUrl).filter(url => url !== null);
|
||||||
|
|
||||||
|
if (urls.length === 1) {
|
||||||
|
return (
|
||||||
|
<ButtonCopy
|
||||||
|
onCopiedLabel={"Ссылка скопирована в буфер обмена"}
|
||||||
|
value={urls[0]}
|
||||||
|
>
|
||||||
|
Ссылка на оплату
|
||||||
|
</ButtonCopy>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover width={380} position="bottom" withArrow shadow="md">
|
||||||
|
<Popover.Target>
|
||||||
|
<Button variant={"default"}>Ссылки на оплату</Button>
|
||||||
|
</Popover.Target>
|
||||||
|
<Popover.Dropdown>
|
||||||
|
<Stack gap={"md"}>
|
||||||
|
{urls.map((url, i) => (
|
||||||
|
<ButtonCopy
|
||||||
|
key={i}
|
||||||
|
onCopiedLabel={"Ссылка скопирована в буфер обмена"}
|
||||||
|
value={url}
|
||||||
|
>
|
||||||
|
{`Ссылка на оплату (часть ${String(i + 1)})`}
|
||||||
|
</ButtonCopy>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Popover.Dropdown>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PaymentLinkButton;
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
import { ActionIcon, Group, Tooltip } from "@mantine/core";
|
||||||
|
import styles from "../../../../../../pages/CardsPage/ui/CardsPage.module.css";
|
||||||
|
import { CardSchema, CardService } from "../../../../../../client";
|
||||||
|
import { base64ToBlob } from "../../../../../../shared/lib/utils.ts";
|
||||||
|
import { notifications } from "../../../../../../shared/lib/notifications.ts";
|
||||||
|
import { IconBarcode, IconPrinter } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
card: CardSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PrintDealBarcodesButton = ({ card }: Props) => {
|
||||||
|
return (
|
||||||
|
<Group wrap={"nowrap"}>
|
||||||
|
<Tooltip
|
||||||
|
className={styles["print-deals-button"]}
|
||||||
|
label={"Распечатать штрихкоды сделки"}
|
||||||
|
>
|
||||||
|
<ActionIcon
|
||||||
|
onClick={async () => {
|
||||||
|
const response =
|
||||||
|
await CardService.getCardProductsBarcodesPdf({
|
||||||
|
requestBody: {
|
||||||
|
cardId: card.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const pdfBlob = base64ToBlob(
|
||||||
|
response.base64String,
|
||||||
|
response.mimeType,
|
||||||
|
);
|
||||||
|
const pdfUrl = URL.createObjectURL(pdfBlob);
|
||||||
|
const pdfWindow = window.open(pdfUrl);
|
||||||
|
if (!pdfWindow) {
|
||||||
|
notifications.error({ message: "Ошибка" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pdfWindow.onload = () => {
|
||||||
|
pdfWindow.print();
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
variant={"default"}>
|
||||||
|
<IconBarcode />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label={"Распечатать сделку"}>
|
||||||
|
<ActionIcon
|
||||||
|
onClick={() => {
|
||||||
|
const pdfWindow = window.open(
|
||||||
|
`${import.meta.env.VITE_API_URL}/card/tech-spec/${card.id}`,
|
||||||
|
);
|
||||||
|
if (!pdfWindow) return;
|
||||||
|
pdfWindow.print();
|
||||||
|
}}
|
||||||
|
variant={"default"}>
|
||||||
|
<IconPrinter />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PrintDealBarcodesButton;
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { DropzoneProps, FileWithPath } from "@mantine/dropzone";
|
||||||
|
import ImageDropzone from "@/components/ui/ImageDropzone/ImageDropzone";
|
||||||
|
import { notifications } from "@/lib/notifications";
|
||||||
|
import BaseFormInputProps from "@/utils/baseFormInputProps";
|
||||||
|
import useImageDropzone from "./useImageDropzone";
|
||||||
|
|
||||||
|
interface RestProps {
|
||||||
|
imageUrlInputProps?: BaseFormInputProps<string>;
|
||||||
|
productId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = Omit<DropzoneProps, "onDrop"> & RestProps;
|
||||||
|
|
||||||
|
const ProductImageDropzone: FC<Props> = ({
|
||||||
|
imageUrlInputProps,
|
||||||
|
productId,
|
||||||
|
}: Props) => {
|
||||||
|
const imageDropzoneProps = useImageDropzone({
|
||||||
|
imageUrlInputProps,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onDrop = (files: FileWithPath[]) => {
|
||||||
|
if (!productId || !imageUrlInputProps) return;
|
||||||
|
if (files.length > 1) {
|
||||||
|
notifications.error({ message: "Прикрепите одно изображение" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { setIsLoading, setShowDropzone } = imageDropzoneProps;
|
||||||
|
const file = files[0];
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
// TODO SEND REQUEST
|
||||||
|
|
||||||
|
// ProductService.uploadProductImage({
|
||||||
|
// productId,
|
||||||
|
// formData: {
|
||||||
|
// upload_file: file,
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// .then(({ ok, message, imageUrl }) => {
|
||||||
|
// notifications.guess(ok, { message });
|
||||||
|
// setIsLoading(false);
|
||||||
|
//
|
||||||
|
// if (!ok || !imageUrl) {
|
||||||
|
// setShowDropzone(true);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// imageUrlInputProps?.onChange(imageUrl);
|
||||||
|
// setShowDropzone(false);
|
||||||
|
// })
|
||||||
|
// .catch(error => {
|
||||||
|
// notifications.error({ message: error.toString() });
|
||||||
|
// setShowDropzone(true);
|
||||||
|
// setIsLoading(false);
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ImageDropzone
|
||||||
|
onDrop={onDrop}
|
||||||
|
imageDropzone={imageDropzoneProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProductImageDropzone;
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import BaseFormInputProps from "@/utils/baseFormInputProps";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
imageUrlInputProps?: BaseFormInputProps<string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useImageDropzone = ({ imageUrlInputProps }: Props) => {
|
||||||
|
const [showDropzone, setShowDropzone] = useState(
|
||||||
|
!(
|
||||||
|
typeof imageUrlInputProps?.value === "string" &&
|
||||||
|
imageUrlInputProps.value.trim() !== ""
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
return {
|
||||||
|
showDropzone,
|
||||||
|
setShowDropzone,
|
||||||
|
isLoading,
|
||||||
|
setIsLoading,
|
||||||
|
imageUrlInputProps,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useImageDropzone;
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { FC, useState } from "react";
|
||||||
|
import { omit } from "lodash";
|
||||||
|
import { Loader, OptionsFilter } from "@mantine/core";
|
||||||
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
|
import ObjectSelect, {
|
||||||
|
ObjectSelectProps,
|
||||||
|
} from "@/components/selects/ObjectSelect/ObjectSelect";
|
||||||
|
import { ProductSchema } from "@/lib/client";
|
||||||
|
import useProductsList from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/hooks/lists/useProductsList";
|
||||||
|
import renderProductOption from "./utils/renderProductOption";
|
||||||
|
|
||||||
|
type RestProps = {
|
||||||
|
clientId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_PRODUCTS = 200;
|
||||||
|
|
||||||
|
type Props = Omit<ObjectSelectProps<ProductSchema>, "data"> & RestProps;
|
||||||
|
|
||||||
|
const ProductSelect: FC<Props> = (props: Props) => {
|
||||||
|
const [searchValue, setSearchValue] = useState("");
|
||||||
|
const [debounced] = useDebouncedValue(searchValue, 500);
|
||||||
|
const { products, isLoading } = useProductsList({
|
||||||
|
// clientId: props.clientId,
|
||||||
|
searchInput: debounced,
|
||||||
|
page: 0,
|
||||||
|
itemsPerPage: MAX_PRODUCTS,
|
||||||
|
});
|
||||||
|
const restProps = omit(props, ["clientId"]);
|
||||||
|
|
||||||
|
const optionsFilter: OptionsFilter = ({ options }) => options;
|
||||||
|
const setSearchValueImpl = (value: string) => {
|
||||||
|
const names = products.map(product => product.name);
|
||||||
|
if (names.includes(value)) return;
|
||||||
|
setSearchValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ObjectSelect
|
||||||
|
rightSection={
|
||||||
|
isLoading || searchValue !== debounced ? (
|
||||||
|
<Loader size={"sm"} />
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
onSearchChange={setSearchValueImpl}
|
||||||
|
renderOption={renderProductOption(products)}
|
||||||
|
searchable
|
||||||
|
{...restProps}
|
||||||
|
data={products}
|
||||||
|
filter={optionsFilter}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProductSelect;
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
import {
|
||||||
|
ComboboxItem,
|
||||||
|
ComboboxLikeRenderOptionInput,
|
||||||
|
SelectProps,
|
||||||
|
Tooltip,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { ProductSchema } from "@/lib/client";
|
||||||
|
import ProductFieldsList from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/ProductView/components/ProductFieldsList";
|
||||||
|
|
||||||
|
const renderProductOption = (
|
||||||
|
products: ProductSchema[]
|
||||||
|
): SelectProps["renderOption"] => {
|
||||||
|
return (item: ComboboxLikeRenderOptionInput<ComboboxItem>) => {
|
||||||
|
const product = products.find(
|
||||||
|
product => product.id === Number(item.option.value)
|
||||||
|
);
|
||||||
|
if (!product) return item.option.label;
|
||||||
|
// const imageUrl =
|
||||||
|
// product.images && product.images[0]
|
||||||
|
// ? product.images[0].imageUrl
|
||||||
|
// : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
style={{ whiteSpace: "pre-line" }}
|
||||||
|
multiline
|
||||||
|
label={
|
||||||
|
<>
|
||||||
|
<ProductFieldsList product={product} />
|
||||||
|
{/*{imageUrl && (*/}
|
||||||
|
{/* <Image*/}
|
||||||
|
{/* src={imageUrl}*/}
|
||||||
|
{/* alt={product.name}*/}
|
||||||
|
{/* maw={rem(250)}*/}
|
||||||
|
{/* />*/}
|
||||||
|
{/*)}*/}
|
||||||
|
</>
|
||||||
|
}>
|
||||||
|
<div>
|
||||||
|
{product.name}
|
||||||
|
<br />
|
||||||
|
{/*{product.barcodes && (*/}
|
||||||
|
{/* <Text size={"xs"}>{product.barcodes[0]}</Text>*/}
|
||||||
|
{/*)}*/}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default renderProductOption;
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
border: dashed var(--item-border-size) var(--mantine-color-default-border);
|
||||||
|
border-radius: var(--item-border-radius);
|
||||||
|
gap: rem(20);
|
||||||
|
padding: rem(10);
|
||||||
|
margin-bottom: rem(10);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container {
|
||||||
|
display: flex;
|
||||||
|
max-height: rem(250);
|
||||||
|
max-width: rem(250);
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: rem(10);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-container {
|
||||||
|
max-width: rem(250);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: rem(10);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attributes-container {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
@ -0,0 +1,156 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { isNumber } from "lodash";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Card,
|
||||||
|
Image,
|
||||||
|
NumberInput,
|
||||||
|
rem,
|
||||||
|
Stack,
|
||||||
|
Textarea,
|
||||||
|
Title,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { useDebouncedCallback } from "@mantine/hooks";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import {
|
||||||
|
addKitToDealProduct,
|
||||||
|
DealProductSchema,
|
||||||
|
duplicateProductServices,
|
||||||
|
ServicesKitSchema,
|
||||||
|
} from "@/lib/client";
|
||||||
|
import ProductFieldsList from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/ProductView/components/ProductFieldsList";
|
||||||
|
import ProductViewActions from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/ProductView/components/ProductViewActions";
|
||||||
|
import { useFulfillmentBaseContext } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/contexts/FulfillmentBaseContext";
|
||||||
|
import { ServiceType } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/types/service";
|
||||||
|
import ProductServicesTable from "./components/ProductServicesTable";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
dealProduct: DealProductSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProductView: FC<Props> = ({ dealProduct }) => {
|
||||||
|
const { dealProductsCrud, deal, dealProductsList } =
|
||||||
|
useFulfillmentBaseContext();
|
||||||
|
|
||||||
|
const debouncedOnChange = useDebouncedCallback(
|
||||||
|
(newValues: Partial<DealProductSchema>) => {
|
||||||
|
dealProductsCrud.onUpdate(
|
||||||
|
dealProduct.dealId,
|
||||||
|
dealProduct.productId,
|
||||||
|
{
|
||||||
|
...dealProduct,
|
||||||
|
...newValues,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
200
|
||||||
|
);
|
||||||
|
|
||||||
|
const duplicateServices = (
|
||||||
|
sourceDealProduct: DealProductSchema,
|
||||||
|
targetDealProducts: DealProductSchema[]
|
||||||
|
) => {
|
||||||
|
duplicateProductServices({
|
||||||
|
body: {
|
||||||
|
dealId: deal.id,
|
||||||
|
sourceDealProductId: sourceDealProduct.productId,
|
||||||
|
targetDealProductIds: targetDealProducts.map(p => p.productId),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(() => dealProductsList.refetch())
|
||||||
|
.catch(err => console.error(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDuplicateServices = (sourceDealProduct: DealProductSchema) => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "duplicateServicesModal",
|
||||||
|
title: "Дублирование услуг",
|
||||||
|
size: "lg",
|
||||||
|
innerProps: {
|
||||||
|
dealProducts: dealProductsList.dealProducts,
|
||||||
|
sourceDealProduct,
|
||||||
|
duplicateServices,
|
||||||
|
},
|
||||||
|
withCloseButton: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onServicesKitAdd = (servicesKit: ServicesKitSchema) => {
|
||||||
|
addKitToDealProduct({
|
||||||
|
body: {
|
||||||
|
dealId: dealProduct.dealId,
|
||||||
|
productId: dealProduct.productId,
|
||||||
|
kitId: servicesKit.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(() => dealProductsList.refetch())
|
||||||
|
.catch(err => console.error(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAddKitClick = () => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "servicesKitSelectModal",
|
||||||
|
innerProps: {
|
||||||
|
onSelect: onServicesKitAdd,
|
||||||
|
serviceType: ServiceType.PRODUCT_SERVICE,
|
||||||
|
},
|
||||||
|
withCloseButton: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
p={"sm"}
|
||||||
|
style={{ display: "flex", flexDirection: "row" }}>
|
||||||
|
<Stack gap={"sm"}>
|
||||||
|
<Image
|
||||||
|
flex={1}
|
||||||
|
radius={rem(10)}
|
||||||
|
fit={"cover"}
|
||||||
|
// src={dealProduct.product.imageUrl}
|
||||||
|
/>
|
||||||
|
<Title order={3}>{dealProduct.product.name}</Title>
|
||||||
|
<ProductFieldsList product={dealProduct.product} />
|
||||||
|
{/*<Text>*/}
|
||||||
|
{/* Штрихкоды:*/}
|
||||||
|
{/*{value.product.barcodes.join(", ")}*/}
|
||||||
|
{/*</Text>*/}
|
||||||
|
<Box />
|
||||||
|
<NumberInput
|
||||||
|
mt={rem(10)}
|
||||||
|
suffix={" шт."}
|
||||||
|
value={dealProduct.quantity}
|
||||||
|
onChange={quantity =>
|
||||||
|
isNumber(quantity) && debouncedOnChange({ quantity })
|
||||||
|
}
|
||||||
|
placeholder={"Введите количество товара"}
|
||||||
|
/>
|
||||||
|
<Textarea
|
||||||
|
mih={rem(140)}
|
||||||
|
styles={{
|
||||||
|
wrapper: { height: "90%" },
|
||||||
|
input: { height: "90%" },
|
||||||
|
}}
|
||||||
|
my={rem(10)}
|
||||||
|
defaultValue={dealProduct.comment}
|
||||||
|
onChange={event =>
|
||||||
|
debouncedOnChange({
|
||||||
|
comment: event.currentTarget.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder={"Комментарий"}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<Stack>
|
||||||
|
<ProductServicesTable
|
||||||
|
dealProduct={dealProduct}
|
||||||
|
onDuplicateServices={() => onDuplicateServices(dealProduct)}
|
||||||
|
onKitAdd={onAddKitClick}
|
||||||
|
/>
|
||||||
|
<ProductViewActions dealProduct={dealProduct} />
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProductView;
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { isNil } from "lodash";
|
||||||
|
import { Text } from "@mantine/core";
|
||||||
|
import { ProductSchema } from "@/lib/client";
|
||||||
|
|
||||||
|
type ProductFieldNames = {
|
||||||
|
[K in keyof ProductSchema]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProductFieldNames: Partial<ProductFieldNames> = {
|
||||||
|
color: "Цвет",
|
||||||
|
article: "Артикул",
|
||||||
|
size: "Размер",
|
||||||
|
brand: "Бренд",
|
||||||
|
composition: "Состав",
|
||||||
|
additionalInfo: "Доп. информация",
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
product: ProductSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProductFieldsList: FC<Props> = ({ product }) => {
|
||||||
|
const fieldList = Object.entries(product).map(([key, value]) => {
|
||||||
|
const fieldName = ProductFieldNames[key as keyof ProductSchema];
|
||||||
|
if (!fieldName || isNil(value) || value === "") return null;
|
||||||
|
return (
|
||||||
|
<Text key={fieldName}>
|
||||||
|
{fieldName}: {value.toString()}{" "}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return <>{fieldList}</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProductFieldsList;
|
||||||
@ -0,0 +1,146 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { IconMoodSad } from "@tabler/icons-react";
|
||||||
|
import { Button, Flex, Group, Stack, Text } from "@mantine/core";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import BaseTable from "@/components/ui/BaseTable/BaseTable";
|
||||||
|
import { DealProductSchema, ProductServiceSchema } from "@/lib/client";
|
||||||
|
import useProductServicesTableColumns from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/ProductView/hooks/useProductServicesTableColumns";
|
||||||
|
import { useFulfillmentBaseContext } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/contexts/FulfillmentBaseContext";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
dealProduct: DealProductSchema;
|
||||||
|
onDuplicateServices?: () => void;
|
||||||
|
onKitAdd?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProductServicesTable: FC<Props> = ({
|
||||||
|
dealProduct,
|
||||||
|
onDuplicateServices,
|
||||||
|
onKitAdd,
|
||||||
|
}) => {
|
||||||
|
const { productServiceCrud } = useFulfillmentBaseContext();
|
||||||
|
|
||||||
|
const onChange = (item: ProductServiceSchema) => {
|
||||||
|
const excludeServiceIds = dealProduct.productServices.map(
|
||||||
|
productService => productService.service.id
|
||||||
|
);
|
||||||
|
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "productServiceEditorModal",
|
||||||
|
innerProps: {
|
||||||
|
entity: item,
|
||||||
|
onChange: values =>
|
||||||
|
productServiceCrud.onUpdate(
|
||||||
|
item.dealId,
|
||||||
|
item.productId,
|
||||||
|
item.serviceId,
|
||||||
|
values
|
||||||
|
),
|
||||||
|
excludeServiceIds,
|
||||||
|
quantity: dealProduct.quantity,
|
||||||
|
isEditing: true,
|
||||||
|
},
|
||||||
|
withCloseButton: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = useProductServicesTableColumns({
|
||||||
|
data: dealProduct.productServices,
|
||||||
|
quantity: dealProduct.quantity,
|
||||||
|
onDelete: productServiceCrud.onDelete,
|
||||||
|
onChange,
|
||||||
|
});
|
||||||
|
|
||||||
|
// const [currentService, setCurrentService] = useState<
|
||||||
|
// ProductServiceSchema | undefined
|
||||||
|
// >();
|
||||||
|
// const [employeesModalVisible, setEmployeesModalVisible] = useState(false);
|
||||||
|
|
||||||
|
const onCreateClick = () => {
|
||||||
|
const excludeServiceIds = dealProduct.productServices.map(
|
||||||
|
productService => productService.service.id
|
||||||
|
);
|
||||||
|
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "productServiceEditorModal",
|
||||||
|
innerProps: {
|
||||||
|
onCreate: values =>
|
||||||
|
productServiceCrud.onCreate({ ...dealProduct, ...values }),
|
||||||
|
excludeServiceIds,
|
||||||
|
quantity: dealProduct.quantity,
|
||||||
|
isEditing: false,
|
||||||
|
},
|
||||||
|
withCloseButton: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// const onEmployeeClick = (item: CardProductServiceSchema) => {
|
||||||
|
// if (!onChange) return;
|
||||||
|
// setCurrentService(item);
|
||||||
|
// setEmployeesModalVisible(true);
|
||||||
|
// };
|
||||||
|
// const onEmployeeModalClose = () => {
|
||||||
|
// setEmployeesModalVisible(false);
|
||||||
|
// setCurrentService(undefined);
|
||||||
|
// };
|
||||||
|
// const getCurrentEmployees = (): UserSchema[] => {
|
||||||
|
// if (!currentService) return [];
|
||||||
|
// const item = items.find(
|
||||||
|
// i => i.service.id === currentService.service.id
|
||||||
|
// );
|
||||||
|
// if (!item) return [];
|
||||||
|
// return item.employees;
|
||||||
|
// };
|
||||||
|
// const onEmployeesChange = (items: UserSchema[]) => {
|
||||||
|
// if (!currentService || !onChange) return;
|
||||||
|
// onChange({
|
||||||
|
// ...currentService,
|
||||||
|
// employees: items,
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
const isEmptyTable = dealProduct.productServices.length === 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap={0}>
|
||||||
|
<BaseTable
|
||||||
|
records={dealProduct.productServices}
|
||||||
|
columns={columns}
|
||||||
|
groups={undefined}
|
||||||
|
idAccessor={"serviceId"}
|
||||||
|
style={{
|
||||||
|
height: isEmptyTable ? "8rem" : "auto",
|
||||||
|
}}
|
||||||
|
emptyState={
|
||||||
|
<Group
|
||||||
|
gap={"xs"}
|
||||||
|
mt={isEmptyTable ? "xl" : 0}>
|
||||||
|
<Text>Нет услуг</Text>
|
||||||
|
<IconMoodSad />
|
||||||
|
</Group>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Flex
|
||||||
|
justify={"flex-end"}
|
||||||
|
gap={"xs"}
|
||||||
|
pt={isEmptyTable ? 0 : "xs"}>
|
||||||
|
<Button
|
||||||
|
onClick={() => onKitAdd && onKitAdd()}
|
||||||
|
variant={"default"}>
|
||||||
|
Добавить набор услуг
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => onDuplicateServices && onDuplicateServices()}
|
||||||
|
variant={"default"}>
|
||||||
|
Продублировать услуги
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={onCreateClick}
|
||||||
|
variant={"default"}>
|
||||||
|
Добавить услугу
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ProductServicesTable;
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||||
|
import { ActionIcon, Flex, rem, Tooltip } from "@mantine/core";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import { DealProductSchema } from "@/lib/client";
|
||||||
|
import { useFulfillmentBaseContext } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/contexts/FulfillmentBaseContext";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
dealProduct: DealProductSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProductViewActions: FC<Props> = ({ dealProduct }) => {
|
||||||
|
const { dealProductsCrud, dealProductsList, productsCrud } =
|
||||||
|
useFulfillmentBaseContext();
|
||||||
|
|
||||||
|
const onProductEditClick = () => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "productEditorModal",
|
||||||
|
title: "Редактирование товара",
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
onChange: values =>
|
||||||
|
productsCrud.onUpdate(
|
||||||
|
dealProduct.productId,
|
||||||
|
values,
|
||||||
|
dealProductsList.refetch
|
||||||
|
),
|
||||||
|
entity: dealProduct.product,
|
||||||
|
isEditing: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
mt={"auto"}
|
||||||
|
ml={"auto"}
|
||||||
|
gap={rem(10)}>
|
||||||
|
{/*<Tooltip*/}
|
||||||
|
{/* onClick={onPrintBarcodeClick}*/}
|
||||||
|
{/* label="Печать штрихкода">*/}
|
||||||
|
{/* <ActionIcon variant={"default"}>*/}
|
||||||
|
{/* <IconBarcode />*/}
|
||||||
|
{/* </ActionIcon>*/}
|
||||||
|
{/*</Tooltip>*/}
|
||||||
|
<Tooltip
|
||||||
|
onClick={onProductEditClick}
|
||||||
|
label="Редактировать товар">
|
||||||
|
<ActionIcon variant={"default"}>
|
||||||
|
<IconEdit />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip
|
||||||
|
onClick={() => dealProductsCrud.onDelete(dealProduct)}
|
||||||
|
label="Удалить товар">
|
||||||
|
<ActionIcon variant={"default"}>
|
||||||
|
<IconTrash />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProductViewActions;
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
|
import { DataTableColumn } from "mantine-datatable";
|
||||||
|
import { ProductServiceSchema } from "@/lib/client";
|
||||||
|
import { ActionIcon, Flex, Tooltip } from "@mantine/core";
|
||||||
|
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
data: ProductServiceSchema[];
|
||||||
|
quantity: number;
|
||||||
|
onChange: (dealProductService: ProductServiceSchema) => void;
|
||||||
|
onDelete: (dealProductService: ProductServiceSchema) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useProductServicesTableColumns = ({ data, quantity, onChange, onDelete }: Props) => {
|
||||||
|
const totalPrice = useMemo(
|
||||||
|
() => data.reduce((acc, row) => acc + row.price * quantity, 0),
|
||||||
|
[data, quantity]
|
||||||
|
);
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() =>
|
||||||
|
[
|
||||||
|
{
|
||||||
|
accessor: "actions",
|
||||||
|
title: "Действия",
|
||||||
|
textAlign: "center",
|
||||||
|
width: "0%",
|
||||||
|
render: dealProductService => (
|
||||||
|
<Flex gap="md">
|
||||||
|
<Tooltip label="Удалить">
|
||||||
|
<ActionIcon
|
||||||
|
onClick={() => onDelete(dealProductService)}
|
||||||
|
variant={"default"}>
|
||||||
|
<IconTrash />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="Редактировать">
|
||||||
|
<ActionIcon
|
||||||
|
onClick={() => onChange(dealProductService)}
|
||||||
|
variant={"default"}>
|
||||||
|
<IconEdit />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
{/*<Tooltip label="Сотрудники">*/}
|
||||||
|
{/* <ActionIcon*/}
|
||||||
|
{/* onClick={() =>*/}
|
||||||
|
{/* onEmployeeClick(row.original)*/}
|
||||||
|
{/* }*/}
|
||||||
|
{/* variant={"default"}>*/}
|
||||||
|
{/* <IconUsersGroup />*/}
|
||||||
|
{/* </ActionIcon>*/}
|
||||||
|
{/*</Tooltip>*/}
|
||||||
|
</Flex>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: "service.name",
|
||||||
|
title: "Услуга",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: "price",
|
||||||
|
title: "Цена",
|
||||||
|
Footer: () => <>Итог: {totalPrice.toLocaleString("ru")}₽</>,
|
||||||
|
},
|
||||||
|
] as DataTableColumn<ProductServiceSchema>[],
|
||||||
|
[totalPrice]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useProductServicesTableColumns;
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { Button, Flex } from "@mantine/core";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import { useFulfillmentBaseContext } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/contexts/FulfillmentBaseContext";
|
||||||
|
|
||||||
|
const ProductsActions: FC = () => {
|
||||||
|
const { deal, dealProductsList, productsCrud, dealProductsCrud } =
|
||||||
|
useFulfillmentBaseContext();
|
||||||
|
|
||||||
|
const onCreateProductClick = () => {
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "productEditorModal",
|
||||||
|
title: "Создание товара",
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
onCreate: productsCrud.onCreate,
|
||||||
|
isEditing: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCreateDealProductClick = () => {
|
||||||
|
const productIdsToExclude = dealProductsList.dealProducts.map(
|
||||||
|
product => product.product.id
|
||||||
|
);
|
||||||
|
|
||||||
|
modals.openContextModal({
|
||||||
|
modal: "dealProductEditorModal",
|
||||||
|
title: "Добавление товара",
|
||||||
|
withCloseButton: false,
|
||||||
|
innerProps: {
|
||||||
|
onCreate: values =>
|
||||||
|
dealProductsCrud.onCreate({ ...values, dealId: deal.id }),
|
||||||
|
productIdsToExclude,
|
||||||
|
isEditing: false,
|
||||||
|
clientId: 0, // TODO add clients
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
w={"100%"}
|
||||||
|
direction={"column"}
|
||||||
|
gap={"sm"}>
|
||||||
|
<Button
|
||||||
|
variant={"default"}
|
||||||
|
onClick={onCreateProductClick}>
|
||||||
|
Создать товар
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={"default"}
|
||||||
|
onClick={onCreateDealProductClick}>
|
||||||
|
Добавить товар
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProductsActions;
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { omit } from "lodash";
|
||||||
|
import {
|
||||||
|
ComboboxItem,
|
||||||
|
ComboboxParsedItemGroup,
|
||||||
|
OptionsFilter,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import ObjectSelect, {
|
||||||
|
ObjectSelectProps,
|
||||||
|
} from "@/components/selects/ObjectSelect/ObjectSelect";
|
||||||
|
import { ServiceSchema } from "@/lib/client";
|
||||||
|
import useServicesList from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/hooks/lists/useServicesList";
|
||||||
|
import { ServiceType } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/types/service";
|
||||||
|
|
||||||
|
type RestProps = {
|
||||||
|
filterType?: ServiceType;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = Omit<ObjectSelectProps<ServiceSchema>, "data"> & RestProps;
|
||||||
|
|
||||||
|
const ServiceSelect: FC<Props> = props => {
|
||||||
|
const { services } = useServicesList();
|
||||||
|
const data = props.filterType
|
||||||
|
? services.filter(service => service.serviceType === props.filterType)
|
||||||
|
: services;
|
||||||
|
|
||||||
|
const restProps = omit(props, ["filterType"]);
|
||||||
|
|
||||||
|
const optionsFilter: OptionsFilter = ({ options, search }) => {
|
||||||
|
return (options as ComboboxParsedItemGroup[]).map(option => {
|
||||||
|
return {
|
||||||
|
...option,
|
||||||
|
items: option.items.filter((item: ComboboxItem) =>
|
||||||
|
item.label.toLowerCase().includes(search.toLowerCase())
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ObjectSelect
|
||||||
|
{...restProps}
|
||||||
|
data={data}
|
||||||
|
searchable
|
||||||
|
groupBy={service => service.category.name}
|
||||||
|
filter={optionsFilter}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServiceSelect;
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { DealSchema } from "@/lib/client";
|
||||||
|
import { getProductsQueryKey } from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
import makeContext from "@/lib/contextFactory/contextFactory";
|
||||||
|
import useDealServicesCrud, {
|
||||||
|
DealServicesCrud,
|
||||||
|
} from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/hooks/cruds/useDealServiceCrud";
|
||||||
|
import useProductServiceCrud, {
|
||||||
|
DealProductServicesCrud,
|
||||||
|
} from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/hooks/cruds/useProductServiceCrud";
|
||||||
|
import useDealServicesList, {
|
||||||
|
DealServicesList,
|
||||||
|
} from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/hooks/lists/useDealServicesList";
|
||||||
|
import useDealProductCrud, {
|
||||||
|
DealProductsCrud,
|
||||||
|
} from "../hooks/cruds/useDealProductCrud";
|
||||||
|
import { ProductsCrud, useProductsCrud } from "../hooks/cruds/useProductsCrud";
|
||||||
|
import useDealProductsList, {
|
||||||
|
DealProductsList,
|
||||||
|
} from "../hooks/lists/useDealProductsList";
|
||||||
|
|
||||||
|
type FulfillmentBaseContextState = {
|
||||||
|
deal: DealSchema;
|
||||||
|
productsCrud: ProductsCrud;
|
||||||
|
dealProductsList: DealProductsList;
|
||||||
|
dealProductsCrud: DealProductsCrud;
|
||||||
|
dealServicesList: DealServicesList;
|
||||||
|
dealServicesCrud: DealServicesCrud;
|
||||||
|
productServiceCrud: DealProductServicesCrud;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
deal: DealSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useFulfillmentBaseContextState = ({
|
||||||
|
deal,
|
||||||
|
}: Props): FulfillmentBaseContextState => {
|
||||||
|
const productQueryKey = getProductsQueryKey();
|
||||||
|
const productsCrud = useProductsCrud({ queryKey: productQueryKey });
|
||||||
|
|
||||||
|
const dealProductsList = useDealProductsList({ dealId: deal.id });
|
||||||
|
const dealProductsCrud = useDealProductCrud({ dealId: deal.id });
|
||||||
|
|
||||||
|
const dealServicesList = useDealServicesList({ dealId: deal.id });
|
||||||
|
const dealServicesCrud = useDealServicesCrud({ dealId: deal.id });
|
||||||
|
|
||||||
|
const productServiceCrud = useProductServiceCrud({
|
||||||
|
refetchDealProducts: dealProductsList.refetch,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
deal,
|
||||||
|
productsCrud,
|
||||||
|
dealProductsList,
|
||||||
|
dealProductsCrud,
|
||||||
|
dealServicesList,
|
||||||
|
dealServicesCrud,
|
||||||
|
productServiceCrud,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const [FulfillmentBaseContextProvider, useFulfillmentBaseContext] =
|
||||||
|
makeContext<FulfillmentBaseContextState, Props>(
|
||||||
|
useFulfillmentBaseContextState,
|
||||||
|
"FulfillmentBase"
|
||||||
|
);
|
||||||
@ -0,0 +1,153 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
import { Text } from "@mantine/core";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import getCommonQueryClient from "@/hooks/cruds/baseCrud/getCommonQueryClient";
|
||||||
|
import {
|
||||||
|
CreateDealProductSchema,
|
||||||
|
DealProductSchema,
|
||||||
|
UpdateDealProductSchema,
|
||||||
|
} from "@/lib/client";
|
||||||
|
import {
|
||||||
|
createDealProductMutation,
|
||||||
|
deleteDealProductMutation,
|
||||||
|
getDealProductsQueryKey,
|
||||||
|
updateDealProductMutation,
|
||||||
|
} from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
dealId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DealProductsCrud = {
|
||||||
|
onCreate: (data: CreateDealProductSchema) => void;
|
||||||
|
onUpdate: (
|
||||||
|
dealId: number,
|
||||||
|
serviceId: number,
|
||||||
|
data: UpdateDealProductSchema
|
||||||
|
) => void;
|
||||||
|
onDelete: (data: DealProductSchema, onSuccess?: () => void) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useDealProductCrud = ({ dealId }: Props): DealProductsCrud => {
|
||||||
|
const queryKey = getDealProductsQueryKey({ path: { dealId } });
|
||||||
|
const key = "getDealProducts";
|
||||||
|
const { queryClient, onError, onSettled } = getCommonQueryClient({
|
||||||
|
queryKey,
|
||||||
|
key,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createMutation = useMutation({
|
||||||
|
...createDealProductMutation(),
|
||||||
|
onError,
|
||||||
|
onSettled,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateMutation = useMutation({
|
||||||
|
...updateDealProductMutation(),
|
||||||
|
onError,
|
||||||
|
onSettled,
|
||||||
|
onMutate: async ({
|
||||||
|
body: { entity: update },
|
||||||
|
path: { dealId, productId },
|
||||||
|
}) => {
|
||||||
|
await queryClient.cancelQueries({ queryKey: [key] });
|
||||||
|
|
||||||
|
const previous = queryClient.getQueryData(queryKey);
|
||||||
|
|
||||||
|
queryClient.setQueryData(
|
||||||
|
queryKey,
|
||||||
|
(old: { items: DealProductSchema[] }) => {
|
||||||
|
const updated: DealProductSchema[] = old.items.map(
|
||||||
|
(entity: DealProductSchema) =>
|
||||||
|
entity.dealId === dealId &&
|
||||||
|
entity.productId === productId
|
||||||
|
? { ...entity, ...update }
|
||||||
|
: entity
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...old,
|
||||||
|
items: updated,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return { previous };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteMutation = useMutation({
|
||||||
|
...deleteDealProductMutation(),
|
||||||
|
onError,
|
||||||
|
onSettled,
|
||||||
|
onMutate: async ({ path: { dealId, productId } }) => {
|
||||||
|
await queryClient.cancelQueries({ queryKey: [key] });
|
||||||
|
|
||||||
|
const previous = queryClient.getQueryData(queryKey);
|
||||||
|
|
||||||
|
queryClient.setQueryData(
|
||||||
|
queryKey,
|
||||||
|
(old: { items: DealProductSchema[] }) => {
|
||||||
|
const filtered = old.items.filter(
|
||||||
|
e => e.dealId !== dealId && e.productId !== productId
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...old,
|
||||||
|
items: filtered,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return { previous };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onCreate = (entity: CreateDealProductSchema) => {
|
||||||
|
createMutation.mutate({
|
||||||
|
body: {
|
||||||
|
entity,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onUpdate = (
|
||||||
|
dealId: number,
|
||||||
|
productId: number,
|
||||||
|
entity: UpdateDealProductSchema
|
||||||
|
) => {
|
||||||
|
updateMutation.mutate({
|
||||||
|
body: {
|
||||||
|
entity,
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
dealId,
|
||||||
|
productId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDelete = (entity: DealProductSchema, onSuccess?: () => void) => {
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: "Удаление товара из сделки",
|
||||||
|
children: (
|
||||||
|
<Text>
|
||||||
|
Вы уверены, что хотите удалить "{entity.product.name}" из
|
||||||
|
сделки?
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
confirmProps: { color: "red" },
|
||||||
|
onConfirm: () => {
|
||||||
|
deleteMutation.mutate({ path: entity }, { onSuccess });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onCreate,
|
||||||
|
onUpdate,
|
||||||
|
onDelete,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDealProductCrud;
|
||||||
@ -0,0 +1,153 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
import { Text } from "@mantine/core";
|
||||||
|
import { modals } from "@mantine/modals";
|
||||||
|
import getCommonQueryClient from "@/hooks/cruds/baseCrud/getCommonQueryClient";
|
||||||
|
import {
|
||||||
|
CreateDealServiceSchema,
|
||||||
|
DealServiceSchema,
|
||||||
|
UpdateDealServiceSchema,
|
||||||
|
} from "@/lib/client";
|
||||||
|
import {
|
||||||
|
createDealServiceMutation,
|
||||||
|
deleteDealServiceMutation,
|
||||||
|
getDealServicesQueryKey,
|
||||||
|
updateDealServiceMutation,
|
||||||
|
} from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
dealId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DealServicesCrud = {
|
||||||
|
onCreate: (data: CreateDealServiceSchema) => void;
|
||||||
|
onUpdate: (
|
||||||
|
dealId: number,
|
||||||
|
serviceId: number,
|
||||||
|
data: UpdateDealServiceSchema
|
||||||
|
) => void;
|
||||||
|
onDelete: (data: DealServiceSchema, onSuccess?: () => void) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useDealServiceCrud = ({ dealId }: Props): DealServicesCrud => {
|
||||||
|
const queryKey = getDealServicesQueryKey({ path: { dealId } });
|
||||||
|
const key = "getDealServices";
|
||||||
|
const { queryClient, onError, onSettled } = getCommonQueryClient({
|
||||||
|
queryKey,
|
||||||
|
key,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createMutation = useMutation({
|
||||||
|
...createDealServiceMutation(),
|
||||||
|
onError,
|
||||||
|
onSettled,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateMutation = useMutation({
|
||||||
|
...updateDealServiceMutation(),
|
||||||
|
onError,
|
||||||
|
onSettled,
|
||||||
|
onMutate: async ({
|
||||||
|
body: { entity: update },
|
||||||
|
path: { dealId, serviceId },
|
||||||
|
}) => {
|
||||||
|
await queryClient.cancelQueries({ queryKey: [key] });
|
||||||
|
|
||||||
|
const previous = queryClient.getQueryData(queryKey);
|
||||||
|
|
||||||
|
queryClient.setQueryData(
|
||||||
|
queryKey,
|
||||||
|
(old: { items: DealServiceSchema[] }) => {
|
||||||
|
const updated: DealServiceSchema[] = old.items.map(
|
||||||
|
(entity: DealServiceSchema) =>
|
||||||
|
entity.dealId === dealId &&
|
||||||
|
entity.serviceId === serviceId
|
||||||
|
? { ...entity, ...update }
|
||||||
|
: entity
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...old,
|
||||||
|
items: updated,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return { previous };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteMutation = useMutation({
|
||||||
|
...deleteDealServiceMutation(),
|
||||||
|
onError,
|
||||||
|
onSettled,
|
||||||
|
onMutate: async ({ path: { dealId, serviceId } }) => {
|
||||||
|
await queryClient.cancelQueries({ queryKey: [key] });
|
||||||
|
|
||||||
|
const previous = queryClient.getQueryData(queryKey);
|
||||||
|
|
||||||
|
queryClient.setQueryData(
|
||||||
|
queryKey,
|
||||||
|
(old: { items: DealServiceSchema[] }) => {
|
||||||
|
const filtered = old.items.filter(
|
||||||
|
e => e.dealId !== dealId && e.serviceId !== serviceId
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...old,
|
||||||
|
items: filtered,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return { previous };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onCreate = (entity: CreateDealServiceSchema) => {
|
||||||
|
createMutation.mutate({
|
||||||
|
body: {
|
||||||
|
entity,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onUpdate = (
|
||||||
|
dealId: number,
|
||||||
|
serviceId: number,
|
||||||
|
entity: UpdateDealServiceSchema
|
||||||
|
) => {
|
||||||
|
updateMutation.mutate({
|
||||||
|
body: {
|
||||||
|
entity,
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
dealId,
|
||||||
|
serviceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDelete = (entity: DealServiceSchema, onSuccess?: () => void) => {
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: "Удаление услуги из сделки",
|
||||||
|
children: (
|
||||||
|
<Text>
|
||||||
|
Вы уверены, что хотите удалить "{entity.service.name}" из
|
||||||
|
сделки?
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
confirmProps: { color: "red" },
|
||||||
|
onConfirm: () => {
|
||||||
|
deleteMutation.mutate({ path: entity }, { onSuccess });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onCreate,
|
||||||
|
onUpdate,
|
||||||
|
onDelete,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDealServiceCrud;
|
||||||
@ -0,0 +1,102 @@
|
|||||||
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
import {
|
||||||
|
CreateProductServiceSchema,
|
||||||
|
ProductServiceSchema,
|
||||||
|
HttpValidationError,
|
||||||
|
UpdateProductServiceSchema,
|
||||||
|
} from "@/lib/client";
|
||||||
|
import {
|
||||||
|
createDealProductServiceMutation,
|
||||||
|
deleteDealProductServiceMutation,
|
||||||
|
updateDealProductServiceMutation,
|
||||||
|
} from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
import { notifications } from "@/lib/notifications";
|
||||||
|
|
||||||
|
export type DealProductServicesCrud = {
|
||||||
|
onCreate: (data: CreateProductServiceSchema) => void;
|
||||||
|
onUpdate: (
|
||||||
|
dealId: number,
|
||||||
|
productId: number,
|
||||||
|
serviceId: number,
|
||||||
|
data: UpdateProductServiceSchema
|
||||||
|
) => void;
|
||||||
|
onDelete: (data: ProductServiceSchema) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
refetchDealProducts: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useProductServiceCrud = ({
|
||||||
|
refetchDealProducts,
|
||||||
|
}: Props): DealProductServicesCrud => {
|
||||||
|
const onError = (error: AxiosError<HttpValidationError>, _: any) => {
|
||||||
|
console.error(error);
|
||||||
|
notifications.error({
|
||||||
|
message: error.response?.data?.detail as string | undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createMutation = useMutation({
|
||||||
|
...createDealProductServiceMutation(),
|
||||||
|
onError,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateMutation = useMutation({
|
||||||
|
...updateDealProductServiceMutation(),
|
||||||
|
onError,
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteMutation = useMutation({
|
||||||
|
...deleteDealProductServiceMutation(),
|
||||||
|
onError,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onCreate = (entity: CreateProductServiceSchema) => {
|
||||||
|
createMutation.mutate(
|
||||||
|
{
|
||||||
|
body: {
|
||||||
|
entity,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ onSuccess: refetchDealProducts }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onUpdate = (
|
||||||
|
dealId: number,
|
||||||
|
productId: number,
|
||||||
|
serviceId: number,
|
||||||
|
entity: UpdateProductServiceSchema
|
||||||
|
) => {
|
||||||
|
updateMutation.mutate(
|
||||||
|
{
|
||||||
|
body: {
|
||||||
|
entity,
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
dealId,
|
||||||
|
productId,
|
||||||
|
serviceId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ onSuccess: refetchDealProducts }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDelete = (entity: ProductServiceSchema) => {
|
||||||
|
deleteMutation.mutate(
|
||||||
|
{ path: entity },
|
||||||
|
{ onSuccess: refetchDealProducts }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onCreate,
|
||||||
|
onUpdate,
|
||||||
|
onDelete,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useProductServiceCrud;
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { useCrudOperations } from "@/hooks/cruds/baseCrud";
|
||||||
|
import {
|
||||||
|
CreateProductSchema,
|
||||||
|
ProductSchema,
|
||||||
|
UpdateProductSchema,
|
||||||
|
} from "@/lib/client";
|
||||||
|
import {
|
||||||
|
createProductMutation,
|
||||||
|
deleteProductMutation,
|
||||||
|
updateProductMutation,
|
||||||
|
} from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
type UseProductsProps = {
|
||||||
|
queryKey: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ProductsCrud = {
|
||||||
|
onCreate: (product: CreateProductSchema) => void;
|
||||||
|
onUpdate: (
|
||||||
|
productId: number,
|
||||||
|
product: UpdateProductSchema,
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => void;
|
||||||
|
onDelete: (product: ProductSchema) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useProductsCrud = ({
|
||||||
|
queryKey,
|
||||||
|
}: UseProductsProps): ProductsCrud => {
|
||||||
|
return useCrudOperations<
|
||||||
|
ProductSchema,
|
||||||
|
UpdateProductSchema,
|
||||||
|
CreateProductSchema
|
||||||
|
>({
|
||||||
|
key: "getProducts",
|
||||||
|
queryKey,
|
||||||
|
mutations: {
|
||||||
|
create: createProductMutation(),
|
||||||
|
update: updateProductMutation(),
|
||||||
|
delete: deleteProductMutation(),
|
||||||
|
},
|
||||||
|
getUpdateEntity: (old, update) =>
|
||||||
|
({
|
||||||
|
...old,
|
||||||
|
...update,
|
||||||
|
}) as ProductSchema,
|
||||||
|
getDeleteConfirmTitle: () => "Удаление товара",
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { useCrudOperations } from "@/hooks/cruds/baseCrud";
|
||||||
|
import {
|
||||||
|
CreateServiceSchema,
|
||||||
|
ServiceSchema,
|
||||||
|
UpdateServiceSchema,
|
||||||
|
} from "@/lib/client";
|
||||||
|
import {
|
||||||
|
createServiceMutation,
|
||||||
|
deleteServiceMutation,
|
||||||
|
updateServiceMutation,
|
||||||
|
} from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
type UseServicesProps = {
|
||||||
|
queryKey: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServicesCrud = {
|
||||||
|
onCreate: (service: CreateServiceSchema) => void;
|
||||||
|
onUpdate: (
|
||||||
|
serviceId: number,
|
||||||
|
service: UpdateServiceSchema,
|
||||||
|
onSuccess?: () => void
|
||||||
|
) => void;
|
||||||
|
onDelete: (service: ServiceSchema) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useServicesCrud = ({
|
||||||
|
queryKey,
|
||||||
|
}: UseServicesProps): ServicesCrud => {
|
||||||
|
return useCrudOperations<
|
||||||
|
ServiceSchema,
|
||||||
|
UpdateServiceSchema,
|
||||||
|
CreateServiceSchema
|
||||||
|
>({
|
||||||
|
key: "getServices",
|
||||||
|
queryKey,
|
||||||
|
mutations: {
|
||||||
|
create: createServiceMutation(),
|
||||||
|
update: updateServiceMutation(),
|
||||||
|
delete: deleteServiceMutation(),
|
||||||
|
},
|
||||||
|
getUpdateEntity: (old, update) =>
|
||||||
|
({
|
||||||
|
...old,
|
||||||
|
...update,
|
||||||
|
}) as ServiceSchema,
|
||||||
|
getDeleteConfirmTitle: () => "Удаление услуги",
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { DealProductSchema } from "@/lib/client";
|
||||||
|
import {
|
||||||
|
getDealProductsOptions,
|
||||||
|
getDealProductsQueryKey,
|
||||||
|
} from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
dealId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DealProductsList = {
|
||||||
|
dealProducts: DealProductSchema[];
|
||||||
|
setDealProducts: (dealProducts: DealProductSchema[]) => void;
|
||||||
|
refetch: () => void;
|
||||||
|
queryKey: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const useDealProductsList = ({ dealId }: Props): DealProductsList => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const options = {
|
||||||
|
path: { dealId },
|
||||||
|
};
|
||||||
|
const { data, refetch } = useQuery({
|
||||||
|
...getDealProductsOptions(options),
|
||||||
|
enabled: !!dealId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryKey = getDealProductsQueryKey(options);
|
||||||
|
|
||||||
|
const setDealProducts = (dealProducts: DealProductSchema[]) => {
|
||||||
|
queryClient.setQueryData(
|
||||||
|
queryKey,
|
||||||
|
(old: { items: DealProductSchema[] }) => ({
|
||||||
|
...old,
|
||||||
|
items: dealProducts,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
dealProducts: data?.items ?? [],
|
||||||
|
setDealProducts,
|
||||||
|
refetch,
|
||||||
|
queryKey,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDealProductsList;
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { DealServiceSchema } from "@/lib/client";
|
||||||
|
import {
|
||||||
|
getDealServicesOptions,
|
||||||
|
getDealServicesQueryKey,
|
||||||
|
} from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
dealId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DealServicesList = {
|
||||||
|
dealServices: DealServiceSchema[];
|
||||||
|
setDealServices: (dealServices: DealServiceSchema[]) => void;
|
||||||
|
refetch: () => void;
|
||||||
|
queryKey: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const useDealServicesList = ({ dealId }: Props): DealServicesList => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const options = {
|
||||||
|
path: { dealId },
|
||||||
|
};
|
||||||
|
const { data, refetch } = useQuery({
|
||||||
|
...getDealServicesOptions(options),
|
||||||
|
enabled: !!dealId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryKey = getDealServicesQueryKey(options);
|
||||||
|
|
||||||
|
const setDealServices = (dealServices: DealServiceSchema[]) => {
|
||||||
|
queryClient.setQueryData(
|
||||||
|
queryKey,
|
||||||
|
(old: { items: DealServiceSchema[] }) => ({
|
||||||
|
...old,
|
||||||
|
items: dealServices,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
dealServices: data?.items ?? [],
|
||||||
|
setDealServices,
|
||||||
|
refetch,
|
||||||
|
queryKey,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDealServicesList;
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { ProductSchema } from "@/lib/client";
|
||||||
|
import {
|
||||||
|
getProductsOptions,
|
||||||
|
getProductsQueryKey,
|
||||||
|
} from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
searchInput: string;
|
||||||
|
page?: number;
|
||||||
|
itemsPerPage?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useProductsList = ({ searchInput, page, itemsPerPage }: Props) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const options = {
|
||||||
|
query: { searchInput, page, itemsPerPage },
|
||||||
|
};
|
||||||
|
const { data, refetch, isLoading } = useQuery(getProductsOptions(options));
|
||||||
|
|
||||||
|
const queryKey = getProductsQueryKey(options);
|
||||||
|
|
||||||
|
const setProducts = (products: ProductSchema[]) => {
|
||||||
|
queryClient.setQueryData(
|
||||||
|
queryKey,
|
||||||
|
(old: { items: ProductSchema[] }) => ({
|
||||||
|
...old,
|
||||||
|
items: products,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
products: data?.items ?? [],
|
||||||
|
setProducts,
|
||||||
|
refetch,
|
||||||
|
queryKey,
|
||||||
|
isLoading,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useProductsList;
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { ServicesKitSchema } from "@/lib/client";
|
||||||
|
import {
|
||||||
|
getServicesKitsOptions,
|
||||||
|
getServicesKitsQueryKey,
|
||||||
|
} from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
const useServicesKitsList = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { data, refetch, isLoading } = useQuery(getServicesKitsOptions());
|
||||||
|
|
||||||
|
const queryKey = getServicesKitsQueryKey();
|
||||||
|
|
||||||
|
const setServicesKits = (servicesKits: ServicesKitSchema[]) => {
|
||||||
|
queryClient.setQueryData(
|
||||||
|
queryKey,
|
||||||
|
(old: { items: ServicesKitSchema[] }) => ({
|
||||||
|
...old,
|
||||||
|
items: servicesKits,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
servicesKits: data?.items ?? [],
|
||||||
|
setServicesKits,
|
||||||
|
refetch,
|
||||||
|
queryKey,
|
||||||
|
isLoading,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useServicesKitsList;
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { ServiceSchema } from "@/lib/client";
|
||||||
|
import {
|
||||||
|
getServicesOptions,
|
||||||
|
getServicesQueryKey,
|
||||||
|
} from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
|
||||||
|
const useServicesList = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { data, refetch, isLoading } = useQuery(getServicesOptions());
|
||||||
|
|
||||||
|
const queryKey = getServicesQueryKey();
|
||||||
|
|
||||||
|
const setServices = (services: ServiceSchema[]) => {
|
||||||
|
queryClient.setQueryData(
|
||||||
|
queryKey,
|
||||||
|
(old: { items: ServiceSchema[] }) => ({
|
||||||
|
...old,
|
||||||
|
items: services,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
services: data?.items ?? [],
|
||||||
|
setServices,
|
||||||
|
refetch,
|
||||||
|
queryKey,
|
||||||
|
isLoading,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useServicesList;
|
||||||
@ -0,0 +1,148 @@
|
|||||||
|
import { CRUDTableProps } from "../../../../../types/CRUDTable.tsx";
|
||||||
|
import { CardService, CardServiceSchema, CardProductSchema } from "../../../../../client";
|
||||||
|
import { useCardPageContext } from "../../../../../pages/CardsPage/contexts/CardPageContext.tsx";
|
||||||
|
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||||||
|
|
||||||
|
const useCardState = () => {
|
||||||
|
|
||||||
|
const { selectedCard, setSelectedCard } = useCardPageContext();
|
||||||
|
const recalculate = async () => {
|
||||||
|
return CardService.recalculateCardPrice({
|
||||||
|
requestBody: {
|
||||||
|
cardId: selectedCard?.id || -1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const refetchCard = async () => {
|
||||||
|
if (!selectedCard) return;
|
||||||
|
|
||||||
|
return CardService.getCardById({ cardId: selectedCard.id }).then(
|
||||||
|
async card => {
|
||||||
|
setSelectedCard(card);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const refetch = async () => {
|
||||||
|
if (!selectedCard) return;
|
||||||
|
await refetchCard();
|
||||||
|
const { ok, message } = await recalculate();
|
||||||
|
if (!ok) notifications.guess(ok, { message });
|
||||||
|
|
||||||
|
await refetchCard();
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
card: selectedCard,
|
||||||
|
refetch,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const useCardServicesState = (): CRUDTableProps<CardServiceSchema> => {
|
||||||
|
const { card, refetch } = useCardState();
|
||||||
|
const refetchAndRecalculate = async () => {
|
||||||
|
await refetch();
|
||||||
|
};
|
||||||
|
const onCreate = (item: CardServiceSchema) => {
|
||||||
|
if (!card) return;
|
||||||
|
CardService.addCardService({
|
||||||
|
requestBody: {
|
||||||
|
cardId: card.id,
|
||||||
|
serviceId: item.service.id,
|
||||||
|
quantity: item.quantity,
|
||||||
|
price: item.price,
|
||||||
|
},
|
||||||
|
}).then(async ({ ok, message }) => {
|
||||||
|
if (!ok) notifications.guess(ok, { message });
|
||||||
|
if (ok) await refetchAndRecalculate();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onDelete = (item: CardServiceSchema) => {
|
||||||
|
if (!card) return;
|
||||||
|
CardService.deleteCardService({
|
||||||
|
requestBody: {
|
||||||
|
cardId: card.id,
|
||||||
|
serviceId: item.service.id,
|
||||||
|
},
|
||||||
|
}).then(async ({ ok, message }) => {
|
||||||
|
if (!ok) notifications.guess(ok, { message });
|
||||||
|
if (ok) await refetchAndRecalculate();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onChange = (item: CardServiceSchema) => {
|
||||||
|
if (!card) return;
|
||||||
|
CardService.updateCardService({
|
||||||
|
requestBody: {
|
||||||
|
cardId: card.id,
|
||||||
|
service: item,
|
||||||
|
},
|
||||||
|
}).then(async ({ ok, message }) => {
|
||||||
|
if (!ok) notifications.guess(ok, { message });
|
||||||
|
if (ok) await refetchAndRecalculate();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
items: card?.services || [],
|
||||||
|
onCreate,
|
||||||
|
onDelete,
|
||||||
|
onChange,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const useDealProductsState = (): CRUDTableProps<CardProductSchema> => {
|
||||||
|
const { card, refetch } = useCardState();
|
||||||
|
const refetchAndRecalculate = async () => {
|
||||||
|
await refetch();
|
||||||
|
};
|
||||||
|
const onCreate = (item: CardProductSchema) => {
|
||||||
|
if (!card) return;
|
||||||
|
CardService.addCardProduct({
|
||||||
|
requestBody: {
|
||||||
|
cardId: card.id,
|
||||||
|
product: item,
|
||||||
|
},
|
||||||
|
}).then(async ({ ok, message }) => {
|
||||||
|
if (!ok) notifications.guess(ok, { message });
|
||||||
|
if (ok) await refetchAndRecalculate();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onDelete = (item: CardProductSchema) => {
|
||||||
|
if (!card) return;
|
||||||
|
CardService.deleteCardProduct({
|
||||||
|
requestBody: {
|
||||||
|
cardId: card.id,
|
||||||
|
productId: item.product.id,
|
||||||
|
},
|
||||||
|
}).then(async ({ ok, message }) => {
|
||||||
|
if (!ok) notifications.guess(ok, { message });
|
||||||
|
if (ok) await refetchAndRecalculate();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onChange = (item: CardProductSchema) => {
|
||||||
|
if (!card) return;
|
||||||
|
CardService.updateCardProduct({
|
||||||
|
requestBody: {
|
||||||
|
cardId: card.id,
|
||||||
|
product: item,
|
||||||
|
},
|
||||||
|
}).then(async ({ ok, message }) => {
|
||||||
|
if (!ok) notifications.guess(ok, { message });
|
||||||
|
if (ok) await refetchAndRecalculate();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
items: card?.products || [],
|
||||||
|
onCreate,
|
||||||
|
onDelete,
|
||||||
|
onChange,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const useCardProductAndServiceTabState = () => {
|
||||||
|
const cardState = useCardState();
|
||||||
|
const cardProductsState = useDealProductsState();
|
||||||
|
const cardServicesState = useCardServicesState();
|
||||||
|
return {
|
||||||
|
cardState,
|
||||||
|
cardProductsState,
|
||||||
|
cardServicesState,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export default useCardProductAndServiceTabState;
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { NumberInput, Textarea } from "@mantine/core";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
|
import {
|
||||||
|
CreateDealProductSchema,
|
||||||
|
DealProductSchema,
|
||||||
|
UpdateDealProductSchema,
|
||||||
|
} from "@/lib/client";
|
||||||
|
import BaseFormModal, {
|
||||||
|
CreateEditFormProps,
|
||||||
|
} from "@/modals/base/BaseFormModal/BaseFormModal";
|
||||||
|
import ProductSelect from "../../components/ProductSelect/ProductSelect";
|
||||||
|
|
||||||
|
type RestProps = {
|
||||||
|
clientId: number;
|
||||||
|
productIdsToExclude?: number[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = CreateEditFormProps<
|
||||||
|
CreateDealProductSchema,
|
||||||
|
UpdateDealProductSchema,
|
||||||
|
DealProductSchema
|
||||||
|
> &
|
||||||
|
RestProps;
|
||||||
|
|
||||||
|
const DealProductEditorModal = ({
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
innerProps,
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const form = useForm<Partial<DealProductSchema>>({
|
||||||
|
initialValues: innerProps.isEditing
|
||||||
|
? innerProps.entity
|
||||||
|
: {
|
||||||
|
quantity: 1,
|
||||||
|
comment: "",
|
||||||
|
},
|
||||||
|
validate: {
|
||||||
|
product: product => !product && "Необходимо выбрать товар",
|
||||||
|
quantity: quantity =>
|
||||||
|
(!quantity || quantity === 0) &&
|
||||||
|
"Количество должно быть больше 0",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
context.closeContextModal(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseFormModal
|
||||||
|
{...innerProps}
|
||||||
|
form={form}
|
||||||
|
closeOnSubmit
|
||||||
|
onClose={onClose}>
|
||||||
|
<ProductSelect
|
||||||
|
placeholder={"Выберите товар"}
|
||||||
|
label={"Товар"}
|
||||||
|
clientId={innerProps.clientId}
|
||||||
|
disabled={innerProps.isEditing}
|
||||||
|
filterBy={item =>
|
||||||
|
!(innerProps.productIdsToExclude || []).includes(item.id)
|
||||||
|
}
|
||||||
|
{...form.getInputProps("product")}
|
||||||
|
onChange={product => {
|
||||||
|
form.setFieldValue("product", product);
|
||||||
|
form.setFieldValue("productId", product.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
placeholder={"Введите количество"}
|
||||||
|
label={"Количество"}
|
||||||
|
min={1}
|
||||||
|
{...form.getInputProps("quantity")}
|
||||||
|
/>
|
||||||
|
<Textarea
|
||||||
|
placeholder={"Введите комментарий"}
|
||||||
|
label={"Комментарий"}
|
||||||
|
{...form.getInputProps("comment")}
|
||||||
|
/>
|
||||||
|
</BaseFormModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DealProductEditorModal;
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
ComboboxItem,
|
||||||
|
ComboboxItemGroup,
|
||||||
|
NumberInput,
|
||||||
|
OptionsFilter,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
|
import {
|
||||||
|
CreateDealServiceSchema,
|
||||||
|
DealServiceSchema,
|
||||||
|
UpdateDealServiceSchema,
|
||||||
|
} from "@/lib/client";
|
||||||
|
import BaseFormModal, {
|
||||||
|
CreateEditFormProps,
|
||||||
|
} from "@/modals/base/BaseFormModal/BaseFormModal";
|
||||||
|
import { ServiceType } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/types/service";
|
||||||
|
import ServiceWithPriceInput from "./components/ServiceWithPriceInput";
|
||||||
|
|
||||||
|
type RestProps = {
|
||||||
|
serviceIdsToExclude?: number[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = CreateEditFormProps<
|
||||||
|
CreateDealServiceSchema,
|
||||||
|
UpdateDealServiceSchema,
|
||||||
|
DealServiceSchema
|
||||||
|
> &
|
||||||
|
RestProps;
|
||||||
|
|
||||||
|
const DealServiceEditorModal = ({
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
innerProps,
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const form = useForm<Partial<DealServiceSchema>>({
|
||||||
|
initialValues: innerProps.isEditing
|
||||||
|
? innerProps.entity
|
||||||
|
: {
|
||||||
|
service: undefined,
|
||||||
|
serviceId: undefined,
|
||||||
|
quantity: 1,
|
||||||
|
isFixedPrice: false,
|
||||||
|
},
|
||||||
|
validate: {
|
||||||
|
service: service => !service && "Необходимо выбрать услугу",
|
||||||
|
quantity: quantity =>
|
||||||
|
(!quantity || quantity === 0) &&
|
||||||
|
"Количество должно быть больше 0",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onClose = () => context.closeContextModal(id);
|
||||||
|
|
||||||
|
const serviceOptionsFilter = ({
|
||||||
|
options,
|
||||||
|
}: {
|
||||||
|
options: ComboboxItemGroup[];
|
||||||
|
}) => {
|
||||||
|
if (!innerProps.serviceIdsToExclude) return options;
|
||||||
|
const productServiceIds = innerProps.serviceIdsToExclude;
|
||||||
|
return (options as ComboboxItemGroup[]).map(({ items, group }) => {
|
||||||
|
return {
|
||||||
|
group,
|
||||||
|
items: items.filter(
|
||||||
|
item =>
|
||||||
|
!productServiceIds.includes(
|
||||||
|
Number((item as ComboboxItem).value)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseFormModal
|
||||||
|
{...innerProps}
|
||||||
|
form={form}
|
||||||
|
closeOnSubmit
|
||||||
|
onClose={onClose}>
|
||||||
|
<ServiceWithPriceInput
|
||||||
|
serviceProps={{
|
||||||
|
...form.getInputProps("service"),
|
||||||
|
onChange: service => {
|
||||||
|
form.setFieldValue("service", service);
|
||||||
|
form.setFieldValue("serviceId", service.id);
|
||||||
|
},
|
||||||
|
label: "Услуга",
|
||||||
|
placeholder: "Выберите услугу",
|
||||||
|
disabled: innerProps.isEditing,
|
||||||
|
filter: serviceOptionsFilter as OptionsFilter,
|
||||||
|
}}
|
||||||
|
priceProps={{
|
||||||
|
...form.getInputProps("price"),
|
||||||
|
label: "Цена",
|
||||||
|
placeholder: "Введите цену",
|
||||||
|
}}
|
||||||
|
quantity={form.values.quantity || 1}
|
||||||
|
filterType={ServiceType.DEAL_SERVICE}
|
||||||
|
lockOnEdit={innerProps.isEditing}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
placeholder={"Введите количество"}
|
||||||
|
label={"Количество"}
|
||||||
|
min={1}
|
||||||
|
{...form.getInputProps("quantity")}
|
||||||
|
/>
|
||||||
|
</BaseFormModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DealServiceEditorModal;
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { FC, useEffect, useRef, useState } from "react";
|
||||||
|
import {
|
||||||
|
NumberInput,
|
||||||
|
NumberInputProps,
|
||||||
|
Stack,
|
||||||
|
StackProps,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { ObjectSelectProps } from "@/components/selects/ObjectSelect/ObjectSelect";
|
||||||
|
import { ServiceSchema } from "@/lib/client";
|
||||||
|
import ServiceSelect from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/ServiceSelect/ServiceSelect";
|
||||||
|
import { ServiceType } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/types/service";
|
||||||
|
|
||||||
|
type ServiceProps = Omit<ObjectSelectProps<ServiceSchema>, "data">;
|
||||||
|
type PriceProps = NumberInputProps;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
serviceProps: ServiceProps;
|
||||||
|
priceProps: PriceProps;
|
||||||
|
quantity: number;
|
||||||
|
containerProps?: StackProps;
|
||||||
|
filterType?: ServiceType;
|
||||||
|
lockOnEdit?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ServiceWithPriceInput: FC<Props> = ({
|
||||||
|
serviceProps,
|
||||||
|
priceProps,
|
||||||
|
quantity,
|
||||||
|
containerProps,
|
||||||
|
filterType = ServiceType.PRODUCT_SERVICE,
|
||||||
|
lockOnEdit = true,
|
||||||
|
}) => {
|
||||||
|
const [price, setPrice] = useState<number | undefined>(
|
||||||
|
typeof priceProps.value === "number" ? priceProps.value : undefined
|
||||||
|
);
|
||||||
|
const [service, setService] = useState<ServiceSchema | undefined>(
|
||||||
|
serviceProps.value
|
||||||
|
);
|
||||||
|
const isFirstRender = useRef(true);
|
||||||
|
|
||||||
|
const getPriceBasedOnQuantity = (): number | null => {
|
||||||
|
if (!service || !service.priceRanges.length) return null;
|
||||||
|
const range =
|
||||||
|
service.priceRanges.find(
|
||||||
|
priceRange =>
|
||||||
|
quantity >= priceRange.fromQuantity &&
|
||||||
|
quantity <= priceRange.toQuantity
|
||||||
|
) || service.priceRanges[0];
|
||||||
|
|
||||||
|
return range.price;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setPriceBasedOnService = () => {
|
||||||
|
if (!service) return;
|
||||||
|
const rangePrice = getPriceBasedOnQuantity();
|
||||||
|
setPrice(rangePrice || service.price);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPriceManualChange = (value: number | string) => {
|
||||||
|
if (typeof value !== "number") return;
|
||||||
|
setPrice(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isFirstRender.current && lockOnEdit) return;
|
||||||
|
const price = getPriceBasedOnQuantity();
|
||||||
|
if (price) setPrice(price);
|
||||||
|
}, [quantity]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isFirstRender.current && lockOnEdit) return;
|
||||||
|
if (!priceProps.onChange || price === undefined) return;
|
||||||
|
priceProps.onChange(price);
|
||||||
|
}, [price]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!serviceProps.onChange ||
|
||||||
|
!service ||
|
||||||
|
(price && isFirstRender.current && lockOnEdit)
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
setPriceBasedOnService();
|
||||||
|
serviceProps.onChange(service);
|
||||||
|
}, [service]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
isFirstRender.current = false;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
gap={"xs"}
|
||||||
|
{...containerProps}>
|
||||||
|
<ServiceSelect
|
||||||
|
{...serviceProps}
|
||||||
|
value={service}
|
||||||
|
onChange={setService}
|
||||||
|
filterType={filterType}
|
||||||
|
disabled={lockOnEdit}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
{...priceProps}
|
||||||
|
onChange={onPriceManualChange}
|
||||||
|
defaultValue={priceProps.value}
|
||||||
|
min={1}
|
||||||
|
allowNegative={false}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServiceWithPriceInput;
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Button, Flex, Text } from "@mantine/core";
|
||||||
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
|
import ObjectMultiSelect from "@/components/selects/ObjectMultiSelect/ObjectMultiSelect";
|
||||||
|
import { DealProductSchema } from "@/lib/client";
|
||||||
|
import { notifications } from "@/lib/notifications";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
dealProducts: DealProductSchema[];
|
||||||
|
sourceDealProduct: DealProductSchema;
|
||||||
|
duplicateServices: (
|
||||||
|
sourceDealProduct: DealProductSchema,
|
||||||
|
targetDealProducts: DealProductSchema[]
|
||||||
|
) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DuplicateServicesModal = ({
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
innerProps,
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const [selectedDealProducts, setSelectedDealProducts] = useState<
|
||||||
|
DealProductSchema[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const onDealProductSelect = () => {
|
||||||
|
if (!selectedDealProducts) {
|
||||||
|
notifications.error({
|
||||||
|
message:
|
||||||
|
"Выберите товары на которые необходимо продублировать услуги",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
innerProps.duplicateServices(
|
||||||
|
innerProps.sourceDealProduct,
|
||||||
|
selectedDealProducts
|
||||||
|
);
|
||||||
|
context.closeContextModal(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDuplicateAllClick = () => {
|
||||||
|
innerProps.duplicateServices(
|
||||||
|
innerProps.sourceDealProduct,
|
||||||
|
innerProps.dealProducts.filter(
|
||||||
|
item => item !== innerProps.sourceDealProduct
|
||||||
|
)
|
||||||
|
);
|
||||||
|
context.closeContextModal(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
direction={"column"}
|
||||||
|
gap={"xs"}>
|
||||||
|
<ObjectMultiSelect
|
||||||
|
w={"100%"}
|
||||||
|
label={"Товары"}
|
||||||
|
placeholder={
|
||||||
|
"Выберите товары на которые нужно продублировать услуги"
|
||||||
|
}
|
||||||
|
onChange={setSelectedDealProducts}
|
||||||
|
value={selectedDealProducts}
|
||||||
|
data={innerProps.dealProducts}
|
||||||
|
getLabelFn={item => item.product.name}
|
||||||
|
getValueFn={item => item.product.id.toString()}
|
||||||
|
filterBy={item => item !== innerProps.sourceDealProduct}
|
||||||
|
/>
|
||||||
|
<Flex
|
||||||
|
gap={"xs"}
|
||||||
|
justify={"flex-end"}>
|
||||||
|
<Button
|
||||||
|
variant={"subtle"}
|
||||||
|
onClick={() => context.closeContextModal(id)}>
|
||||||
|
<Text>Отменить</Text>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={"default"}
|
||||||
|
onClick={onDuplicateAllClick}>
|
||||||
|
<Text>Продублировать на все товары</Text>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={onDealProductSelect}
|
||||||
|
variant={"default"}>
|
||||||
|
<Text>Продублировать</Text>
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DuplicateServicesModal;
|
||||||
@ -0,0 +1,122 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Fieldset, Flex, rem, TextInput } from "@mantine/core";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
|
import {
|
||||||
|
CreateProductSchema,
|
||||||
|
ProductSchema,
|
||||||
|
UpdateProductSchema,
|
||||||
|
} from "@/lib/client";
|
||||||
|
import BaseFormModal, {
|
||||||
|
CreateEditFormProps,
|
||||||
|
} from "@/modals/base/BaseFormModal/BaseFormModal";
|
||||||
|
import ProductImageDropzone from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/components/ProductImageDropzone/ProductImageDropzone";
|
||||||
|
import BaseFormInputProps from "@/utils/baseFormInputProps";
|
||||||
|
|
||||||
|
type Props = CreateEditFormProps<
|
||||||
|
CreateProductSchema,
|
||||||
|
UpdateProductSchema,
|
||||||
|
ProductSchema
|
||||||
|
>;
|
||||||
|
|
||||||
|
const ProductEditorModal = ({
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
innerProps,
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const isEditing = "entity" in innerProps;
|
||||||
|
|
||||||
|
const initialValues: Partial<ProductSchema> = isEditing
|
||||||
|
? innerProps.entity!
|
||||||
|
: {
|
||||||
|
name: "",
|
||||||
|
article: "",
|
||||||
|
factoryArticle: "",
|
||||||
|
brand: "",
|
||||||
|
composition: "",
|
||||||
|
color: "",
|
||||||
|
size: "",
|
||||||
|
additionalInfo: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const form = useForm<Partial<ProductSchema>>({
|
||||||
|
initialValues,
|
||||||
|
validate: {
|
||||||
|
name: name =>
|
||||||
|
!name || name.trim() !== ""
|
||||||
|
? null
|
||||||
|
: "Необходимо ввести название товара",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onClose = () => context.closeContextModal(id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseFormModal
|
||||||
|
{...innerProps}
|
||||||
|
form={form}
|
||||||
|
closeOnSubmit
|
||||||
|
onClose={onClose}>
|
||||||
|
<Flex
|
||||||
|
gap={rem(10)}
|
||||||
|
direction={"column"}>
|
||||||
|
<Fieldset legend={"Основные характеристики"}>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите название товара"}
|
||||||
|
label={"Название товара"}
|
||||||
|
{...form.getInputProps("name")}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите артикул"}
|
||||||
|
label={"Артикул"}
|
||||||
|
{...form.getInputProps("article")}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите складской артикул"}
|
||||||
|
label={"Складской артикул"}
|
||||||
|
{...form.getInputProps("factoryArticle")}
|
||||||
|
/>
|
||||||
|
</Fieldset>
|
||||||
|
<Fieldset legend={"Дополнительные характеристики"}>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите бренд"}
|
||||||
|
label={"Бренд"}
|
||||||
|
{...form.getInputProps("brand")}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите состав"}
|
||||||
|
label={"Состав"}
|
||||||
|
{...form.getInputProps("composition")}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите цвет"}
|
||||||
|
label={"Цвет"}
|
||||||
|
{...form.getInputProps("color")}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите размер"}
|
||||||
|
label={"Размер"}
|
||||||
|
{...form.getInputProps("size")}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
placeholder={"Введите доп. информацию"}
|
||||||
|
label={"Доп. информация"}
|
||||||
|
{...form.getInputProps("additionalInfo")}
|
||||||
|
/>
|
||||||
|
</Fieldset>
|
||||||
|
{isEditing && (
|
||||||
|
<ProductImageDropzone
|
||||||
|
imageUrlInputProps={
|
||||||
|
form.getInputProps(
|
||||||
|
"imageUrl"
|
||||||
|
) as BaseFormInputProps<string>
|
||||||
|
}
|
||||||
|
productId={innerProps.entity.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</BaseFormModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ProductEditorModal;
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { isNil, isNumber } from "lodash";
|
||||||
|
import { Checkbox, Flex } from "@mantine/core";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
|
import {
|
||||||
|
CreateProductServiceSchema,
|
||||||
|
ProductServiceSchema,
|
||||||
|
UpdateProductServiceSchema,
|
||||||
|
} from "@/lib/client/index.js";
|
||||||
|
import BaseFormModal, {
|
||||||
|
CreateEditFormProps,
|
||||||
|
} from "@/modals/base/BaseFormModal/BaseFormModal";
|
||||||
|
import ServiceWithPriceInput from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/modals/DealServiceEditorModal/components/ServiceWithPriceInput";
|
||||||
|
import { ServiceType } from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/types/service";
|
||||||
|
|
||||||
|
type RestProps = {
|
||||||
|
quantity: number;
|
||||||
|
excludeServiceIds: number[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = CreateEditFormProps<
|
||||||
|
CreateProductServiceSchema,
|
||||||
|
UpdateProductServiceSchema,
|
||||||
|
ProductServiceSchema
|
||||||
|
> &
|
||||||
|
RestProps;
|
||||||
|
|
||||||
|
const ProductServiceEditorModal = ({
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
innerProps,
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const initialValues: Partial<ProductServiceSchema> = innerProps.isEditing
|
||||||
|
? innerProps.entity
|
||||||
|
: {
|
||||||
|
service: undefined,
|
||||||
|
serviceId: undefined,
|
||||||
|
price: undefined,
|
||||||
|
// employees: [],
|
||||||
|
isFixedPrice: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const form = useForm<Partial<ProductServiceSchema>>({
|
||||||
|
initialValues,
|
||||||
|
validate: {
|
||||||
|
service: service =>
|
||||||
|
(isNil(service) || service.id < 0) && "Укажите услугу",
|
||||||
|
price: price => (!isNumber(price) || price < 0) && "Укажите цену",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onClose = () => context.closeContextModal(id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseFormModal
|
||||||
|
{...innerProps}
|
||||||
|
form={form}
|
||||||
|
onClose={onClose}
|
||||||
|
closeOnSubmit>
|
||||||
|
<Flex
|
||||||
|
w={"100%"}
|
||||||
|
direction={"column"}
|
||||||
|
gap={"xs"}>
|
||||||
|
<ServiceWithPriceInput
|
||||||
|
serviceProps={{
|
||||||
|
...form.getInputProps("service"),
|
||||||
|
onChange: value => {
|
||||||
|
form.setFieldValue("service", value);
|
||||||
|
form.setFieldValue("serviceId", value.id);
|
||||||
|
},
|
||||||
|
label: "Услуга",
|
||||||
|
placeholder: "Выберите услугу",
|
||||||
|
disabled: innerProps.isEditing,
|
||||||
|
filterBy: item =>
|
||||||
|
!innerProps.excludeServiceIds.includes(item.id) ||
|
||||||
|
innerProps.isEditing,
|
||||||
|
}}
|
||||||
|
priceProps={{
|
||||||
|
...form.getInputProps("price"),
|
||||||
|
label: "Цена",
|
||||||
|
placeholder: "Введите цену",
|
||||||
|
}}
|
||||||
|
filterType={ServiceType.PRODUCT_SERVICE}
|
||||||
|
lockOnEdit={innerProps.isEditing}
|
||||||
|
quantity={innerProps.quantity}
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
{...form.getInputProps("isFixedPrice", {
|
||||||
|
type: "checkbox",
|
||||||
|
})}
|
||||||
|
label={"Зафиксировать цену"}
|
||||||
|
placeholder={"Зафиксировать цену"}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</BaseFormModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ProductServiceEditorModal;
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Flex } from "@mantine/core";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import { ContextModalProps } from "@mantine/modals";
|
||||||
|
import { ServicesKitSchema } from "@/lib/client";
|
||||||
|
import BaseFormModalActions from "@/modals/base/BaseFormModal/BaseFormModalActions";
|
||||||
|
import ServicesKitSelect from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/modals/ServicesKitSelectModal/components/ServicesKitSelect";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onSelect: (kit: ServicesKitSchema) => void;
|
||||||
|
serviceType: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ServicesKitForm = {
|
||||||
|
servicesKit: ServicesKitSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ServicesKitSelectModal = ({
|
||||||
|
context,
|
||||||
|
id,
|
||||||
|
innerProps,
|
||||||
|
}: ContextModalProps<Props>) => {
|
||||||
|
const form = useForm<ServicesKitForm>({
|
||||||
|
validate: {
|
||||||
|
servicesKit: servicesKit => !servicesKit && "Выберите сервис",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onClose = () => context.closeContextModal(id);
|
||||||
|
|
||||||
|
const onSubmit = (values: ServicesKitForm) => {
|
||||||
|
innerProps.onSelect(values.servicesKit);
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={form.onSubmit(onSubmit)}>
|
||||||
|
<Flex
|
||||||
|
gap={"xs"}
|
||||||
|
direction={"column"}>
|
||||||
|
<Flex>
|
||||||
|
<ServicesKitSelect
|
||||||
|
w={"100%"}
|
||||||
|
{...form.getInputProps("servicesKit")}
|
||||||
|
filterBy={item =>
|
||||||
|
item.serviceType === innerProps.serviceType
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<BaseFormModalActions onClose={onClose} />
|
||||||
|
</Flex>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServicesKitSelectModal;
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import ObjectSelect, {
|
||||||
|
ObjectSelectProps,
|
||||||
|
} from "@/components/selects/ObjectSelect/ObjectSelect";
|
||||||
|
import { ServicesKitSchema } from "@/lib/client";
|
||||||
|
import useServicesKitsList from "@/modules/dealModules/dealEditorTabs/FulfillmentBaseTab/hooks/lists/useServicesKitsList";
|
||||||
|
|
||||||
|
type Props = Omit<ObjectSelectProps<ServicesKitSchema>, "data">;
|
||||||
|
|
||||||
|
const ServicesKitSelect: FC<Props> = props => {
|
||||||
|
const { servicesKits } = useServicesKitsList();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ObjectSelect
|
||||||
|
label={"Набор услуг"}
|
||||||
|
placeholder={"Выберите набор услуг"}
|
||||||
|
data={servicesKits}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServicesKitSelect;
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
export enum ServiceType {
|
||||||
|
DEAL_SERVICE,
|
||||||
|
PRODUCT_SERVICE,
|
||||||
|
}
|
||||||
28
src/modules/modules.tsx
Normal file
28
src/modules/modules.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import {
|
||||||
|
IconBox,
|
||||||
|
} from "@tabler/icons-react";
|
||||||
|
import ModulesType from "./types";
|
||||||
|
import connectModules from "./connectModules";
|
||||||
|
|
||||||
|
export enum ModuleNames {
|
||||||
|
FULFILLMENT_BASE = "fulfillment_base",
|
||||||
|
}
|
||||||
|
|
||||||
|
const modules: ModulesType = {
|
||||||
|
[ModuleNames.FULFILLMENT_BASE]: {
|
||||||
|
renderInfo: {
|
||||||
|
label: "Фулфиллмент",
|
||||||
|
key: "fulfillment_base",
|
||||||
|
icon: <IconBox />,
|
||||||
|
},
|
||||||
|
modelData: {
|
||||||
|
id: 1,
|
||||||
|
key: "fulfillment_base",
|
||||||
|
label: "Фулфиллмент",
|
||||||
|
iconName: "IconBox",
|
||||||
|
description: "Создание товаров и услуг, их привязка к сделкам",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MODULES = connectModules(modules);
|
||||||
86
src/modules/modulesFileGen/modulesFileGen.ts
Normal file
86
src/modules/modulesFileGen/modulesFileGen.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
import axios, { AxiosResponse } from "axios";
|
||||||
|
import * as handlebars from "handlebars";
|
||||||
|
|
||||||
|
// region types
|
||||||
|
type Module = {
|
||||||
|
id: number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
iconName: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ModulesResponse = {
|
||||||
|
items: Module[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type Args = {
|
||||||
|
[key: string]: string | boolean;
|
||||||
|
};
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region utils
|
||||||
|
const getArgs = (): Args =>
|
||||||
|
process.argv.slice(2).reduce((args: Args, arg: string) => {
|
||||||
|
if (arg.startsWith("--")) {
|
||||||
|
// Handle long arguments like --port=8000
|
||||||
|
const [key, value] = arg.slice(2).split("=");
|
||||||
|
args[key] = value !== undefined ? value : true;
|
||||||
|
} else if (arg.startsWith("-") && arg.length > 1) {
|
||||||
|
// Handle short arguments like -p=8000
|
||||||
|
const [key, value] = arg.slice(1).split("=");
|
||||||
|
args[key] = value !== undefined ? value : true;
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}, {});
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
const kwargs = getArgs();
|
||||||
|
|
||||||
|
// region constants
|
||||||
|
const HOST = kwargs.host ?? kwargs.h ?? "127.0.0.1";
|
||||||
|
const PORT = kwargs.port ?? kwargs.p ?? "8000";
|
||||||
|
const ENDPOINT = `http://${HOST}:${PORT}/api/module/built-in/`;
|
||||||
|
|
||||||
|
const TEMPLATE_PATH = path.join(
|
||||||
|
__dirname,
|
||||||
|
"templates",
|
||||||
|
"modulesFileTemplate.hbs"
|
||||||
|
);
|
||||||
|
const OUTPUT_PATH = path.join(__dirname, "..", "modules.tsx");
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
const templateSource = fs.readFileSync(TEMPLATE_PATH, "utf8");
|
||||||
|
const template = handlebars.compile(templateSource);
|
||||||
|
|
||||||
|
handlebars.registerHelper("uppercase", text => {
|
||||||
|
return text.replace(/([a-z])([A-Z])/g, "$1_$2").toUpperCase();
|
||||||
|
});
|
||||||
|
|
||||||
|
const generateRows = (modules: Module[]) => {
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
modules,
|
||||||
|
};
|
||||||
|
const tsxContent = template(data);
|
||||||
|
fs.writeFileSync(OUTPUT_PATH, tsxContent);
|
||||||
|
console.log("File successfully generated.");
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const modulesFileGen = () => {
|
||||||
|
console.log("Start file generation...");
|
||||||
|
|
||||||
|
axios
|
||||||
|
.get(ENDPOINT)
|
||||||
|
.then((response: AxiosResponse<ModulesResponse>) => {
|
||||||
|
generateRows(response.data.items);
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
modulesFileGen();
|
||||||
34
src/modules/modulesFileGen/templates/modulesFileTemplate.hbs
Normal file
34
src/modules/modulesFileGen/templates/modulesFileTemplate.hbs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import {
|
||||||
|
{{#each modules}}
|
||||||
|
{{#if this.iconName}}{{this.iconName}},{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
} from "@tabler/icons-react";
|
||||||
|
import ModulesType from "./types";
|
||||||
|
import connectModules from "./connectModules";
|
||||||
|
|
||||||
|
export enum ModuleNames {
|
||||||
|
{{#each modules}}
|
||||||
|
{{uppercase this.key}} = "{{this.key}}",
|
||||||
|
{{/each}}
|
||||||
|
}
|
||||||
|
|
||||||
|
const modules: ModulesType = {
|
||||||
|
{{#each modules}}
|
||||||
|
[ModuleNames.{{uppercase this.key}}]: {
|
||||||
|
renderInfo: {
|
||||||
|
label: "{{this.label}}",
|
||||||
|
key: "{{this.key}}",
|
||||||
|
icon: {{#if this.iconName}}<{{this.iconName}} />{{else}}None{{/if}},
|
||||||
|
},
|
||||||
|
modelData: {
|
||||||
|
id: {{this.id}},
|
||||||
|
key: "{{this.key}}",
|
||||||
|
label: "{{this.label}}",
|
||||||
|
iconName: "{{this.iconName}}",
|
||||||
|
description: "{{this.description}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{{/each}}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MODULES = connectModules(modules);
|
||||||
18
src/modules/types.tsx
Normal file
18
src/modules/types.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
import { BuiltInModuleSchema } from "@/lib/client";
|
||||||
|
|
||||||
|
export type Module = {
|
||||||
|
renderInfo: {
|
||||||
|
label: string;
|
||||||
|
key: string;
|
||||||
|
icon: ReactNode;
|
||||||
|
};
|
||||||
|
modelData: BuiltInModuleSchema;
|
||||||
|
getTab?: (props: any) => ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ModulesType = {
|
||||||
|
[key: string]: Module;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModulesType;
|
||||||
8
src/modules/utils/isModuleInProject.ts
Normal file
8
src/modules/utils/isModuleInProject.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { ProjectSchema } from "../../client";
|
||||||
|
import { ModuleNames } from "../modules.tsx";
|
||||||
|
|
||||||
|
const isModuleInProject = (module: ModuleNames, project?: ProjectSchema | null) => {
|
||||||
|
return project?.modules.findIndex(m => m.key === module.toString()) !== -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isModuleInProject;
|
||||||
7
src/utils/baseFormInputProps.ts
Normal file
7
src/utils/baseFormInputProps.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
type BaseFormInputProps<T> = {
|
||||||
|
onChange: (value: T) => void;
|
||||||
|
value: T;
|
||||||
|
error?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BaseFormInputProps;
|
||||||
49
yarn.lock
49
yarn.lock
@ -2890,6 +2890,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@mantine/dropzone@npm:^8.3.1":
|
||||||
|
version: 8.3.1
|
||||||
|
resolution: "@mantine/dropzone@npm:8.3.1"
|
||||||
|
dependencies:
|
||||||
|
react-dropzone: "npm:14.3.8"
|
||||||
|
peerDependencies:
|
||||||
|
"@mantine/core": 8.3.1
|
||||||
|
"@mantine/hooks": 8.3.1
|
||||||
|
react: ^18.x || ^19.x
|
||||||
|
react-dom: ^18.x || ^19.x
|
||||||
|
checksum: 10c0/643c1224925b4575ea7fecef58c26c4bb520f097fb308464c099502ae444a22cc2abd52e96bd84513da9741a49e12ecec0f5a2147322a212c17de00659ec206a
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@mantine/form@npm:^8.1.3":
|
"@mantine/form@npm:^8.1.3":
|
||||||
version: 8.2.1
|
version: 8.2.1
|
||||||
resolution: "@mantine/form@npm:8.2.1"
|
resolution: "@mantine/form@npm:8.2.1"
|
||||||
@ -5050,6 +5064,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"attr-accept@npm:^2.2.4":
|
||||||
|
version: 2.2.5
|
||||||
|
resolution: "attr-accept@npm:2.2.5"
|
||||||
|
checksum: 10c0/9b4cb82213925cab2d568f71b3f1c7a7778f9192829aac39a281e5418cd00c04a88f873eb89f187e0bf786fa34f8d52936f178e62cbefb9254d57ecd88ada99b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"autoprefixer@npm:^10.4.21":
|
"autoprefixer@npm:^10.4.21":
|
||||||
version: 10.4.21
|
version: 10.4.21
|
||||||
resolution: "autoprefixer@npm:10.4.21"
|
resolution: "autoprefixer@npm:10.4.21"
|
||||||
@ -6108,6 +6129,7 @@ __metadata:
|
|||||||
"@ianvs/prettier-plugin-sort-imports": "npm:^4.4.2"
|
"@ianvs/prettier-plugin-sort-imports": "npm:^4.4.2"
|
||||||
"@mantine/core": "npm:8.1.2"
|
"@mantine/core": "npm:8.1.2"
|
||||||
"@mantine/dates": "npm:^8.2.7"
|
"@mantine/dates": "npm:^8.2.7"
|
||||||
|
"@mantine/dropzone": "npm:^8.3.1"
|
||||||
"@mantine/form": "npm:^8.1.3"
|
"@mantine/form": "npm:^8.1.3"
|
||||||
"@mantine/hooks": "npm:8.1.2"
|
"@mantine/hooks": "npm:8.1.2"
|
||||||
"@mantine/modals": "npm:^8.2.1"
|
"@mantine/modals": "npm:^8.2.1"
|
||||||
@ -6146,6 +6168,7 @@ __metadata:
|
|||||||
eslint-plugin-jsx-a11y: "npm:^6.10.2"
|
eslint-plugin-jsx-a11y: "npm:^6.10.2"
|
||||||
eslint-plugin-react: "npm:^7.37.5"
|
eslint-plugin-react: "npm:^7.37.5"
|
||||||
framer-motion: "npm:^12.23.7"
|
framer-motion: "npm:^12.23.7"
|
||||||
|
handlebars: "npm:^4.7.8"
|
||||||
i18n-iso-countries: "npm:^7.14.0"
|
i18n-iso-countries: "npm:^7.14.0"
|
||||||
jest: "npm:^30.0.0"
|
jest: "npm:^30.0.0"
|
||||||
jest-environment-jsdom: "npm:^30.0.0"
|
jest-environment-jsdom: "npm:^30.0.0"
|
||||||
@ -7554,6 +7577,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"file-selector@npm:^2.1.0":
|
||||||
|
version: 2.1.2
|
||||||
|
resolution: "file-selector@npm:2.1.2"
|
||||||
|
dependencies:
|
||||||
|
tslib: "npm:^2.7.0"
|
||||||
|
checksum: 10c0/fe827e0e95410aacfcc3eabc38c29cc36055257f03c1c06b631a2b5af9730c142ad2c52f5d64724d02231709617bda984701f52bd1f4b7aca50fb6585a27c1d2
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"filelist@npm:^1.0.4":
|
"filelist@npm:^1.0.4":
|
||||||
version: 1.0.4
|
version: 1.0.4
|
||||||
resolution: "filelist@npm:1.0.4"
|
resolution: "filelist@npm:1.0.4"
|
||||||
@ -8087,7 +8119,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"handlebars@npm:4.7.8":
|
"handlebars@npm:4.7.8, handlebars@npm:^4.7.8":
|
||||||
version: 4.7.8
|
version: 4.7.8
|
||||||
resolution: "handlebars@npm:4.7.8"
|
resolution: "handlebars@npm:4.7.8"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -11671,6 +11703,19 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"react-dropzone@npm:14.3.8":
|
||||||
|
version: 14.3.8
|
||||||
|
resolution: "react-dropzone@npm:14.3.8"
|
||||||
|
dependencies:
|
||||||
|
attr-accept: "npm:^2.2.4"
|
||||||
|
file-selector: "npm:^2.1.0"
|
||||||
|
prop-types: "npm:^15.8.1"
|
||||||
|
peerDependencies:
|
||||||
|
react: ">= 16.8 || 18.0.0"
|
||||||
|
checksum: 10c0/e17b1832783cda7b8824fe9370e99185d1abbdd5e4980b2985d6321c5768c8de18ff7b9ad550c809ee9743269dea608ff74d5208062754ce8377ad022897b278
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"react-imask@npm:^7.6.1":
|
"react-imask@npm:^7.6.1":
|
||||||
version: 7.6.1
|
version: 7.6.1
|
||||||
resolution: "react-imask@npm:7.6.1"
|
resolution: "react-imask@npm:7.6.1"
|
||||||
@ -13604,7 +13649,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.8.0":
|
"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.7.0, tslib@npm:^2.8.0":
|
||||||
version: 2.8.1
|
version: 2.8.1
|
||||||
resolution: "tslib@npm:2.8.1"
|
resolution: "tslib@npm:2.8.1"
|
||||||
checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62
|
checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62
|
||||||
|
|||||||
Reference in New Issue
Block a user