Add all certificates download button
This commit is contained in:
parent
a6e08eaea0
commit
d85d90bd16
@ -5,7 +5,7 @@ import { IItem } from "@Front/Components/DesignSystem/Menu/MenuItem";
|
||||
import Tag, { ETagColor } from "@Front/Components/DesignSystem/Tag";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import Module from "@Front/Config/Module";
|
||||
import { ArchiveBoxIcon, EllipsisHorizontalIcon, PaperAirplaneIcon, PencilSquareIcon, ShieldCheckIcon, UsersIcon } from "@heroicons/react/24/outline";
|
||||
import { ArchiveBoxIcon, DocumentTextIcon, EllipsisHorizontalIcon, PaperAirplaneIcon, PencilSquareIcon, ShieldCheckIcon, UsersIcon } from "@heroicons/react/24/outline";
|
||||
import classNames from "classnames";
|
||||
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||
import Link from "next/link";
|
||||
@ -21,10 +21,11 @@ type IProps = {
|
||||
onArchive: () => void;
|
||||
anchorStatus: AnchorStatus;
|
||||
isArchived: boolean;
|
||||
onDownloadAllCertificates: () => void;
|
||||
};
|
||||
|
||||
export default function InformationSection(props: IProps) {
|
||||
const { folder, progress, onArchive, anchorStatus, isArchived } = props;
|
||||
const { folder, progress, onArchive, anchorStatus, isArchived, onDownloadAllCertificates } = props;
|
||||
const router = useRouter();
|
||||
|
||||
const menuItemsDekstop = useMemo(() => {
|
||||
@ -56,6 +57,13 @@ export default function InformationSection(props: IProps) {
|
||||
.modules.pages.Folder.pages.VerifyDocuments.props.path.replace("[folderUid]", folder?.uid ?? "");
|
||||
router.push(verifyPath);
|
||||
},
|
||||
hasSeparator: true,
|
||||
};
|
||||
|
||||
const downloadAllCertificatesElement = {
|
||||
icon: <DocumentTextIcon />,
|
||||
text: "Télécharger tous les certificats",
|
||||
onClick: () => onDownloadAllCertificates(),
|
||||
hasSeparator: false,
|
||||
};
|
||||
|
||||
@ -67,6 +75,7 @@ export default function InformationSection(props: IProps) {
|
||||
|
||||
// Add verify document option
|
||||
elements.push(verifyDocumentElement);
|
||||
elements.push(downloadAllCertificatesElement);
|
||||
|
||||
return elements;
|
||||
}, [anchorStatus, folder?.uid, router]);
|
||||
@ -103,6 +112,13 @@ export default function InformationSection(props: IProps) {
|
||||
hasSeparator: true,
|
||||
};
|
||||
|
||||
const downloadAllCertificatesElement = {
|
||||
icon: <DocumentTextIcon />,
|
||||
text: "Télécharger tous les certificats",
|
||||
onClick: () => onDownloadAllCertificates(),
|
||||
hasSeparator: false,
|
||||
};
|
||||
|
||||
// If the folder is not anchored, we can modify the collaborators and the informations
|
||||
if (anchorStatus === AnchorStatus.NOT_ANCHORED) {
|
||||
elements.push(modifyCollaboratorsElement);
|
||||
@ -111,6 +127,7 @@ export default function InformationSection(props: IProps) {
|
||||
|
||||
// Add verify document option
|
||||
elements.push(verifyDocumentElement);
|
||||
elements.push(downloadAllCertificatesElement);
|
||||
|
||||
elements.push({
|
||||
icon: <PaperAirplaneIcon />,
|
||||
@ -131,7 +148,7 @@ export default function InformationSection(props: IProps) {
|
||||
}
|
||||
|
||||
return elements;
|
||||
}, [anchorStatus, folder?.uid, isArchived, onArchive, router]);
|
||||
}, [anchorStatus, folder?.uid, isArchived, onArchive, onDownloadAllCertificates, router]);
|
||||
return (
|
||||
<section className={classes["root"]}>
|
||||
<div className={classes["info-box1"]}>
|
||||
|
@ -21,6 +21,13 @@ import NoClientView from "./NoClientView";
|
||||
import AnchoringProcessingInfo from "./elements/AnchoringProcessingInfo";
|
||||
|
||||
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||
import PdfService from "@Front/Services/PdfService";
|
||||
import MessageBus from "src/sdk/MessageBus";
|
||||
import { saveAs } from "file-saver";
|
||||
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
|
||||
|
||||
export enum AnchorStatus {
|
||||
@ -145,6 +152,121 @@ export default function FolderInformation(props: IProps) {
|
||||
archiveModal.open();
|
||||
}, [anchorStatus, archiveModal, requireAnchoringModal]);
|
||||
|
||||
const onDownloadAllCertificates = useCallback(async () => {
|
||||
if (!folder?.uid) return;
|
||||
|
||||
try {
|
||||
LoaderService.getInstance().show();
|
||||
|
||||
// Get all documents for this folder
|
||||
const allDocuments = await DocumentService.getDocuments();
|
||||
const folderDocuments = allDocuments
|
||||
.map((process: any) => process.processData)
|
||||
.filter((doc: any) =>
|
||||
doc.folder?.uid === folder.uid &&
|
||||
doc.document_status === EDocumentStatus.VALIDATED
|
||||
);
|
||||
|
||||
if (folderDocuments.length === 0) {
|
||||
ToasterService.getInstance().warning({
|
||||
title: "Aucun certificat",
|
||||
description: "Aucun document validé trouvé pour ce dossier."
|
||||
});
|
||||
LoaderService.getInstance().hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate certificates for all validated documents
|
||||
const certificates: any[] = [];
|
||||
|
||||
for (const doc of folderDocuments) {
|
||||
try {
|
||||
// Get customer info
|
||||
const customer = doc.depositor || doc.customer;
|
||||
|
||||
const certificateData: any = {
|
||||
customer: {
|
||||
firstName: customer?.first_name || customer?.firstName || "N/A",
|
||||
lastName: customer?.last_name || customer?.lastName || "N/A",
|
||||
postalAddress: customer?.postal_address || customer?.address || customer?.postalAddress || "N/A",
|
||||
email: customer?.email || "N/A"
|
||||
},
|
||||
notary: {
|
||||
name: "N/A"
|
||||
},
|
||||
folderUid: folder.uid,
|
||||
documentHash: "N/A",
|
||||
metadata: {
|
||||
fileName: "N/A",
|
||||
isDeleted: false,
|
||||
updatedAt: new Date(),
|
||||
commitmentId: "N/A",
|
||||
createdAt: new Date(),
|
||||
documentUid: "N/A",
|
||||
documentType: "N/A",
|
||||
merkleProof: "N/A"
|
||||
}
|
||||
};
|
||||
|
||||
// Process files for this document
|
||||
if (doc.files && doc.files.length > 0) {
|
||||
for (const file of doc.files) {
|
||||
const fileProcess = await FileService.getFileByUid(file.uid);
|
||||
|
||||
if (fileProcess.lastUpdatedFileState?.pcd_commitment?.file_blob) {
|
||||
const hash = fileProcess.lastUpdatedFileState.pcd_commitment.file_blob;
|
||||
certificateData.documentHash = hash;
|
||||
|
||||
const proof = await MessageBus.getInstance().generateMerkleProof(
|
||||
fileProcess.lastUpdatedFileState,
|
||||
'file_blob'
|
||||
);
|
||||
|
||||
const metadata: any = {
|
||||
fileName: fileProcess.processData.file_name,
|
||||
isDeleted: false,
|
||||
updatedAt: new Date(fileProcess.processData.updated_at),
|
||||
commitmentId: fileProcess.lastUpdatedFileState.commited_in,
|
||||
createdAt: new Date(fileProcess.processData.created_at),
|
||||
documentUid: doc.uid,
|
||||
documentType: doc.document_type?.name || "N/A",
|
||||
merkleProof: proof
|
||||
};
|
||||
certificateData.metadata = metadata;
|
||||
break; // Use first file for now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
certificates.push(certificateData);
|
||||
} catch (error) {
|
||||
console.error(`Error processing document ${doc.uid}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate combined PDF
|
||||
const combinedPdf = await PdfService.getInstance().generateCombinedCertificates(certificates, folder);
|
||||
|
||||
// Download the combined PDF
|
||||
const filename = `certificats_${folder.folder_number || folder.uid}_${new Date().toISOString().split('T')[0]}.pdf`;
|
||||
saveAs(combinedPdf, filename);
|
||||
|
||||
ToasterService.getInstance().success({
|
||||
title: "Succès !",
|
||||
description: `${certificates.length} certificat(s) téléchargé(s) avec succès.`
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error downloading all certificates:', error);
|
||||
ToasterService.getInstance().error({
|
||||
title: "Erreur",
|
||||
description: "Une erreur est survenue lors du téléchargement des certificats."
|
||||
});
|
||||
} finally {
|
||||
LoaderService.getInstance().hide();
|
||||
}
|
||||
}, [folder?.uid]);
|
||||
|
||||
return (
|
||||
<DefaultNotaryDashboard title={"Dossier"} isArchived={isArchived} mobileBackText="Retour aux dossiers">
|
||||
{!isLoading && (
|
||||
@ -155,6 +277,7 @@ export default function FolderInformation(props: IProps) {
|
||||
onArchive={onArchive}
|
||||
anchorStatus={anchorStatus}
|
||||
isArchived={isArchived}
|
||||
onDownloadAllCertificates={onDownloadAllCertificates}
|
||||
/>
|
||||
{progress === 100 && /*anchorStatus === AnchorStatus.NOT_ANCHORED*/ folder.status !== EFolderStatus.ARCHIVED && (
|
||||
<AnchoringAlertInfo onAnchor={anchoringModal.open} />
|
||||
|
@ -335,4 +335,193 @@ export default class PdfService {
|
||||
fileReader.readAsArrayBuffer(certificateFile);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a combined PDF with multiple certificates
|
||||
* @param certificates - Array of certificate data
|
||||
* @param folder - Folder information
|
||||
* @returns Promise<Blob> - Combined PDF as blob
|
||||
*/
|
||||
public async generateCombinedCertificates(certificates: CertificateData[], folder: any): Promise<Blob> {
|
||||
try {
|
||||
// Import pdf-lib dynamically to avoid SSR issues
|
||||
const { PDFDocument, rgb, StandardFonts } = await import('pdf-lib');
|
||||
|
||||
// Create a new PDF document
|
||||
const pdfDoc = await PDFDocument.create();
|
||||
const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
||||
const helveticaBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
|
||||
|
||||
// Add a cover page
|
||||
const coverPage = pdfDoc.addPage([595.28, 841.89]); // A4 size
|
||||
const { width, height } = coverPage.getSize();
|
||||
|
||||
// Cover page title
|
||||
coverPage.drawText('Certificats de Validation', {
|
||||
x: 50,
|
||||
y: height - 100,
|
||||
size: 24,
|
||||
font: helveticaBold,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
// Folder information
|
||||
coverPage.drawText(`Dossier: ${folder.folder_number || folder.uid}`, {
|
||||
x: 50,
|
||||
y: height - 150,
|
||||
size: 16,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
coverPage.drawText(`Nom: ${folder.name || 'N/A'}`, {
|
||||
x: 50,
|
||||
y: height - 180,
|
||||
size: 16,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
coverPage.drawText(`Date de génération: ${new Date().toLocaleDateString('fr-FR')}`, {
|
||||
x: 50,
|
||||
y: height - 210,
|
||||
size: 16,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
coverPage.drawText(`Nombre de certificats: ${certificates.length}`, {
|
||||
x: 50,
|
||||
y: height - 240,
|
||||
size: 16,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
// Add each certificate as a separate page
|
||||
for (let i = 0; i < certificates.length; i++) {
|
||||
const certificate = certificates[i];
|
||||
if (!certificate) continue;
|
||||
|
||||
const page = pdfDoc.addPage([595.28, 841.89]); // A4 size
|
||||
const { width: pageWidth, height: pageHeight } = page.getSize();
|
||||
|
||||
// Certificate title
|
||||
page.drawText(`Certificat ${i + 1}`, {
|
||||
x: 50,
|
||||
y: pageHeight - 50,
|
||||
size: 20,
|
||||
font: helveticaBold,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
// Customer information
|
||||
page.drawText('Informations Client:', {
|
||||
x: 50,
|
||||
y: pageHeight - 100,
|
||||
size: 16,
|
||||
font: helveticaBold,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
page.drawText(`Nom: ${certificate.customer.firstName} ${certificate.customer.lastName}`, {
|
||||
x: 50,
|
||||
y: pageHeight - 130,
|
||||
size: 12,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
page.drawText(`Adresse: ${certificate.customer.postalAddress}`, {
|
||||
x: 50,
|
||||
y: pageHeight - 150,
|
||||
size: 12,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
page.drawText(`Email: ${certificate.customer.email}`, {
|
||||
x: 50,
|
||||
y: pageHeight - 170,
|
||||
size: 12,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
// Document information
|
||||
page.drawText('Informations Document:', {
|
||||
x: 50,
|
||||
y: pageHeight - 200,
|
||||
size: 16,
|
||||
font: helveticaBold,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
page.drawText(`Type: ${certificate.metadata.documentType}`, {
|
||||
x: 50,
|
||||
y: pageHeight - 230,
|
||||
size: 12,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
page.drawText(`Fichier: ${certificate.metadata.fileName}`, {
|
||||
x: 50,
|
||||
y: pageHeight - 250,
|
||||
size: 12,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
page.drawText(`Hash: ${certificate.documentHash}`, {
|
||||
x: 50,
|
||||
y: pageHeight - 270,
|
||||
size: 12,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
page.drawText(`Commitment ID: ${certificate.metadata.commitmentId}`, {
|
||||
x: 50,
|
||||
y: pageHeight - 290,
|
||||
size: 12,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
page.drawText(`Date de création: ${certificate.metadata.createdAt.toLocaleDateString('fr-FR')}`, {
|
||||
x: 50,
|
||||
y: pageHeight - 310,
|
||||
size: 12,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
page.drawText(`Date de mise à jour: ${certificate.metadata.updatedAt.toLocaleDateString('fr-FR')}`, {
|
||||
x: 50,
|
||||
y: pageHeight - 330,
|
||||
size: 12,
|
||||
font: helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
});
|
||||
|
||||
// Add page number
|
||||
page.drawText(`Page ${i + 2}`, {
|
||||
x: pageWidth - 80,
|
||||
y: 30,
|
||||
size: 10,
|
||||
font: helveticaFont,
|
||||
color: rgb(0.5, 0.5, 0.5),
|
||||
});
|
||||
}
|
||||
|
||||
// Save the PDF
|
||||
const pdfBytes = await pdfDoc.save();
|
||||
return new Blob([pdfBytes], { type: 'application/pdf' });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error generating combined certificates:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user