Files
Fulfillment-Frontend/src/pages/LeadsPage/ui/LeadsPage.tsx

397 lines
17 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, useEffect, useState } from "react";
import styles from "./LeadsPage.module.css";
import Board from "../../../components/Dnd/Board/Board.tsx";
import { DragDropContext, Droppable, DropResult } from "@hello-pangea/dnd";
import { useDealSummaries } from "../hooks/useDealSummaries.tsx";
import {
DealStatus,
getDealStatusByName,
} from "../../../shared/enums/DealStatus.ts";
import PageBlock from "../../../components/PageBlock/PageBlock.tsx";
import DealEditDrawer from "../drawers/DealEditDrawer/DealEditDrawer.tsx";
import { DealPageContextProvider } from "../contexts/DealPageContext.tsx";
import { modals } from "@mantine/modals";
import { DealService, DealSummaryReorderRequest } from "../../../client";
import { ActionIcon, Flex, NumberInput, rem, Text } from "@mantine/core";
import classNames from "classnames";
import { notifications } from "../../../shared/lib/notifications.ts";
import { IconMenu2, IconMenuDeep } from "@tabler/icons-react";
import useDealsPageState from "../../DealsPage/hooks/useDealsPageState.tsx";
import DealStatusSelect from "../../DealsPage/components/DealStatusSelect/DealStatusSelect.tsx";
import BaseMarketplaceSelect from "../../../components/Selects/BaseMarketplaceSelect/BaseMarketplaceSelect.tsx";
import ClientSelectNew from "../../../components/Selects/ClientSelectNew/ClientSelectNew.tsx";
import DealsTable from "../../DealsPage/components/DealsTable/DealsTable.tsx";
import { motion } from "framer-motion";
enum DisplayMode {
BOARD,
TABLE,
}
export const LeadsPage: FC = () => {
const { data, form } = useDealsPageState();
const { summariesRaw, refetch } = useDealSummaries();
const [summaries, setSummaries] = useState(summariesRaw);
const [displayMode, setDisplayMode] = useState<DisplayMode>(
DisplayMode.BOARD
);
const [isDragEnded, setIsDragEnded] = useState(true);
useEffect(() => {
setSummaries(summariesRaw);
}, [summariesRaw]);
const onDelete = (dealId: number) => {
const summary = summaries.find(summary => summary.id == dealId);
if (!summary) return;
modals.openConfirmModal({
title: "Удаление сделки",
children: (
<Flex>
Вы действительно хотите удалить сделку {summary.name}?
</Flex>
),
onConfirm: () => {
DealService.deleteDeal({
requestBody: { dealId: dealId },
}).then(async ({ ok, message }) => {
notifications.guess(ok, { message });
if (!ok) return;
await refetch();
});
},
labels: {
confirm: "Удалить",
cancel: "Отмена",
},
});
};
const onSuccess = (dealId: number) => {
const summary = summaries.find(summary => summary.id == dealId);
if (!summary) return;
modals.openConfirmModal({
title: "Завершение сделки",
children: (
<Flex>
Вы действительно хотите завершить сделку {summary.name}?
</Flex>
),
onConfirm: () => {
DealService.completeDeal({
requestBody: { dealId: dealId },
}).then(async ({ ok, message }) => {
notifications.guess(ok, { message });
if (!ok) return;
await refetch();
});
},
labels: {
confirm: "Завершить",
cancel: "Отмена",
},
});
};
const onDragEnd = async (result: DropResult) => {
setIsDragEnded(true);
// If there is no changes
if (!result.destination || result.destination == result.source) return;
// Checking for valid dealId
const dealId = parseInt(result.draggableId);
if (isNaN(dealId)) return;
// Checking for valid deal
const summary = summaries.find(summary => summary.id == dealId);
if (!summary) return;
// Checking if it is custom actions
const droppableId = result.destination.droppableId;
if (droppableId === "DELETE") {
onDelete(dealId);
return;
}
if (droppableId === "SUCCESS") {
onSuccess(dealId);
return;
}
const status = getDealStatusByName(droppableId);
const request: Partial<DealSummaryReorderRequest> = {
dealId: dealId,
index: result.destination.index,
status: status,
};
if (status == summary.status) {
DealService.reorderDealSummaries({
requestBody: request as DealSummaryReorderRequest,
}).then(async response => {
setSummaries(response.summaries);
await refetch();
});
return;
}
modals.openContextModal({
modal: "enterDeadline",
title: "Необходимо указать дедлайн",
innerProps: {
onSubmit: event =>
DealService.reorderDealSummaries({
requestBody: event,
}).then(async response => {
setSummaries(response.summaries);
await refetch();
}),
request: request,
},
});
};
const getTableBody = () => {
return (
<motion.div
key={displayMode}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}>
<DealsTable items={data} />
</motion.div>
);
};
const getBoardBody = () => {
return (
<motion.div
style={{
display: "flex",
height: "100%",
flex: 1,
}}
key={displayMode}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}>
<DragDropContext
onDragStart={() => {
setIsDragEnded(false);
}}
onDragEnd={onDragEnd}>
<Flex
justify={"space-between"}
direction={"column"}
style={{ flex: 1 }}>
<div className={styles["boards"]}>
<Board
withCreateButton
summaries={summaries.filter(
summary =>
summary.status ==
DealStatus.AWAITING_ACCEPTANCE
)}
title={"Ожидает приемки"}
droppableId={"AWAITING_ACCEPTANCE"}
color={"#4A90E2"}
/>
<Board
summaries={summaries.filter(
summary =>
summary.status == DealStatus.PACKAGING
)}
title={"Упаковка"}
droppableId={"PACKAGING"}
color={"#F5A623"}
/>
<Board
summaries={summaries.filter(
summary =>
summary.status ==
DealStatus.AWAITING_SHIPMENT
)}
title={"Ожидает отгрузки"}
droppableId={"AWAITING_SHIPMENT"}
color={"#7ED321"}
/>
<Board
summaries={summaries.filter(
summary =>
summary.status ==
DealStatus.AWAITING_PAYMENT
)}
title={"Ожидает оплаты"}
droppableId={"AWAITING_PAYMENT"}
color={"#D0021B"}
/>
<Board
summaries={summaries.filter(
summary =>
summary.status == DealStatus.COMPLETED
)}
title={"Завершена"}
droppableId={"COMPLETED"}
color={"#417505"}
/>
</div>
<Flex
justify={"space-between"}
gap={rem(10)}>
<div
className={classNames(
styles["delete"],
isDragEnded && styles["delete-hidden"]
)}>
<Droppable droppableId={"DELETE"}>
{(provided, snapshot) => (
<>
<div
{...provided.droppableProps}
ref={provided.innerRef}>
{!isDragEnded &&
!snapshot.isDraggingOver && (
<span>Удалить</span>
)}
</div>
{provided.placeholder}
</>
)}
</Droppable>
</div>
<div
className={classNames(
styles["delete"],
isDragEnded && styles["delete-hidden"]
)}>
<Droppable droppableId={"SUCCESS"}>
{(provided, snapshot) => (
<>
<div
{...provided.droppableProps}
ref={provided.innerRef}>
{!isDragEnded &&
!snapshot.isDraggingOver && (
<span>
Успешно завершена
</span>
)}
</div>
{provided.placeholder}
</>
)}
</Droppable>
</div>
</Flex>
</Flex>
</DragDropContext>
</motion.div>
);
};
const getBody = () => {
return displayMode === DisplayMode.TABLE
? getTableBody()
: getBoardBody();
};
return (
<PageBlock
fullHeight
style={{
gap: rem(10),
display: "flex",
flexDirection: "column",
backgroundColor: "transparent",
boxShadow: "none",
}}>
<DealPageContextProvider>
<PageBlock style={{ flex: 0 }}>
<Flex
align={"center"}
justify={"space-between"}>
<Flex
gap={rem(10)}
direction={"column"}
align={"center"}>
<Text size={"xs"}>Вид</Text>
<Flex gap={rem(10)}>
<ActionIcon
onClick={() =>
setDisplayMode(DisplayMode.BOARD)
}
variant={
displayMode === DisplayMode.BOARD
? "filled"
: "default"
}>
<IconMenuDeep
style={{ rotate: "-90deg" }}
/>
</ActionIcon>
<ActionIcon
onClick={() =>
setDisplayMode(DisplayMode.TABLE)
}
variant={
displayMode === DisplayMode.TABLE
? "filled"
: "default"
}>
<IconMenu2 />
</ActionIcon>
</Flex>
</Flex>
<motion.div
key={displayMode}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}>
<div
className={styles["top-panel"]}
style={{
display:
displayMode === DisplayMode.TABLE
? "flex"
: "none",
}}>
<NumberInput
min={1}
step={1}
placeholder={"Введите номер"}
{...form.getInputProps("id")}
/>
<DealStatusSelect
onClear={() =>
form.setFieldValue("dealStatus", null)
}
clearable
placeholder={"Выберите статус "}
{...form.getInputProps("dealStatus")}
/>
<BaseMarketplaceSelect
onClear={() =>
form.setFieldValue("marketplace", null)
}
clearable
placeholder={"Выберите маркетплейс"}
{...form.getInputProps("marketplace")}
/>
<ClientSelectNew
onClear={() =>
form.setFieldValue("client", null)
}
clearable
searchable
placeholder={"Выберите клиента"}
{...form.getInputProps("client")}
/>
</div>
</motion.div>
</Flex>
</PageBlock>
<PageBlock
style={{
display: "flex",
flexDirection: "column",
flex: 1,
height: "100%",
}}>
{getBody()}
</PageBlock>
<DealEditDrawer />
</DealPageContextProvider>
</PageBlock>
);
};