feat: client returned, slot is a slot

This commit is contained in:
2024-10-02 01:40:29 +03:00
parent f202e9b9fa
commit ec63a51109
4 changed files with 384 additions and 295 deletions

View File

@@ -1,290 +1,281 @@
import { Box, Drawer, rem, Tabs, Text } from "@mantine/core";
import { FC, useEffect, useRef } from "react";
import DealServicesTable from "../../components/DealServicesTable/DealServicesTable.tsx";
import { Box, Drawer, rem, Tabs } from "@mantine/core";
import { FC, useEffect } from "react";
import { useDealPageContext } from "../../contexts/DealPageContext.tsx";
import {
DealProductSchema,
DealService,
DealServiceSchema,
} from "../../../../client";
import { notifications } from "../../../../shared/lib/notifications.ts";
import { modals } from "@mantine/modals";
import { BaseTableRef } from "../../../../components/BaseTable/BaseTable.tsx";
import DealProductsTable from "../../components/DealProductsTable/DealProductsTable.tsx";
import { IconBox, IconCalendarUser, IconSettings } from "@tabler/icons-react";
import { IconBox, IconCalendarUser, IconSettings, IconUser } from "@tabler/icons-react";
import DealStatusChangeTable from "../../components/DealStatusChangeTable/DealStatusChangeTable.tsx";
import DealEditDrawerGeneralTab from "./tabs/DealEditDrawerGeneralTab.tsx";
import { useQueryClient } from "@tanstack/react-query";
import ProductAndServiceTab from "../../tabs/ProductAndServiceTab/ProductAndServiceTab.tsx";
import { motion } from "framer-motion";
import ClientTab from "./tabs/ClientTab.tsx";
// import styles from './DealEditDrawer.module.css';
const useDealServicesTableState = () => {
const { selectedDeal, setSelectedDeal } = useDealPageContext();
const tableRef = useRef<BaseTableRef<DealServiceSchema>>(null);
// const useDealServicesTableState = () => {
// const { selectedDeal, setSelectedDeal } = useDealPageContext();
// const tableRef = useRef<BaseTableRef<DealServiceSchema>>(null);
//
// const onServiceUpdate = (service: DealServiceSchema) => {
// if (!selectedDeal) return;
// DealService.updateDealService({
// requestBody: {
// dealId: selectedDeal.id,
// service,
// },
// }).then(async ({ ok, message }) => {
// if (!ok) {
// notifications.guess(ok, { message });
// return;
// }
// await DealService.getDealById({ dealId: selectedDeal.id }).then(
// setSelectedDeal,
// );
// });
// };
// const onServiceDelete = (service: DealServiceSchema) => {
// if (!selectedDeal) return;
// modals.openConfirmModal({
// title: "Удаление услуги",
// children: (
// <>
// <Text>Вы уверены, что хотите удалить услугу:</Text>
// <Text>{service.service.name}?</Text>
// </>
// ),
// onConfirm: () => {
// DealService.deleteDealService({
// requestBody: {
// dealId: selectedDeal.id,
// serviceId: service.service.id,
// },
// }).then(async ({ ok, message }) => {
// if (!ok) {
// notifications.guess(ok, { message });
// return;
// }
// await DealService.getDealById({
// dealId: selectedDeal.id,
// }).then(setSelectedDeal);
// });
// },
// labels: {
// cancel: "Отмена",
// confirm: "Удалить",
// },
// });
// };
// const onServiceCreate = (service: DealServiceSchema) => {
// if (!selectedDeal) return;
// DealService.addDealService({
// requestBody: {
// dealId: selectedDeal.id,
// serviceId: service.service.id,
// quantity: service.quantity,
// price: service.price,
// },
// }).then(async ({ ok, message }) => {
// if (!ok) {
// notifications.guess(ok, { message });
// return;
// }
// await DealService.getDealById({ dealId: selectedDeal.id }).then(
// setSelectedDeal,
// );
// });
// };
// const onsServiceMultipleDelete = (items: DealServiceSchema[]) => {
// if (!selectedDeal) return;
// modals.openConfirmModal({
// title: "Удаление услуг",
// children: (
// <>
// <Text>
// Вы уверены, что хотите удалить выбранные услуги?
// </Text>
// </>
// ),
// onConfirm: () => {
// DealService.deleteMultipleDealServices({
// requestBody: {
// dealId: selectedDeal.id,
// serviceIds: items.map(item => item.service.id),
// },
// }).then(async ({ ok, message }) => {
// if (!ok) {
// notifications.guess(ok, { message });
// return;
// }
// await DealService.getDealById({
// dealId: selectedDeal.id,
// }).then(setSelectedDeal);
// });
// },
// labels: {
// cancel: "Отмена",
// confirm: "Удалить",
// },
// });
// };
//
// return {
// onServiceUpdate,
// onServiceDelete,
// onServiceCreate,
// onsServiceMultipleDelete,
// tableRef,
// services: selectedDeal?.services || [],
// };
// };
// const DealEditDrawerServicesTable = () => {
// const {
// services,
// tableRef,
// onServiceCreate,
// onServiceUpdate,
// onServiceDelete,
// onsServiceMultipleDelete,
// } = useDealServicesTableState();
//
// return (
// <DealServicesTable
// tableRef={tableRef}
// items={services}
// onChange={onServiceUpdate}
// onDelete={onServiceDelete}
// onCreate={onServiceCreate}
// onMultipleDelete={onsServiceMultipleDelete}
// />
// );
// };
const onServiceUpdate = (service: DealServiceSchema) => {
if (!selectedDeal) return;
DealService.updateDealService({
requestBody: {
dealId: selectedDeal.id,
service,
},
}).then(async ({ ok, message }) => {
if (!ok) {
notifications.guess(ok, { message });
return;
}
await DealService.getDealById({ dealId: selectedDeal.id }).then(
setSelectedDeal
);
});
};
const onServiceDelete = (service: DealServiceSchema) => {
if (!selectedDeal) return;
modals.openConfirmModal({
title: "Удаление услуги",
children: (
<>
<Text>Вы уверены, что хотите удалить услугу:</Text>
<Text>{service.service.name}?</Text>
</>
),
onConfirm: () => {
DealService.deleteDealService({
requestBody: {
dealId: selectedDeal.id,
serviceId: service.service.id,
},
}).then(async ({ ok, message }) => {
if (!ok) {
notifications.guess(ok, { message });
return;
}
await DealService.getDealById({
dealId: selectedDeal.id,
}).then(setSelectedDeal);
});
},
labels: {
cancel: "Отмена",
confirm: "Удалить",
},
});
};
const onServiceCreate = (service: DealServiceSchema) => {
if (!selectedDeal) return;
DealService.addDealService({
requestBody: {
dealId: selectedDeal.id,
serviceId: service.service.id,
quantity: service.quantity,
price: service.price,
},
}).then(async ({ ok, message }) => {
if (!ok) {
notifications.guess(ok, { message });
return;
}
await DealService.getDealById({ dealId: selectedDeal.id }).then(
setSelectedDeal
);
});
};
const onsServiceMultipleDelete = (items: DealServiceSchema[]) => {
if (!selectedDeal) return;
modals.openConfirmModal({
title: "Удаление услуг",
children: (
<>
<Text>
Вы уверены, что хотите удалить выбранные услуги?
</Text>
</>
),
onConfirm: () => {
DealService.deleteMultipleDealServices({
requestBody: {
dealId: selectedDeal.id,
serviceIds: items.map(item => item.service.id),
},
}).then(async ({ ok, message }) => {
if (!ok) {
notifications.guess(ok, { message });
return;
}
await DealService.getDealById({
dealId: selectedDeal.id,
}).then(setSelectedDeal);
});
},
labels: {
cancel: "Отмена",
confirm: "Удалить",
},
});
};
return {
onServiceUpdate,
onServiceDelete,
onServiceCreate,
onsServiceMultipleDelete,
tableRef,
services: selectedDeal?.services || [],
};
};
const DealEditDrawerServicesTable = () => {
const {
services,
tableRef,
onServiceCreate,
onServiceUpdate,
onServiceDelete,
onsServiceMultipleDelete,
} = useDealServicesTableState();
return (
<DealServicesTable
tableRef={tableRef}
items={services}
onChange={onServiceUpdate}
onDelete={onServiceDelete}
onCreate={onServiceCreate}
onMultipleDelete={onsServiceMultipleDelete}
/>
);
};
const useDealProductTableState = () => {
const { selectedDeal, setSelectedDeal } = useDealPageContext();
const onProductUpdate = (product: DealProductSchema) => {
if (!selectedDeal) return;
DealService.updateDealProduct({
requestBody: {
dealId: selectedDeal.id,
product: product,
},
}).then(async ({ ok, message }) => {
notifications.guess(ok, { message });
if (!ok) return;
await DealService.getDealById({ dealId: selectedDeal.id }).then(
setSelectedDeal
);
});
};
const onProductDelete = (product: DealProductSchema) => {
if (!selectedDeal) return;
modals.openConfirmModal({
title: "Удаление товара",
children: (
<>
<Text>Вы уверены, что хотите удалить товар:</Text>
<Text>{product.product.name}?</Text>
</>
),
onConfirm: () => {
DealService.deleteDealProduct({
requestBody: {
dealId: selectedDeal.id,
productId: product.product.id,
},
}).then(async ({ ok, message }) => {
if (!ok) {
notifications.guess(ok, { message });
return;
}
await DealService.getDealById({
dealId: selectedDeal.id,
}).then(setSelectedDeal);
});
},
labels: {
cancel: "Отмена",
confirm: "Удалить",
},
});
};
const onProductCreate = (product: DealProductSchema) => {
if (!selectedDeal) return;
DealService.addDealProduct({
requestBody: {
dealId: selectedDeal.id,
product: product,
},
}).then(async ({ ok, message }) => {
if (!ok) {
notifications.guess(ok, { message });
return;
}
await DealService.getDealById({ dealId: selectedDeal.id }).then(
setSelectedDeal
);
});
};
const onProductMultipleDelete = (items: DealProductSchema[]) => {
if (!selectedDeal) return;
modals.openConfirmModal({
title: "Удаление товаров",
children: (
<>
<Text>
Вы уверены, что хотите удалить выбранные товары?
</Text>
</>
),
onConfirm: () => {
DealService.deleteMultipleDealProducts({
requestBody: {
dealId: selectedDeal.id,
productIds: items.map(item => item.product.id),
},
}).then(async ({ ok, message }) => {
if (!ok) {
notifications.guess(ok, { message });
return;
}
await DealService.getDealById({
dealId: selectedDeal.id,
}).then(setSelectedDeal);
});
},
labels: {
cancel: "Отмена",
confirm: "Удалить",
},
});
};
return {
clientId: selectedDeal?.clientId || -1,
products: selectedDeal?.products || [],
onProductUpdate,
onProductDelete,
onProductCreate,
onProductMultipleDelete,
};
};
const DealEditDrawerProductsTable = () => {
const {
products,
clientId,
onProductUpdate,
onProductDelete,
onProductCreate,
onProductMultipleDelete,
} = useDealProductTableState();
return (
<DealProductsTable
clientId={clientId}
items={products}
onChange={onProductUpdate}
onMultipleDelete={onProductMultipleDelete}
onDelete={onProductDelete}
onCreate={onProductCreate}
/>
);
};
// const useDealProductTableState = () => {
// const { selectedDeal, setSelectedDeal } = useDealPageContext();
//
// const onProductUpdate = (product: DealProductSchema) => {
// if (!selectedDeal) return;
// DealService.updateDealProduct({
// requestBody: {
// dealId: selectedDeal.id,
// product: product,
// },
// }).then(async ({ ok, message }) => {
// notifications.guess(ok, { message });
// if (!ok) return;
// await DealService.getDealById({ dealId: selectedDeal.id }).then(
// setSelectedDeal,
// );
// });
// };
// const onProductDelete = (product: DealProductSchema) => {
// if (!selectedDeal) return;
// modals.openConfirmModal({
// title: "Удаление товара",
// children: (
// <>
// <Text>Вы уверены, что хотите удалить товар:</Text>
// <Text>{product.product.name}?</Text>
// </>
// ),
// onConfirm: () => {
// DealService.deleteDealProduct({
// requestBody: {
// dealId: selectedDeal.id,
// productId: product.product.id,
// },
// }).then(async ({ ok, message }) => {
// if (!ok) {
// notifications.guess(ok, { message });
// return;
// }
// await DealService.getDealById({
// dealId: selectedDeal.id,
// }).then(setSelectedDeal);
// });
// },
// labels: {
// cancel: "Отмена",
// confirm: "Удалить",
// },
// });
// };
// const onProductCreate = (product: DealProductSchema) => {
// if (!selectedDeal) return;
// DealService.addDealProduct({
// requestBody: {
// dealId: selectedDeal.id,
// product: product,
// },
// }).then(async ({ ok, message }) => {
// if (!ok) {
// notifications.guess(ok, { message });
// return;
// }
// await DealService.getDealById({ dealId: selectedDeal.id }).then(
// setSelectedDeal,
// );
// });
// };
// const onProductMultipleDelete = (items: DealProductSchema[]) => {
// if (!selectedDeal) return;
// modals.openConfirmModal({
// title: "Удаление товаров",
// children: (
// <>
// <Text>
// Вы уверены, что хотите удалить выбранные товары?
// </Text>
// </>
// ),
// onConfirm: () => {
// DealService.deleteMultipleDealProducts({
// requestBody: {
// dealId: selectedDeal.id,
// productIds: items.map(item => item.product.id),
// },
// }).then(async ({ ok, message }) => {
// if (!ok) {
// notifications.guess(ok, { message });
// return;
// }
// await DealService.getDealById({
// dealId: selectedDeal.id,
// }).then(setSelectedDeal);
// });
// },
// labels: {
// cancel: "Отмена",
// confirm: "Удалить",
// },
// });
// };
// return {
// clientId: selectedDeal?.clientId || -1,
// products: selectedDeal?.products || [],
// onProductUpdate,
// onProductDelete,
// onProductCreate,
// onProductMultipleDelete,
// };
// };
// const DealEditDrawerProductsTable = () => {
// const {
// products,
// clientId,
// onProductUpdate,
// onProductDelete,
// onProductCreate,
// onProductMultipleDelete,
// } = useDealProductTableState();
// return (
// <DealProductsTable
// clientId={clientId}
// items={products}
// onChange={onProductUpdate}
// onMultipleDelete={onProductMultipleDelete}
// onDelete={onProductDelete}
// onCreate={onProductCreate}
// />
// );
// };
const useDealStatusChangeState = () => {
const { selectedDeal } = useDealPageContext();
@@ -292,6 +283,7 @@ const useDealStatusChangeState = () => {
statusHistory: selectedDeal?.statusHistory || [],
};
};
const DealEditDrawerStatusChangeTable = () => {
const { statusHistory } = useDealStatusChangeState();
@@ -341,6 +333,11 @@ const DealEditDrawer: FC = () => {
leftSection={<IconSettings />}>
Общее
</Tabs.Tab>
<Tabs.Tab
value={"client"}
leftSection={<IconUser />}>
Клиент
</Tabs.Tab>
<Tabs.Tab
value={"history"}
leftSection={<IconCalendarUser />}>
@@ -365,6 +362,16 @@ const DealEditDrawer: FC = () => {
</Box>
</motion.div>
</Tabs.Panel>
<Tabs.Panel value={"client"}>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}>
<Box p={rem(10)}>
<ClientTab />
</Box>
</motion.div>
</Tabs.Panel>
<Tabs.Panel value={"history"}>
<motion.div
initial={{ opacity: 0 }}
@@ -385,16 +392,6 @@ const DealEditDrawer: FC = () => {
</Box>
</motion.div>
</Tabs.Panel>
<Tabs.Panel value={"services"}>
<Box p={rem(10)}>
<DealEditDrawerServicesTable />
</Box>
</Tabs.Panel>
<Tabs.Panel value={"products"}>
<Box p={rem(10)}>
<DealEditDrawerProductsTable />
</Box>
</Tabs.Panel>
</Tabs>
</Drawer>
);

View File

@@ -0,0 +1,92 @@
import { Button, Fieldset, Flex, rem, TextInput } from "@mantine/core";
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
import { useForm } from "@mantine/form";
import { DealGeneralFormType } from "./DealEditDrawerGeneralTab.tsx";
import { ClientService, DealSchema, DealService } from "../../../../../client";
import { isEqual } from "lodash";
import { notifications } from "../../../../../shared/lib/notifications.ts";
import { useQueryClient } from "@tanstack/react-query";
const ClientTab = () => {
const { selectedDeal: deal, setSelectedDeal } = useDealPageContext();
const initialValues: DealGeneralFormType = deal as DealSchema;
const queryClient = useQueryClient();
const form = useForm<DealGeneralFormType>(
{
initialValues: initialValues,
validate: {
name: (value: string) => value.length > 0 ? null : "Название сделки не может быть пустым",
},
},
);
const hasChanges = !isEqual(form.values, initialValues);
const updateClientInfo = async (values: DealGeneralFormType) => {
return ClientService.updateClient({
requestBody: {
data: values.client,
},
}).then(({ ok, message }) => notifications.guess(ok, { message }));
};
const update = async () => {
return DealService.getDealById({ dealId: form.values.id }).then(data => {
setSelectedDeal(data);
form.setInitialValues(data);
queryClient.invalidateQueries({
queryKey: ["getDealSummaries"],
});
});
};
const handleSave = () => {
updateClientInfo(form.values).then(async () => {
await update();
});
};
const handleCancel = () => {
form.setInitialValues(initialValues);
};
return (
<Flex direction={"column"} flex={1} gap={rem(10)}>
<Flex flex={1}>
<Fieldset legend={"Клиент"} flex={1}>
<TextInput
disabled
placeholder={"Название"}
label={"Название"}
value={deal?.client.name}
/>
<TextInput
placeholder={"Введите телефон"}
label={"Телефон клиента"}
{...form.getInputProps("client.details.phoneNumber")}
/>
<TextInput
placeholder={"Введите email"}
label={"Email"}
{...form.getInputProps("client.details.email")}
/>
<TextInput
placeholder={"Введите телеграм"}
label={"Телеграм"}
{...form.getInputProps("client.details.telegram")}
/>
<TextInput
placeholder={"Введите ИНН"}
label={"ИНН"}
{...form.getInputProps("client.details.inn")}
/>
</Fieldset>
</Flex>
<Flex
gap={rem(10)}
justify={"flex-end"}
display={!hasChanges ? "none" : "flex"}
>
<Button onClick={handleCancel} variant={"default"}>Отмена</Button>
<Button onClick={handleSave} variant={"default"}>Сохранить</Button>
</Flex>
</Flex>
);
};
export default ClientTab;

View File

@@ -35,7 +35,7 @@ type Props = {
deal: DealSchema;
};
type FormType = Omit<DealSchema, "statusHistory" | "services" | "products">;
export type DealGeneralFormType = Omit<DealSchema, "statusHistory" | "services" | "products">;
const Content: FC<Props> = ({ deal }) => {
const { setSelectedDeal } = useDealPageContext();
@@ -44,7 +44,7 @@ const Content: FC<Props> = ({ deal }) => {
// ignore typescript
const initialValues: FormType = {
const initialValues: DealGeneralFormType = {
...deal,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
@@ -53,7 +53,7 @@ const Content: FC<Props> = ({ deal }) => {
// @ts-expect-error
receivingSlotDate: deal.receivingSlotDate ? new Date(deal.receivingSlotDate) : null,
};
const form = useForm<FormType>({
const form = useForm<DealGeneralFormType>({
initialValues: initialValues,
validate: {
name: (value: string) =>
@@ -62,7 +62,7 @@ const Content: FC<Props> = ({ deal }) => {
: "Название сделки не может быть пустым",
},
});
const updateDealInfo = async (values: FormType) => {
const updateDealInfo = async (values: DealGeneralFormType) => {
return DealService.updateDealGeneralInfo({
requestBody: {
dealId: deal.id,
@@ -83,14 +83,14 @@ const Content: FC<Props> = ({ deal }) => {
});
});
};
const updateClientInfo = async (values: FormType) => {
const updateClientInfo = async (values: DealGeneralFormType) => {
return ClientService.updateClient({
requestBody: {
data: values.client,
},
}).then(({ ok, message }) => notifications.guess(ok, { message }));
};
const handleSubmit = async (values: FormType) => {
const handleSubmit = async (values: DealGeneralFormType) => {
// Updating client info if there changes
if (!isEqual(values.client, deal.client)) {
await updateClientInfo(values);