273 lines
12 KiB
TypeScript
273 lines
12 KiB
TypeScript
import { FC } from "react";
|
||
import { useDealPageContext } from "../../../contexts/DealPageContext.tsx";
|
||
import {
|
||
ActionIcon,
|
||
Button,
|
||
Checkbox,
|
||
Divider,
|
||
Fieldset,
|
||
Flex,
|
||
Group,
|
||
rem,
|
||
Textarea,
|
||
TextInput,
|
||
Tooltip,
|
||
} from "@mantine/core";
|
||
import { useForm } from "@mantine/form";
|
||
import { ClientService, DealSchema, DealService, ShippingWarehouseSchema } from "../../../../../client";
|
||
import { DealStatus, DealStatusDictionary } from "../../../../../shared/enums/DealStatus.ts";
|
||
import { isEqual } from "lodash";
|
||
import { notifications } from "../../../../../shared/lib/notifications.ts";
|
||
import { useQueryClient } from "@tanstack/react-query";
|
||
import ShippingWarehouseAutocomplete
|
||
from "../../../../../components/Selects/ShippingWarehouseAutocomplete/ShippingWarehouseAutocomplete.tsx";
|
||
import { ButtonCopyControlled } from "../../../../../components/ButtonCopyControlled/ButtonCopyControlled.tsx";
|
||
import { useClipboard } from "@mantine/hooks";
|
||
import ButtonCopy from "../../../../../components/ButtonCopy/ButtonCopy.tsx";
|
||
import FileSaver from "file-saver";
|
||
import { getCurrentDateTimeForFilename } from "../../../../../shared/lib/date.ts";
|
||
import { IconPrinter } from "@tabler/icons-react";
|
||
|
||
type Props = {
|
||
deal: DealSchema
|
||
}
|
||
|
||
type FormType = Omit<DealSchema, "statusHistory" | "services" | "products">
|
||
|
||
const Content: FC<Props> = ({ deal }) => {
|
||
const { setSelectedDeal } = useDealPageContext();
|
||
const clipboard = useClipboard();
|
||
const queryClient = useQueryClient();
|
||
|
||
const initialValues: FormType = deal;
|
||
const form = useForm<FormType>(
|
||
{
|
||
initialValues: initialValues,
|
||
validate: {
|
||
name: (value: string) => value.length > 0 ? null : "Название сделки не может быть пустым",
|
||
},
|
||
},
|
||
);
|
||
const updateDealInfo = async (values: FormType) => {
|
||
return DealService.updateDealGeneralInfo({
|
||
requestBody: {
|
||
dealId: deal.id,
|
||
data: { ...values, shippingWarehouse: values.shippingWarehouse?.toString() },
|
||
},
|
||
}).then(({ ok, message }) => {
|
||
notifications.guess(ok, { message });
|
||
if (!ok) return;
|
||
DealService.getDealById({ dealId: deal.id })
|
||
.then((data) => {
|
||
setSelectedDeal(data);
|
||
form.setInitialValues(data);
|
||
queryClient.invalidateQueries({
|
||
queryKey: ["getDealSummaries"],
|
||
});
|
||
});
|
||
});
|
||
};
|
||
const updateClientInfo = async (values: FormType) => {
|
||
return ClientService.updateClient({
|
||
requestBody: {
|
||
data: values.client,
|
||
},
|
||
}).then(({ ok, message }) =>
|
||
notifications.guess(ok, { message }));
|
||
};
|
||
const handleSubmit = async (values: FormType) => {
|
||
// Updating client info if there changes
|
||
if (!isEqual(values.client, deal.client)) {
|
||
await updateClientInfo(values);
|
||
}
|
||
// updating deal info
|
||
await updateDealInfo(values);
|
||
};
|
||
const isShippingWarehouse = (value: (ShippingWarehouseSchema | string | null | undefined)): value is ShippingWarehouseSchema => {
|
||
return !["string", "null", "undefined"].includes((typeof value));
|
||
|
||
};
|
||
|
||
const onCopyGuestUrlClick = () => {
|
||
DealService.createDealGuestUrl({
|
||
requestBody: {
|
||
dealId: deal.id,
|
||
},
|
||
}).then(({ ok, message, url }) => {
|
||
if (!ok)
|
||
notifications.guess(ok, { message });
|
||
clipboard.copy(
|
||
`${window.location.origin}/${url}`,
|
||
);
|
||
});
|
||
};
|
||
return (
|
||
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
|
||
<Flex direction={"column"}>
|
||
<Fieldset legend={`Общие параметры [ID: ${deal.id}]`}>
|
||
<Flex direction={"column"} gap={rem(10)}>
|
||
<TextInput
|
||
placeholder={"Название сделки"}
|
||
label={"Название сделки"}
|
||
{...form.getInputProps("name")}
|
||
/>
|
||
<TextInput
|
||
disabled
|
||
placeholder={"Дата создания"}
|
||
label={"Дата создания"}
|
||
value={new Date(deal.createdAt).toLocaleString("ru-RU")}
|
||
/>
|
||
<TextInput
|
||
disabled
|
||
placeholder={"Текущий статус"}
|
||
label={"Текущий статус"}
|
||
value={DealStatusDictionary[deal.currentStatus as DealStatus]} />
|
||
<Textarea
|
||
label={"Коментарий к сделке"}
|
||
placeholder={"Введите коментарий к сделке"}
|
||
{...form.getInputProps("comment")}
|
||
/>
|
||
<ShippingWarehouseAutocomplete
|
||
placeholder={"Введите склад отгрузки"}
|
||
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);
|
||
}}
|
||
/>
|
||
</Flex>
|
||
</Fieldset>
|
||
<Fieldset legend={"Клиент"}>
|
||
<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 mt={"md"} gap={rem(10)} align={"center"} justify={"flex-end"}>
|
||
<Flex align={"center"} gap={rem(10)} justify={"center"}>
|
||
|
||
<Flex gap={rem(10)} align={"center"} justify={"space-between"}>
|
||
<Tooltip
|
||
label={"Распечатать сделку"}
|
||
>
|
||
<ActionIcon
|
||
onClick={() => {
|
||
const pdfWindow = window.open(`${import.meta.env.VITE_API_URL}/deal/detailedDocument/${deal.id}`);
|
||
if (!pdfWindow) return;
|
||
pdfWindow.print();
|
||
}}
|
||
variant={"default"}>
|
||
<IconPrinter />
|
||
</ActionIcon>
|
||
</Tooltip>
|
||
|
||
<Flex gap={rem(10)}>
|
||
|
||
{(deal.billRequest && deal.billRequest.pdfUrl) ?
|
||
<ButtonCopy
|
||
onCopiedLabel={"Ссылка скопирована в буфер обмена"}
|
||
value={deal.billRequest.pdfUrl}
|
||
>
|
||
Ссылка на оплату
|
||
</ButtonCopy>
|
||
:
|
||
<ButtonCopyControlled
|
||
onCopyClick={() => {
|
||
// get current datetime for filename, replaced dots with _
|
||
const date = getCurrentDateTimeForFilename();
|
||
|
||
|
||
FileSaver.saveAs(`${import.meta.env.VITE_API_URL}/deal/document/${deal.id}`,
|
||
`bill_${deal.id}_${date}.pdf`);
|
||
}}
|
||
copied={false}
|
||
onCopiedLabel={"Ссылка скопирована в буфер обмена"}
|
||
>
|
||
Ссылка на оплату (PDF)
|
||
</ButtonCopyControlled>
|
||
}
|
||
<ButtonCopyControlled
|
||
onCopyClick={onCopyGuestUrlClick}
|
||
onCopiedLabel={"Ссылка скопирована в буфер обмена"}
|
||
copied={clipboard.copied}>
|
||
Ссылка на редактирование
|
||
</ButtonCopyControlled>
|
||
</Flex>
|
||
|
||
</Flex>
|
||
<Flex gap={rem(10)}>
|
||
|
||
<Checkbox
|
||
label={"Оплачен"}
|
||
checked={deal.billRequest?.paid || false}
|
||
disabled
|
||
/>
|
||
<Checkbox
|
||
label={"Сделка завершена"}
|
||
{...form.getInputProps("isCompleted")}
|
||
/>
|
||
|
||
<Checkbox
|
||
label={"Сделка удалена"}
|
||
{...form.getInputProps("isDeleted")}
|
||
/>
|
||
</Flex>
|
||
|
||
</Flex>
|
||
<Divider
|
||
orientation={"vertical"}
|
||
/>
|
||
<Group align={"center"} justify={"center"}>
|
||
<Button
|
||
color={"red"}
|
||
type={"reset"}
|
||
disabled={isEqual(initialValues, form.values)}
|
||
onClick={() => form.reset()}
|
||
>Отменить изменения</Button>
|
||
<Button
|
||
variant={"default"}
|
||
type={"submit"}
|
||
disabled={isEqual(initialValues, form.values)}
|
||
>Сохранить изменения</Button>
|
||
</Group>
|
||
</Flex>
|
||
</Flex>
|
||
|
||
</form>
|
||
|
||
);
|
||
};
|
||
const DealEditDrawerGeneralTab: FC = () => {
|
||
const { selectedDeal } = useDealPageContext();
|
||
if (!selectedDeal) return <>No deal selected</>;
|
||
return (
|
||
<Content deal={selectedDeal} />
|
||
);
|
||
};
|
||
export default DealEditDrawerGeneralTab; |