Files
Fulfillment-Frontend/src/pages/CardsPage/tabs/GeneralTab/GeneralTab.tsx

310 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { FC, useState } from "react";
import { useCardPageContext } from "../../contexts/CardPageContext.tsx";
import { Button, Checkbox, Divider, Fieldset, Flex, Group, rem, Textarea, TextInput } from "@mantine/core";
import { useForm } from "@mantine/form";
import {
CardSchema,
CardService,
ClientService,
ProjectSchema,
ShippingWarehouseSchema,
StatusSchema,
} from "../../../../client";
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 ManagerSelect from "../../../../components/ManagerSelect/ManagerSelect.tsx";
import ProjectSelect from "../../../../components/ProjectSelect/ProjectSelect.tsx";
import BoardSelect from "../../../../components/BoardSelect/BoardSelect.tsx";
import DealStatusSelect from "../../../../components/DealStatusSelect/DealStatusSelect.tsx";
import CardAttributeFields from "../../../../components/CardAttributeFields/CardAttributeFields.tsx";
import getAttributesFromCard from "../../../../components/CardAttributeFields/utils/getAttributesFromCard.ts";
import isModuleInProject, { Modules } from "../../utils/isModuleInProject.ts";
import PaymentLinkButton from "./components/PaymentLinkButton.tsx";
import PrintDealBarcodesButton from "./components/PrintDealBarcodesButton.tsx";
type Props = {
card: CardSchema;
};
type Attributes = {
[key: string]: number | boolean | string;
};
export type CardGeneralFormType = Omit<CardSchema, "statusHistory" | "services" | "products">;
const Content: FC<Props> = ({ card }) => {
const { setSelectedCard } = useCardPageContext();
const clipboard = useClipboard();
const queryClient = useQueryClient();
const [project, setProject] = useState<ProjectSchema | null>(card.board.project);
const isServicesAndProductsIncluded = isModuleInProject(Modules.SERVICES_AND_PRODUCTS, card.board.project);
const getInitialValues = (card: CardSchema): CardGeneralFormType => {
return {
...card,
...getAttributesFromCard(card),
};
};
let initialValues = getInitialValues(card);
const form = useForm<CardGeneralFormType>({
initialValues,
validate: {
name: (value: string) =>
value.length > 0
? null
: "Название не может быть пустым",
status: (value: StatusSchema) =>
!value && "Статус не выбран",
},
});
const updateCardInfo = async (values: CardGeneralFormType) => {
console.log("Updated attributes:");
console.log(values);
const formCardAttrs = values as unknown as Attributes;
const attributes = project?.attributes.reduce((attrs, projectAttr) => {
return {
...attrs,
[projectAttr.name]: formCardAttrs[projectAttr.name],
};
}, {});
return CardService.updateCardGeneralInfo({
requestBody: {
cardId: card.id,
data: {
...values,
statusId: values.status.id,
boardId: values.board.id,
shippingWarehouse: values.shippingWarehouse?.toString(),
attributes,
},
},
}).then(({ ok, message }) => {
notifications.guess(ok, { message });
if (!ok) return;
CardService.getCardById({ cardId: card.id }).then(data => {
console.log(data);
setSelectedCard(data);
initialValues = getInitialValues(data);
form.setValues(initialValues);
queryClient.invalidateQueries({
queryKey: ["getCardSummaries"],
});
});
});
};
const updateClientInfo = async (values: CardGeneralFormType) => {
return ClientService.updateClient({
requestBody: {
data: values.client,
},
}).then(({ ok, message }) => notifications.guess(ok, { message }));
};
const handleSubmit = async (values: CardGeneralFormType) => {
// Updating client info if there changes
if (!isEqual(values.client, card.client)) {
await updateClientInfo(values);
}
const shippingWarehouse = isShippingWarehouse(values.shippingWarehouse) ? values.shippingWarehouse.name : values.shippingWarehouse;
await updateCardInfo(
{
...values,
shippingWarehouse,
},
);
};
const isShippingWarehouse = (
value: ShippingWarehouseSchema | string | null | undefined,
): value is ShippingWarehouseSchema => {
return !["string", "null", "undefined"].includes(typeof value);
};
const onCopyGuestUrlClick = () => {
CardService.createDealGuestUrl({
requestBody: {
cardId: card.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"}
justify={"space-between"}
h={"100%"}>
<Fieldset legend={`Общие параметры [ID: ${card.id}]`}>
<Flex
direction={"column"}
gap={rem(10)}
>
<TextInput
placeholder={"Название сделки"}
label={"Название сделки"}
{...form.getInputProps("name")}
/>
<TextInput
disabled
placeholder={"Дата создания"}
label={"Дата создания"}
value={new Date(card.createdAt).toLocaleString(
"ru-RU",
)}
/>
<ProjectSelect
value={project}
onChange={setProject}
label={"Проект"}
disabled
/>
<BoardSelect
project={project}
{...form.getInputProps("board")}
label={"Доска"}
/>
<DealStatusSelect
board={form.values.board}
{...form.getInputProps("status")}
label={"Статус"}
/>
<Textarea
h={rem(150)}
styles={{
wrapper: { height: "90%" },
input: { height: "90%" },
}}
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);
}}
/>
<ManagerSelect
placeholder={"Укажите менеджера"}
label={"Менеджер"}
{...form.getInputProps("manager")}
/>
{project && (
<CardAttributeFields
project={project}
form={form}
/>
)}
</Flex>
</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"}>
{isServicesAndProductsIncluded && (
<PrintDealBarcodesButton card={card}/>
)}
<Flex gap={rem(10)}>
{isServicesAndProductsIncluded && (
<PaymentLinkButton card={card} />
)}
<ButtonCopyControlled
onCopyClick={onCopyGuestUrlClick}
onCopiedLabel={
"Ссылка скопирована в буфер обмена"
}
copied={clipboard.copied}
>
Ссылка на редактирование
</ButtonCopyControlled>
</Flex>
</Flex>
<Flex gap={rem(10)}>
{isServicesAndProductsIncluded && (
<Checkbox
label={"Оплачен"}
checked={card.billRequest?.paid || card.group?.billRequest?.paid || false}
disabled
/>
)}
<Checkbox
label={"Завершена"}
{...form.getInputProps("isCompleted", { type: "checkbox" })}
/>
<Checkbox
label={"Удалена"}
{...form.getInputProps("isDeleted", { type: "checkbox" })}
/>
</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 GeneralTab: FC = () => {
const { selectedCard } = useCardPageContext();
if (!selectedCard) return <>No card selected</>;
return <Content card={selectedCard} />;
};
export default GeneralTab;