feat: modules, products, services, services kits

This commit is contained in:
2025-09-16 10:56:10 +04:00
parent f2746b8b65
commit 553e76d610
92 changed files with 8404 additions and 103 deletions

View File

@ -3,11 +3,13 @@ import { IconMoodSad } from "@tabler/icons-react";
import { Group, Pagination, Stack, Text } from "@mantine/core";
import useDealsTableColumns from "@/app/deals/components/desktop/DealsTable/useDealsTableColumns";
import { useDealsContext } from "@/app/deals/contexts/DealsContext";
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
import BaseTable from "@/components/ui/BaseTable/BaseTable";
import { useDrawersContext } from "@/drawers/DrawersContext";
import { DealSchema } from "@/lib/client";
const DealsTable: FC = () => {
const { selectedProject } = useProjectsContext();
const { deals, paginationInfo, page, setPage, sortingForm, dealsCrud } =
useDealsContext();
const { openDrawer } = useDrawersContext();
@ -20,6 +22,7 @@ const DealsTable: FC = () => {
value: deal,
onChange: deal => dealsCrud.onUpdate(deal.id, deal),
onDelete: dealsCrud.onDelete,
project: selectedProject,
},
});
},

View File

@ -35,7 +35,7 @@ const TopToolPanel: FC<Props> = ({ view, setView }) => {
title: "Создание проекта",
withCloseButton: true,
innerProps: {
onChange: values => projectsCrud.onCreate(values.name),
onChange: projectsCrud.onCreate,
},
});
};

View File

