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

@@ -118,7 +118,7 @@ const DealSummaryCard: FC<Props> = ({ dealSummary }) => {
<Text <Text
c={"gray.6"} c={"gray.6"}
size={"sm"}> size={"sm"}>
Приемка: {(new Date(dealSummary.receivingSlotDate)).toLocaleDateString("ru-RU")} Слот: {(new Date(dealSummary.receivingSlotDate)).toLocaleDateString("ru-RU")}
</Text> </Text>
)} )}
</Flex> </Flex>

View File

@@ -1,290 +1,281 @@
import { Box, Drawer, rem, Tabs, Text } from "@mantine/core"; import { Box, Drawer, rem, Tabs } from "@mantine/core";
import { FC, useEffect, useRef } from "react"; import { FC, useEffect } from "react";
import DealServicesTable from "../../components/DealServicesTable/DealServicesTable.tsx";
import { useDealPageContext } from "../../contexts/DealPageContext.tsx"; import { useDealPageContext } from "../../contexts/DealPageContext.tsx";
import { import { IconBox, IconCalendarUser, IconSettings, IconUser } from "@tabler/icons-react";
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 DealStatusChangeTable from "../../components/DealStatusChangeTable/DealStatusChangeTable.tsx"; import DealStatusChangeTable from "../../components/DealStatusChangeTable/DealStatusChangeTable.tsx";
import DealEditDrawerGeneralTab from "./tabs/DealEditDrawerGeneralTab.tsx"; import DealEditDrawerGeneralTab from "./tabs/DealEditDrawerGeneralTab.tsx";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import ProductAndServiceTab from "../../tabs/ProductAndServiceTab/ProductAndServiceTab.tsx"; import ProductAndServiceTab from "../../tabs/ProductAndServiceTab/ProductAndServiceTab.tsx";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import ClientTab from "./tabs/ClientTab.tsx";
// import styles from './DealEditDrawer.module.css'; // import styles from './DealEditDrawer.module.css';
const useDealServicesTableState = () => { // const useDealServicesTableState = () => {
const { selectedDeal, setSelectedDeal } = useDealPageContext(); // const { selectedDeal, setSelectedDeal } = useDealPageContext();
const tableRef = useRef<BaseTableRef<DealServiceSchema>>(null); // 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) => { // const useDealProductTableState = () => {
if (!selectedDeal) return; // const { selectedDeal, setSelectedDeal } = useDealPageContext();
DealService.updateDealService({ //
requestBody: { // const onProductUpdate = (product: DealProductSchema) => {
dealId: selectedDeal.id, // if (!selectedDeal) return;
service, // DealService.updateDealProduct({
}, // requestBody: {
}).then(async ({ ok, message }) => { // dealId: selectedDeal.id,
if (!ok) { // product: product,
notifications.guess(ok, { message }); // },
return; // }).then(async ({ ok, message }) => {
} // notifications.guess(ok, { message });
await DealService.getDealById({ dealId: selectedDeal.id }).then( // if (!ok) return;
setSelectedDeal // await DealService.getDealById({ dealId: selectedDeal.id }).then(
); // setSelectedDeal,
}); // );
}; // });
const onServiceDelete = (service: DealServiceSchema) => { // };
if (!selectedDeal) return; // const onProductDelete = (product: DealProductSchema) => {
modals.openConfirmModal({ // if (!selectedDeal) return;
title: "Удаление услуги", // modals.openConfirmModal({
children: ( // title: "Удаление товара",
<> // children: (
<Text>Вы уверены, что хотите удалить услугу:</Text> // <>
<Text>{service.service.name}?</Text> // <Text>Вы уверены, что хотите удалить товар:</Text>
</> // <Text>{product.product.name}?</Text>
), // </>
onConfirm: () => { // ),
DealService.deleteDealService({ // onConfirm: () => {
requestBody: { // DealService.deleteDealProduct({
dealId: selectedDeal.id, // requestBody: {
serviceId: service.service.id, // dealId: selectedDeal.id,
}, // productId: product.product.id,
}).then(async ({ ok, message }) => { // },
if (!ok) { // }).then(async ({ ok, message }) => {
notifications.guess(ok, { message }); // if (!ok) {
return; // notifications.guess(ok, { message });
} // return;
await DealService.getDealById({ // }
dealId: selectedDeal.id, // await DealService.getDealById({
}).then(setSelectedDeal); // dealId: selectedDeal.id,
}); // }).then(setSelectedDeal);
}, // });
labels: { // },
cancel: "Отмена", // labels: {
confirm: "Удалить", // cancel: "Отмена",
}, // confirm: "Удалить",
}); // },
}; // });
const onServiceCreate = (service: DealServiceSchema) => { // };
if (!selectedDeal) return; // const onProductCreate = (product: DealProductSchema) => {
DealService.addDealService({ // if (!selectedDeal) return;
requestBody: { // DealService.addDealProduct({
dealId: selectedDeal.id, // requestBody: {
serviceId: service.service.id, // dealId: selectedDeal.id,
quantity: service.quantity, // product: product,
price: service.price, // },
}, // }).then(async ({ ok, message }) => {
}).then(async ({ ok, message }) => { // if (!ok) {
if (!ok) { // notifications.guess(ok, { message });
notifications.guess(ok, { message }); // return;
return; // }
} // await DealService.getDealById({ dealId: selectedDeal.id }).then(
await DealService.getDealById({ dealId: selectedDeal.id }).then( // setSelectedDeal,
setSelectedDeal // );
); // });
}); // };
}; // const onProductMultipleDelete = (items: DealProductSchema[]) => {
const onsServiceMultipleDelete = (items: DealServiceSchema[]) => { // if (!selectedDeal) return;
if (!selectedDeal) return; // modals.openConfirmModal({
modals.openConfirmModal({ // title: "Удаление товаров",
title: "Удаление услуг", // children: (
children: ( // <>
<> // <Text>
<Text> // Вы уверены, что хотите удалить выбранные товары?
Вы уверены, что хотите удалить выбранные услуги? // </Text>
</Text> // </>
</> // ),
), // onConfirm: () => {
onConfirm: () => { // DealService.deleteMultipleDealProducts({
DealService.deleteMultipleDealServices({ // requestBody: {
requestBody: { // dealId: selectedDeal.id,
dealId: selectedDeal.id, // productIds: items.map(item => item.product.id),
serviceIds: items.map(item => item.service.id), // },
}, // }).then(async ({ ok, message }) => {
}).then(async ({ ok, message }) => { // if (!ok) {
if (!ok) { // notifications.guess(ok, { message });
notifications.guess(ok, { message }); // return;
return; // }
} // await DealService.getDealById({
await DealService.getDealById({ // dealId: selectedDeal.id,
dealId: selectedDeal.id, // }).then(setSelectedDeal);
}).then(setSelectedDeal); // });
}); // },
}, // labels: {
labels: { // cancel: "Отмена",
cancel: "Отмена", // confirm: "Удалить",
confirm: "Удалить", // },
}, // });
}); // };
}; // return {
// clientId: selectedDeal?.clientId || -1,
return { // products: selectedDeal?.products || [],
onServiceUpdate, // onProductUpdate,
onServiceDelete, // onProductDelete,
onServiceCreate, // onProductCreate,
onsServiceMultipleDelete, // onProductMultipleDelete,
tableRef, // };
services: selectedDeal?.services || [], // };
}; // const DealEditDrawerProductsTable = () => {
}; // const {
const DealEditDrawerServicesTable = () => { // products,
const { // clientId,
services, // onProductUpdate,
tableRef, // onProductDelete,
onServiceCreate, // onProductCreate,
onServiceUpdate, // onProductMultipleDelete,
onServiceDelete, // } = useDealProductTableState();
onsServiceMultipleDelete, // return (
} = useDealServicesTableState(); // <DealProductsTable
// clientId={clientId}
return ( // items={products}
<DealServicesTable // onChange={onProductUpdate}
tableRef={tableRef} // onMultipleDelete={onProductMultipleDelete}
items={services} // onDelete={onProductDelete}
onChange={onServiceUpdate} // onCreate={onProductCreate}
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 useDealStatusChangeState = () => { const useDealStatusChangeState = () => {
const { selectedDeal } = useDealPageContext(); const { selectedDeal } = useDealPageContext();
@@ -292,6 +283,7 @@ const useDealStatusChangeState = () => {
statusHistory: selectedDeal?.statusHistory || [], statusHistory: selectedDeal?.statusHistory || [],
}; };
}; };
const DealEditDrawerStatusChangeTable = () => { const DealEditDrawerStatusChangeTable = () => {
const { statusHistory } = useDealStatusChangeState(); const { statusHistory } = useDealStatusChangeState();
@@ -341,6 +333,11 @@ const DealEditDrawer: FC = () => {
leftSection={<IconSettings />}> leftSection={<IconSettings />}>
Общее Общее
</Tabs.Tab> </Tabs.Tab>
<Tabs.Tab
value={"client"}
leftSection={<IconUser />}>
Клиент
</Tabs.Tab>
<Tabs.Tab <Tabs.Tab
value={"history"} value={"history"}
leftSection={<IconCalendarUser />}> leftSection={<IconCalendarUser />}>
@@ -365,6 +362,16 @@ const DealEditDrawer: FC = () => {
</Box> </Box>
</motion.div> </motion.div>
</Tabs.Panel> </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"}> <Tabs.Panel value={"history"}>
<motion.div <motion.div
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
@@ -385,16 +392,6 @@ const DealEditDrawer: FC = () => {
</Box> </Box>
</motion.div> </motion.div>
</Tabs.Panel> </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> </Tabs>
</Drawer> </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; deal: DealSchema;
}; };
type FormType = Omit<DealSchema, "statusHistory" | "services" | "products">; export type DealGeneralFormType = Omit<DealSchema, "statusHistory" | "services" | "products">;
const Content: FC<Props> = ({ deal }) => { const Content: FC<Props> = ({ deal }) => {
const { setSelectedDeal } = useDealPageContext(); const { setSelectedDeal } = useDealPageContext();
@@ -44,7 +44,7 @@ const Content: FC<Props> = ({ deal }) => {
// ignore typescript // ignore typescript
const initialValues: FormType = { const initialValues: DealGeneralFormType = {
...deal, ...deal,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
@@ -53,7 +53,7 @@ const Content: FC<Props> = ({ deal }) => {
// @ts-expect-error // @ts-expect-error
receivingSlotDate: deal.receivingSlotDate ? new Date(deal.receivingSlotDate) : null, receivingSlotDate: deal.receivingSlotDate ? new Date(deal.receivingSlotDate) : null,
}; };
const form = useForm<FormType>({ const form = useForm<DealGeneralFormType>({
initialValues: initialValues, initialValues: initialValues,
validate: { validate: {
name: (value: string) => 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({ return DealService.updateDealGeneralInfo({
requestBody: { requestBody: {
dealId: deal.id, 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({ return ClientService.updateClient({
requestBody: { requestBody: {
data: values.client, data: values.client,
}, },
}).then(({ ok, message }) => notifications.guess(ok, { message })); }).then(({ ok, message }) => notifications.guess(ok, { message }));
}; };
const handleSubmit = async (values: FormType) => { const handleSubmit = async (values: DealGeneralFormType) => {
// Updating client info if there changes // Updating client info if there changes
if (!isEqual(values.client, deal.client)) { if (!isEqual(values.client, deal.client)) {
await updateClientInfo(values); await updateClientInfo(values);