2025-04-10 16:29:16 +02:00

341 lines
11 KiB
TypeScript

import Customers from "@Front/Api/LeCoffreApi/Notary/Customers/Customers";
import DocumentReminders from "@Front/Api/LeCoffreApi/Notary/DocumentReminders/DocumentReminders";
import Dropdown from "@Front/Components/DesignSystem/Dropdown";
import { IOption } from "@Front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption";
import Table from "@Front/Components/DesignSystem/Table";
import { IHead, IRowProps } from "@Front/Components/DesignSystem/Table/MuiTable";
import Tag, { ETagColor, ETagVariant } from "@Front/Components/DesignSystem/Tag";
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
import BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
import Module from "@Front/Config/Module";
import Customer from "le-coffre-resources/dist/Customer";
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
import { DocumentReminder } from "le-coffre-resources/dist/Notary";
import { useRouter } from "next/router";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import classes from "./classes.module.scss";
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
type IProps = {};
const tradDocumentStatus: Record<EDocumentStatus, string> = {
[EDocumentStatus.ASKED]: "DEMANDÉ",
[EDocumentStatus.DEPOSITED]: "À VALIDER",
[EDocumentStatus.VALIDATED]: "VALIDÉ",
[EDocumentStatus.REFUSED]: "REFUSÉ",
};
const header: readonly IHead[] = [
{
key: "remindedAt",
title: "Date de relance",
},
{
key: "customer",
title: "Client",
},
{
key: "document_type",
title: "Type de document",
},
{
key: "statut",
title: "Satut",
},
];
export default function DocumentsReminderHistory(props: IProps) {
const [reminders, setReminders] = useState<DocumentReminder[] | null>(null);
const [customers, setCustomers] = useState<Customer[] | null>(null);
const [customerOption, setCustomerOption] = useState<IOption | null>(null);
const router = useRouter();
const [page, setPage] = useState(1); // Current page number
const [pageSize, setPageSize] = useState(10); // Number of items per page
const [totalPages, setTotalPages] = useState(1); // Total number of pages
let { folderUid } = router.query;
const fetchTotalPages = useCallback(() => {
DocumentReminders.getInstance()
.count({
where: {
...(folderUid && {
document: {
folder: {
uid: folderUid as string,
},
},
}),
},
})
.then((response) => {
const totalPages = Math.ceil(response.count / pageSize);
setTotalPages(totalPages);
})
.catch((e) => console.warn(e));
}, [pageSize]);
const fetchReminders = useCallback(() => {
DocumentReminders.getInstance()
.get({
...(customerOption && customerOption.id !== "tous" && { where: { document: { depositor: { uid: customerOption.id } } } }),
where: {
...(folderUid && {
document: {
folder: {
uid: folderUid as string,
},
},
}),
},
include: {
document: {
include: {
depositor: {
include: {
contact: true,
},
},
document_type: true,
},
},
},
orderBy: { reminder_date: "desc" },
skip: (page - 1) * pageSize, // Skip based on the page number
take: pageSize, // Take the number of items for the page
})
.then((response) => {
setReminders(response); // Set the reminders
})
.catch((e) => console.warn(e));
}, [customerOption, page, pageSize]); // Update on page change
const fetchCustomers = useCallback(async () => {
if (!folderUid) return;
Customers.getInstance()
.get({
where: {
office_folders: {
some: {
uid: folderUid as string,
},
},
},
})
.then(setCustomers)
.catch(console.warn);
}, [folderUid]);
const customersOptions: IOption[] = useMemo(() => {
let options = [
{
id: "tous",
label: "Tous",
},
];
customers?.forEach((customer) => {
options.push({
id: customer.uid ?? "",
label: `${customer.contact?.first_name} ${customer.contact?.last_name}`,
});
});
return options;
}, [customers]);
useEffect(() => {
fetchTotalPages();
fetchReminders();
fetchCustomers();
}, [fetchTotalPages, fetchCustomers, fetchReminders]);
const generatePageNumbers = (currentPage: number, totalPages: number) => {
const pageNumbers = [];
const maxPagesToShow = 7; // Maximum number of page buttons, including ellipsis
// Case 1: If total pages are <= 5, show all pages
if (totalPages <= 5) {
for (let i = 1; i <= totalPages; i++) {
pageNumbers.push(i);
}
}
// Case 2: If current page is <= 3, and totalPages > 5, show the first 4 pages + ellipsis
else if (currentPage <= 3) {
for (let i = 1; i <= 4; i++) {
pageNumbers.push(i);
}
pageNumbers.push("...");
}
// Case 3: If current page is in the middle, show ellipses and surrounding pages
else if (currentPage > 3 && currentPage < totalPages - 2) {
pageNumbers.push("...");
pageNumbers.push(currentPage - 1);
pageNumbers.push(currentPage);
pageNumbers.push(currentPage + 1);
pageNumbers.push("...");
}
// Case 4: If current page is near the end, show ellipsis and last 4 pages
else if (currentPage >= totalPages - 2) {
pageNumbers.push("...");
for (let i = totalPages - 3; i <= totalPages; i++) {
pageNumbers.push(i);
}
}
// Ensure the pagination length is exactly maxPagesToShow (counting ellipses as one)
if (pageNumbers.length > maxPagesToShow) {
// Remove ellipsis at the start or end if pagination exceeds max length
if (pageNumbers[0] === "...") {
pageNumbers.shift(); // Remove first ellipsis if it's the first item
}
if (pageNumbers.length > maxPagesToShow && pageNumbers[pageNumbers.length - 1] === "...") {
pageNumbers.pop(); // Remove last ellipsis if it's the last item
}
}
return pageNumbers;
};
// Handle page change
const handlePageChange = (newPage: number) => {
if (newPage >= 1 && newPage <= totalPages) {
setPage(newPage);
}
};
return (
<DefaultTemplate title={"Historique des relances de documents"} isPadding={false}>
<div className={classes["root"]}>
<BackArrow
text="Retour"
url={Module.getInstance()
.get()
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", folderUid as string)}
/>
<Typography typo={ETypo.TITLE_H1} color={ETypoColor.TEXT_PRIMARY}>
Historique des relances de documents
</Typography>
<Dropdown
className={classes["customer-filter"]}
options={customersOptions}
onSelectionChange={(option) => setCustomerOption(option ?? null)}
selectedOption={customerOption ?? customersOptions?.[0]}
label="Client"
/>
{/* Page Size Selector */}
<Dropdown
className={classes["page-size-selector"]}
options={[
{ id: "10", label: "10 par page" },
{ id: "20", label: "20 par page" },
{ id: "50", label: "50 par page" },
]}
selectedOption={{ id: `${pageSize}`, label: `${pageSize} par page` }}
onSelectionChange={(option) => {
setPageSize(parseInt(option.id, 10)); // Update the page size
setPage(1); // Reset the page to 1
}}
label="Items par page"
/>
<Table className={classes["table"]} header={header} rows={buildRows(reminders)} />
<div className={classes["pagination"]}>
{/* Left Arrow (Prev) */}
<Button
variant={EButtonVariant.PRIMARY}
styletype={EButtonstyletype.OUTLINED}
className={`${page === 1 ? classes["disabled"] : ""} ${classes["prev"]}`}
onClick={() => handlePageChange(page - 1)}>
&lt;
</Button>
{/* <button
className={`${page === 1 ? classes["disabled"] : ""} ${classes["prev"]}`}
onClick={() => handlePageChange(page - 1)}
aria-label="Previous Page">
&lt;
</button> */}
{/* Page numbers */}
{generatePageNumbers(page, totalPages).map((pageNum, index) => (
<React.Fragment key={index}>
{pageNum === "..." ? (
<Button variant={EButtonVariant.PRIMARY}>...</Button>
) : (
<Button
variant={EButtonVariant.PRIMARY}
className={page === pageNum ? classes["active"] : ""}
onClick={() => handlePageChange(pageNum as number)}>
{pageNum}
</Button>
// <button
// className={page === pageNum ? classes["active"] : ""}
// onClick={() => handlePageChange(pageNum as number)}
// aria-label={`Go to page ${pageNum}`}>
// {pageNum}
// </button>
)}
</React.Fragment>
))}
{/* Right Arrow (Next) */}
<Button
variant={EButtonVariant.PRIMARY}
styletype={EButtonstyletype.OUTLINED}
className={`${page === totalPages ? classes["disabled"] : ""} ${classes["next"]}`}
onClick={() => handlePageChange(page + 1)}>
&gt;
</Button>
{/* <button
className={`${page === totalPages ? classes["disabled"] : ""} ${classes["next"]}`}
onClick={() => handlePageChange(page + 1)}
aria-label="Next Page">
&gt;
</button> */}
</div>
</div>
</DefaultTemplate>
);
}
function buildRows(reminders: DocumentReminder[] | null): IRowProps[] {
if (!reminders) return [];
return reminders.map((reminder) => ({
key: reminder.uid ?? "",
remindedAt: { sx: { width: 220 }, content: formatDateWithHours(reminder.reminder_date) },
customer: {
sx: { width: 220 },
content: `${reminder.document?.depositor?.contact?.first_name} ${reminder.document?.depositor?.contact?.last_name}`,
},
document_type: reminder.document?.document_type?.name,
statut: { sx: { width: 220 }, content: getTag(reminder.document?.document_status as EDocumentStatus) },
}));
}
function getTag(status: EDocumentStatus) {
switch (status) {
case EDocumentStatus.ASKED:
return <Tag label={tradDocumentStatus[status]} color={ETagColor.INFO} variant={ETagVariant.SEMI_BOLD} />;
case EDocumentStatus.DEPOSITED:
return <Tag label={tradDocumentStatus[status]} color={ETagColor.WARNING} variant={ETagVariant.SEMI_BOLD} />;
case EDocumentStatus.VALIDATED:
return <Tag label={tradDocumentStatus[status]} color={ETagColor.SUCCESS} variant={ETagVariant.SEMI_BOLD} />;
case EDocumentStatus.REFUSED:
return <Tag label={tradDocumentStatus[status]} color={ETagColor.ERROR} variant={ETagVariant.SEMI_BOLD} />;
default:
return <Tag label={tradDocumentStatus[status]} color={ETagColor.INFO} variant={ETagVariant.SEMI_BOLD} />;
}
}
function formatDateWithHours(date: Date | null) {
if (!date) return "-";
return new Date(date).toLocaleDateString("fr-FR", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
});
}