@ -11,7 +11,7 @@ const CreateBoardButton = () => {
<Flex style={{ borderBottom: "2px solid gray" }}>
<InPlaceInput
placeholder={"Название доски"}
onChange={boardsCrud.onCreate}
onChange={name => boardsCrud.onCreate({ name })}
getChildren={startEditing => (
<Box
onClick={startEditing}

View File

@ -11,7 +11,7 @@ const CreateCardButton = () => {
const { dealsCrud } = useDealsContext();
const onSubmit = (values: CreateDealForm) => {
dealsCrud.onCreate(values.name);
dealsCrud.onCreate(values);
setIsCreating(prevState => !prevState);
setIsTransitionEnded(false);
};

View File

@ -17,7 +17,7 @@ const CreateStatusButton = () => {
className={styles["inner-container"]}>
<InPlaceInput
placeholder={"Название колонки"}
onChange={statusesCrud.onCreate}
onChange={name => statusesCrud.onCreate({ name })}
getChildren={startEditing => (
<Center
p={"sm"}

View File

@ -1,5 +1,6 @@
import { Box, Card, Group, Pill, Stack, Text } from "@mantine/core";
import { useDealsContext } from "@/app/deals/contexts/DealsContext";
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
import { useDrawersContext } from "@/drawers/DrawersContext";
import { DealSchema } from "@/lib/client";
import styles from "./DealCard.module.css";
@ -9,6 +10,7 @@ type Props = {
};
const DealCard = ({ deal }: Props) => {
const { selectedProject } = useProjectsContext();
const { dealsCrud } = useDealsContext();
const { openDrawer } = useDrawersContext();
@ -19,6 +21,7 @@ const DealCard = ({ deal }: Props) => {
value: deal,
onChange: deal => dealsCrud.onUpdate(deal.id, deal),
onDelete: dealsCrud.onDelete,
project: selectedProject,
},
});
};

View File

@ -2,9 +2,10 @@ import { FC } from "react";
import { IconPlus } from "@tabler/icons-react";
import { Box, Group, Text } from "@mantine/core";
import { modals } from "@mantine/modals";
import { CreateBoardSchema } from "@/lib/client";
type Props = {
onCreateBoard: (name: string) => void;
onCreateBoard: (data: Partial<CreateBoardSchema>) => void;
};
const CreateBoardButton: FC<Props> = ({ onCreateBoard }) => {
@ -14,7 +15,7 @@ const CreateBoardButton: FC<Props> = ({ onCreateBoard }) => {
title: "Создание доски",
withCloseButton: true,
innerProps: {
onChange: values => onCreateBoard(values.name),
onChange: values => onCreateBoard(values),
},
});
};

View File

@ -6,12 +6,13 @@ import DealEditorBody from "@/app/deals/drawers/DealEditorDrawer/components/Deal
import Header from "@/app/deals/drawers/DealEditorDrawer/components/Header";
import { DrawerProps } from "@/drawers/types";
import useIsMobile from "@/hooks/utils/useIsMobile";
import { DealSchema } from "@/lib/client";
import { DealSchema, ProjectSchema } from "@/lib/client";
type Props = {
value: DealSchema;
onChange: (deal: DealSchema) => void;
onDelete: (deal: DealSchema, onSuccess: () => void) => void;
project: ProjectSchema | null;
};
const DealEditorDrawer: FC<DrawerProps<Props>> = ({
@ -24,7 +25,7 @@ const DealEditorDrawer: FC<DrawerProps<Props>> = ({
return (
<Drawer
size={isMobile ? "100%" : "40%"}
size={isMobile ? "100%" : "60%"}
position={"right"}
onClose={onClose}
removeScrollProps={{ allowPinchZoom: true }}
@ -49,6 +50,7 @@ const DealEditorDrawer: FC<DrawerProps<Props>> = ({
props.onChange(deal);
}}
onDelete={value => props.onDelete(value, onClose)}
project={props.project}
/>
</Drawer>
);

View File

@ -1,17 +1,55 @@
import React, { FC } from "react";
import { IconCircleDotted, IconEdit } from "@tabler/icons-react";
import { Tabs, Text } from "@mantine/core";
import React, { FC, ReactNode } from "react";
import { IconEdit } from "@tabler/icons-react";
import { motion } from "framer-motion";
import { Box, Tabs } from "@mantine/core";
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";
type Props = {
value: DealSchema;
onChange: (deal: DealSchema) => void;
onDelete: (deal: DealSchema) => void;
project: ProjectSchema | null;
};
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 (
<Tabs
defaultValue="general"
@ -22,19 +60,11 @@ const DealEditorBody: FC<Props> = props => {
leftSection={<IconEdit />}>
Общая информация
</Tabs.Tab>
<Tabs.Tab
value="mock"
leftSection={<IconCircleDotted />}>
Mock
</Tabs.Tab>
{getModuleTabs()}
</Tabs.List>
<Tabs.Panel value="general">
<GeneralTab {...props} />
</Tabs.Panel>
<Tabs.Panel value="mock">
<Text>mock</Text>
</Tabs.Panel>
{getTabPanel("general", <GeneralTab {...props} />)}
{getModuleTabPanels()}
</Tabs>
);
};

View File

@ -1,7 +1,7 @@
import React, { FC } from "react";
import { Stack, Text, TextInput } from "@mantine/core";
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 StatusSelect from "@/components/selects/StatusSelect/StatusSelect";
import { BoardSchema, DealSchema, StatusSchema } from "@/lib/client";

View File

@ -1,7 +1,10 @@
import { FC } from "react";
import { IconEdit } from "@tabler/icons-react";
import { IconBlocks, IconEdit } from "@tabler/icons-react";
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 styles from "../ProjectEditorDrawer.module.css";
@ -22,10 +25,18 @@ const ProjectEditorBody: FC<Props> = props => {
leftSection={<IconEdit />}>
Общая информация
</Tabs.Tab>
<Tabs.Tab
value="modules"
leftSection={<IconBlocks />}>
Модули
</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="general">
<GeneralTab {...props} />
</Tabs.Panel>
<Tabs.Panel value="modules">
<ModulesTab {...props} />
</Tabs.Panel>
</Tabs>
);
};

View File

@ -1,7 +1,7 @@
import { FC } from "react";
import { Stack, TextInput } from "@mantine/core";
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";
type Props = {
@ -10,7 +10,7 @@ type Props = {
onDelete: (value: ProjectSchema) => void;
};
const GeneralTab: FC<Props> = ({ value, onChange, onDelete }) => {
export const GeneralTab: FC<Props> = ({ value, onChange, onDelete }) => {
const form = useForm<ProjectSchema>({
initialValues: value,
validate: {
@ -38,5 +38,3 @@ const GeneralTab: FC<Props> = ({ value, onChange, onDelete }) => {
</form>
);
};
export default GeneralTab;

View File

@ -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>
);
};

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,2 @@
export { GeneralTab } from "./GeneralTab/GeneralTab";
export { ModulesTab } from "./ModulesTab/ModulesTab";

View File

@ -13,7 +13,7 @@ const CreateProjectButton: FC = () => {
title: "Создание проекта",
withCloseButton: true,
innerProps: {
onChange: values => projectsCrud.onCreate(values.name),
onChange: projectsCrud.onCreate,
},
});
};

View File

@ -13,7 +13,7 @@ const CreateStatusButton: FC = () => {
title: "Создание колонки",
withCloseButton: true,
innerProps: {
onChange: values => statusesCrud.onCreate(values.name),
onChange: statusesCrud.onCreate,
},
});
};