Add basic certificate
This commit is contained in:
parent
6ed6682824
commit
368052c7ac
@ -213,9 +213,9 @@ export default function DragAndDrop(props: IProps) {
|
|||||||
</div>
|
</div>
|
||||||
{documentFiles.length > 0 && (
|
{documentFiles.length > 0 && (
|
||||||
<div className={classes["documents"]}>
|
<div className={classes["documents"]}>
|
||||||
{documentFiles.map((documentFile) => (
|
{documentFiles.map((documentFile, index) => (
|
||||||
<DocumentFileElement
|
<DocumentFileElement
|
||||||
key={documentFile.id}
|
key={documentFile.uid || `${documentFile.id}-${index}`}
|
||||||
isLoading={documentFile.isLoading}
|
isLoading={documentFile.isLoading}
|
||||||
file={documentFile.file}
|
file={documentFile.file}
|
||||||
onRemove={() => handleRemove(documentFile)}
|
onRemove={() => handleRemove(documentFile)}
|
||||||
|
@ -82,9 +82,9 @@ export default function DepositDocumentComponent(props: IProps) {
|
|||||||
(fileUid: string) => {
|
(fileUid: string) => {
|
||||||
return new Promise<void>(
|
return new Promise<void>(
|
||||||
(resolve: () => void) => {
|
(resolve: () => void) => {
|
||||||
FileService.getFileByUid(fileUid).then((process: any) => {
|
FileService.getFileByUid(fileUid).then((res: any) => {
|
||||||
if (process) {
|
if (res) {
|
||||||
FileService.updateFile(process, { isDeleted: 'true' }).then(() => {
|
FileService.updateFile(res.processId, { isDeleted: 'true' }).then(() => {
|
||||||
DocumentService.getDocumentByUid(document.uid!).then((process: any) => {
|
DocumentService.getDocumentByUid(document.uid!).then((process: any) => {
|
||||||
if (process) {
|
if (process) {
|
||||||
const document: any = process.processData;
|
const document: any = process.processData;
|
||||||
|
@ -17,6 +17,7 @@ import backgroundImage from "@Assets/images/background_refonte.svg";
|
|||||||
|
|
||||||
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||||
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||||
|
import { DocumentData } from "../FolderInformation/ClientView/DocumentTables/types";
|
||||||
|
|
||||||
export default function AskDocuments() {
|
export default function AskDocuments() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -61,17 +62,21 @@ export default function AskDocuments() {
|
|||||||
const documentAsked: [] = values["document_types"] as [];
|
const documentAsked: [] = values["document_types"] as [];
|
||||||
|
|
||||||
for (let i = 0; i < documentAsked.length; i++) {
|
for (let i = 0; i < documentAsked.length; i++) {
|
||||||
|
const documentTypeUid = documentAsked[i];
|
||||||
|
if (!documentTypeUid) continue;
|
||||||
|
|
||||||
const documentData: any = {
|
const documentData: any = {
|
||||||
folder: {
|
folder: {
|
||||||
uid: folderUid,
|
uid: folderUid as string,
|
||||||
},
|
},
|
||||||
depositor: {
|
depositor: {
|
||||||
uid: customerUid,
|
uid: customerUid as string,
|
||||||
},
|
},
|
||||||
document_type: {
|
document_type: {
|
||||||
uid: documentAsked[i]
|
uid: documentTypeUid
|
||||||
},
|
},
|
||||||
document_status: EDocumentStatus.ASKED
|
document_status: EDocumentStatus.ASKED,
|
||||||
|
file_uid: null,
|
||||||
};
|
};
|
||||||
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import Tag, { ETagColor, ETagVariant } from "@Front/Components/DesignSystem/Tag"
|
|||||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import useOpenable from "@Front/Hooks/useOpenable";
|
import useOpenable from "@Front/Hooks/useOpenable";
|
||||||
import { ArrowDownTrayIcon, EyeIcon, TrashIcon } from "@heroicons/react/24/outline";
|
import { ArrowDownTrayIcon, EyeIcon, TrashIcon, DocumentTextIcon } from "@heroicons/react/24/outline";
|
||||||
import { useMediaQuery } from "@mui/material";
|
import { useMediaQuery } from "@mui/material";
|
||||||
import { Document } from "le-coffre-resources/dist/Customer";
|
import { Document } from "le-coffre-resources/dist/Customer";
|
||||||
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
|
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
|
||||||
@ -24,6 +24,8 @@ import DeleteSentDocumentModal from "./DeleteSentDocumentModal";
|
|||||||
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||||
import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService";
|
import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService";
|
||||||
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
|
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
|
||||||
|
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||||
|
import PdfService, { CertificateData, Metadata } from "@Front/Services/PdfService";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
customerUid: string;
|
customerUid: string;
|
||||||
@ -64,6 +66,12 @@ export default function DocumentTables(props: IProps) {
|
|||||||
// FilterBy folder.uid & depositor.uid
|
// FilterBy folder.uid & depositor.uid
|
||||||
documents = documents.filter((document: any) => document.folder.uid === folderUid && document.depositor.uid === customerUid);
|
documents = documents.filter((document: any) => document.folder.uid === folderUid && document.depositor.uid === customerUid);
|
||||||
|
|
||||||
|
console.log('[DocumentTables] fetchDocuments: all documents for this folder/customer:', documents.map(doc => ({
|
||||||
|
uid: doc.uid,
|
||||||
|
status: doc.document_status,
|
||||||
|
type: doc.document_type?.name
|
||||||
|
})));
|
||||||
|
|
||||||
for (const document of documents) {
|
for (const document of documents) {
|
||||||
document.document_type = (await DocumentTypeService.getDocumentTypeByUid(document.document_type.uid)).processData;
|
document.document_type = (await DocumentTypeService.getDocumentTypeByUid(document.document_type.uid)).processData;
|
||||||
|
|
||||||
@ -154,6 +162,71 @@ export default function DocumentTables(props: IProps) {
|
|||||||
.catch((e) => console.warn(e));
|
.catch((e) => console.warn(e));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const onDownloadCertificate = useCallback(async (doc: any) => {
|
||||||
|
try {
|
||||||
|
console.log('[DocumentTables] onDownloadCertificate: doc', doc);
|
||||||
|
const certificateData: CertificateData = {
|
||||||
|
customer: {
|
||||||
|
firstName: doc.depositor.first_name || doc.depositor.firstName || "N/A",
|
||||||
|
lastName: doc.depositor.last_name || doc.depositor.lastName || "N/A",
|
||||||
|
postalAddress: doc.depositor.postal_address || doc.depositor.address || doc.depositor.postalAddress || "N/A",
|
||||||
|
email: doc.depositor.email || "N/A"
|
||||||
|
},
|
||||||
|
notary: {
|
||||||
|
name: "N/A"
|
||||||
|
},
|
||||||
|
folderUid: folderUid,
|
||||||
|
documentHash: "N/A",
|
||||||
|
metadata: {
|
||||||
|
fileName: "N/A",
|
||||||
|
isDeleted: false,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
commitmentId: "N/A",
|
||||||
|
createdAt: new Date(),
|
||||||
|
documentUid: "N/A",
|
||||||
|
documentType: "N/A"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch folder data to get office/notary information
|
||||||
|
// const folderProcess = await FolderService.getFolderByUid(folderUid, false, true);
|
||||||
|
// const folderData = folderProcess?.processData;
|
||||||
|
|
||||||
|
// console.log('[DocumentTables] onDownloadCertificate: folderData', folderData);
|
||||||
|
|
||||||
|
const documentProcesses = await DocumentService.getDocuments();
|
||||||
|
documentProcesses.filter((process: any) => process.processData.folder.uid === folderUid);
|
||||||
|
|
||||||
|
console.log('[DocumentTables] onDownloadCertificate: documentProcesses', documentProcesses);
|
||||||
|
|
||||||
|
for (const document of documentProcesses) {
|
||||||
|
for (const file of document.processData.files) {
|
||||||
|
await FileService.getFileByUid(file.uid).then((res: any) => {
|
||||||
|
console.log('[DocumentTables] onDownloadCertificate: fileProcess', res);
|
||||||
|
const hash = res.lastUpdatedFileState.pcd_commitment.file_blob;
|
||||||
|
certificateData.documentHash = hash;
|
||||||
|
const metadata: Metadata = {
|
||||||
|
fileName: res.processData.file_name,
|
||||||
|
isDeleted: false,
|
||||||
|
updatedAt: new Date(res.processData.updated_at),
|
||||||
|
commitmentId: res.lastUpdatedFileState.commited_in,
|
||||||
|
createdAt: new Date(res.processData.created_at),
|
||||||
|
documentUid: doc.document_type.uid,
|
||||||
|
documentType: doc.document_type.name
|
||||||
|
};
|
||||||
|
certificateData.metadata = metadata;
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[DocumentTables] onDownloadCertificate: certificateData', certificateData);
|
||||||
|
|
||||||
|
await PdfService.getInstance().downloadCertificate(certificateData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading certificate:', error);
|
||||||
|
}
|
||||||
|
}, [folderUid, customerUid]);
|
||||||
|
|
||||||
const askedDocuments: IRowProps[] = useMemo(
|
const askedDocuments: IRowProps[] = useMemo(
|
||||||
() =>
|
() =>
|
||||||
documents
|
documents
|
||||||
@ -233,8 +306,8 @@ export default function DocumentTables(props: IProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const validatedDocuments: IRowProps[] = useMemo(
|
const validatedDocuments: IRowProps[] = useMemo(
|
||||||
() =>
|
() => {
|
||||||
documents
|
const validated = documents
|
||||||
.map((document) => {
|
.map((document) => {
|
||||||
if (document.document_status !== EDocumentStatus.VALIDATED) return null;
|
if (document.document_status !== EDocumentStatus.VALIDATED) return null;
|
||||||
return {
|
return {
|
||||||
@ -266,13 +339,17 @@ export default function DocumentTables(props: IProps) {
|
|||||||
<IconButton icon={<EyeIcon />} />
|
<IconButton icon={<EyeIcon />} />
|
||||||
</Link>
|
</Link>
|
||||||
<IconButton onClick={() => onDownload(document)} icon={<ArrowDownTrayIcon />} />
|
<IconButton onClick={() => onDownload(document)} icon={<ArrowDownTrayIcon />} />
|
||||||
|
<IconButton onClick={() => onDownloadCertificate(document)} icon={<DocumentTextIcon />} />
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((document) => document !== null) as IRowProps[],
|
.filter((document) => document !== null) as IRowProps[];
|
||||||
[documents, folderUid, onDownload],
|
|
||||||
|
return validated;
|
||||||
|
},
|
||||||
|
[documents, folderUid, onDownload, onDownloadCertificate],
|
||||||
);
|
);
|
||||||
|
|
||||||
const refusedDocuments: IRowProps[] = useMemo(
|
const refusedDocuments: IRowProps[] = useMemo(
|
||||||
|
311
src/front/Services/PdfService/index.ts
Normal file
311
src/front/Services/PdfService/index.ts
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import jsPDF from 'jspdf';
|
||||||
|
|
||||||
|
export interface CustomerInfo {
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
postalAddress: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NotaryInfo {
|
||||||
|
name: string;
|
||||||
|
// Add more notary fields as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Metadata {
|
||||||
|
fileName: string,
|
||||||
|
createdAt: Date,
|
||||||
|
isDeleted: boolean,
|
||||||
|
updatedAt: Date,
|
||||||
|
commitmentId: string,
|
||||||
|
documentUid: string;
|
||||||
|
documentType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CertificateData {
|
||||||
|
customer: CustomerInfo;
|
||||||
|
notary: NotaryInfo;
|
||||||
|
folderUid: string;
|
||||||
|
documentHash: string;
|
||||||
|
metadata: Metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class PdfService {
|
||||||
|
private static instance: PdfService;
|
||||||
|
|
||||||
|
public static getInstance(): PdfService {
|
||||||
|
if (!PdfService.instance) {
|
||||||
|
PdfService.instance = new PdfService();
|
||||||
|
}
|
||||||
|
return PdfService.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a certificate PDF for a document
|
||||||
|
* @param certificateData - Data needed for the certificate
|
||||||
|
* @returns Promise<Blob> - The generated PDF as a blob
|
||||||
|
*/
|
||||||
|
public async generateCertificate(certificateData: CertificateData): Promise<Blob> {
|
||||||
|
try {
|
||||||
|
const doc = new jsPDF();
|
||||||
|
|
||||||
|
// Notary Information Section (Top Left)
|
||||||
|
doc.setFontSize(12);
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Notaire Validateur:', 20, 30);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(certificateData.notary.name, 20, 37);
|
||||||
|
|
||||||
|
// Customer Information Section (Top Right)
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Fournisseur de Document:', 120, 30);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(`${certificateData.customer.firstName} ${certificateData.customer.lastName}`, 120, 37);
|
||||||
|
doc.text(certificateData.customer.email, 120, 44);
|
||||||
|
doc.text(certificateData.customer.postalAddress, 120, 51);
|
||||||
|
|
||||||
|
// Centered Title
|
||||||
|
doc.setFontSize(20);
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Certificat de Validation de Document', 105, 80, { align: 'center' });
|
||||||
|
|
||||||
|
// Add a line separator
|
||||||
|
doc.setLineWidth(0.5);
|
||||||
|
doc.line(20, 90, 190, 90);
|
||||||
|
|
||||||
|
// Reset font for content
|
||||||
|
doc.setFontSize(12);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
|
||||||
|
let yPosition = 110;
|
||||||
|
|
||||||
|
// Document Information Section
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Informations du Document', 20, yPosition);
|
||||||
|
yPosition += 10;
|
||||||
|
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(`UID du Document: ${certificateData.metadata.documentUid}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`Type de Document: ${certificateData.metadata.documentType}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`UID du Dossier: ${certificateData.folderUid}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`Créé le: ${certificateData.metadata.createdAt.toLocaleDateString('fr-FR')}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`Nom du fichier: ${certificateData.metadata.fileName}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`ID de transaction: ${certificateData.metadata.commitmentId}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`Dernière modification: ${certificateData.metadata.updatedAt.toLocaleDateString('fr-FR')}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`Statut: ${certificateData.metadata.isDeleted ? 'Supprimé' : 'Actif'}`, 20, yPosition);
|
||||||
|
yPosition += 15;
|
||||||
|
|
||||||
|
// Document Hash Section
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Intégrité du Document', 20, yPosition);
|
||||||
|
yPosition += 10;
|
||||||
|
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(`Hash SHA256 du Document:`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
|
||||||
|
// Split hash into multiple lines if needed
|
||||||
|
const hash = certificateData.documentHash;
|
||||||
|
const maxWidth = 170; // Available width for text
|
||||||
|
const hashLines = this.splitTextToFit(doc, hash, maxWidth);
|
||||||
|
|
||||||
|
for (const line of hashLines) {
|
||||||
|
doc.text(line, 25, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
}
|
||||||
|
yPosition += 8; // Extra space after hash
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
const pageCount = doc.getNumberOfPages();
|
||||||
|
for (let i = 1; i <= pageCount; i++) {
|
||||||
|
doc.setPage(i);
|
||||||
|
doc.setFontSize(10);
|
||||||
|
doc.setFont('helvetica', 'italic');
|
||||||
|
doc.text(`Page ${i} sur ${pageCount}`, 105, 280, { align: 'center' });
|
||||||
|
doc.text(`Généré le ${new Date().toLocaleString('fr-FR')}`, 105, 285, { align: 'center' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc.output('blob');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating certificate:', error);
|
||||||
|
throw new Error('Failed to generate certificate');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split text to fit within a specified width
|
||||||
|
* @param doc - jsPDF document instance
|
||||||
|
* @param text - Text to split
|
||||||
|
* @param maxWidth - Maximum width in points
|
||||||
|
* @returns Array of text lines
|
||||||
|
*/
|
||||||
|
private splitTextToFit(doc: jsPDF, text: string, maxWidth: number): string[] {
|
||||||
|
const lines: string[] = [];
|
||||||
|
let currentLine = '';
|
||||||
|
|
||||||
|
// Split by words first
|
||||||
|
const words = text.split('');
|
||||||
|
|
||||||
|
for (const char of words) {
|
||||||
|
const testLine = currentLine + char;
|
||||||
|
const testWidth = doc.getTextWidth(testLine);
|
||||||
|
|
||||||
|
if (testWidth <= maxWidth) {
|
||||||
|
currentLine = testLine;
|
||||||
|
} else {
|
||||||
|
if (currentLine) {
|
||||||
|
lines.push(currentLine);
|
||||||
|
currentLine = char;
|
||||||
|
} else {
|
||||||
|
// If even a single character is too wide, force a break
|
||||||
|
lines.push(char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentLine) {
|
||||||
|
lines.push(currentLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download a certificate PDF
|
||||||
|
* @param certificateData - Data needed for the certificate
|
||||||
|
* @param filename - Optional filename for the download
|
||||||
|
*/
|
||||||
|
public async downloadCertificate(certificateData: CertificateData, filename?: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const pdfBlob = await this.generateCertificate(certificateData);
|
||||||
|
const defaultFilename = `certificate_${certificateData.metadata.documentUid}_${new Date().toISOString().split('T')[0]}.pdf`;
|
||||||
|
saveAs(pdfBlob, filename || defaultFilename);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading certificate:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate certificate for notary documents
|
||||||
|
* @param certificateData - Data needed for the certificate
|
||||||
|
* @returns Promise<Blob> - The generated PDF as a blob
|
||||||
|
*/
|
||||||
|
public async generateNotaryCertificate(certificateData: CertificateData): Promise<Blob> {
|
||||||
|
try {
|
||||||
|
const doc = new jsPDF();
|
||||||
|
|
||||||
|
// Notary Information Section (Top Left)
|
||||||
|
doc.setFontSize(12);
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Notaire Validateur:', 20, 30);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(certificateData.notary.name, 20, 37);
|
||||||
|
|
||||||
|
// Customer Information Section (Top Right)
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Fournisseur de Document:', 120, 30);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(`${certificateData.customer.firstName} ${certificateData.customer.lastName}`, 120, 37);
|
||||||
|
doc.text(certificateData.customer.email, 120, 44);
|
||||||
|
doc.text(certificateData.customer.postalAddress, 120, 51);
|
||||||
|
|
||||||
|
// Centered Title
|
||||||
|
doc.setFontSize(20);
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Certificat Notarial de Validation de Document', 105, 80, { align: 'center' });
|
||||||
|
|
||||||
|
// Add a line separator
|
||||||
|
doc.setLineWidth(0.5);
|
||||||
|
doc.line(20, 90, 190, 90);
|
||||||
|
|
||||||
|
// Reset font for content
|
||||||
|
doc.setFontSize(12);
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
|
||||||
|
let yPosition = 110;
|
||||||
|
|
||||||
|
// Document Information Section
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Informations du Document', 20, yPosition);
|
||||||
|
yPosition += 10;
|
||||||
|
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(`Identifiant du Document: ${certificateData.metadata.documentUid}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`Type de Document: ${certificateData.metadata.documentType}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`Numéro de dossier: ${certificateData.folderUid}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`Créé le: ${certificateData.metadata.createdAt.toLocaleDateString('fr-FR')}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`Nom du fichier: ${certificateData.metadata.fileName}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`ID de transaction: ${certificateData.metadata.commitmentId}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`Dernière modification: ${certificateData.metadata.updatedAt.toLocaleDateString('fr-FR')}`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
doc.text(`Statut: ${certificateData.metadata.isDeleted ? 'Supprimé' : 'Actif'}`, 20, yPosition);
|
||||||
|
yPosition += 15;
|
||||||
|
|
||||||
|
// Document Hash Section
|
||||||
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.text('Intégrité du Document', 20, yPosition);
|
||||||
|
yPosition += 10;
|
||||||
|
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.text(`Hash SHA256 du Document:`, 20, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
|
||||||
|
// Split hash into multiple lines if needed
|
||||||
|
const hash = certificateData.documentHash;
|
||||||
|
const maxWidth = 170; // Available width for text
|
||||||
|
const hashLines = this.splitTextToFit(doc, hash, maxWidth);
|
||||||
|
|
||||||
|
for (const line of hashLines) {
|
||||||
|
doc.text(line, 25, yPosition);
|
||||||
|
yPosition += 7;
|
||||||
|
}
|
||||||
|
yPosition += 8; // Extra space after hash
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
const pageCount = doc.getNumberOfPages();
|
||||||
|
for (let i = 1; i <= pageCount; i++) {
|
||||||
|
doc.setPage(i);
|
||||||
|
doc.setFontSize(10);
|
||||||
|
doc.setFont('helvetica', 'italic');
|
||||||
|
doc.text(`Page ${i} sur ${pageCount}`, 105, 280, { align: 'center' });
|
||||||
|
doc.text(`Certificat Notarial - Généré le ${new Date().toLocaleString('fr-FR')}`, 105, 285, { align: 'center' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc.output('blob');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating notary certificate:', error);
|
||||||
|
throw new Error('Failed to generate notary certificate');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download a notary certificate PDF
|
||||||
|
* @param certificateData - Data needed for the certificate
|
||||||
|
* @param filename - Optional filename for the download
|
||||||
|
*/
|
||||||
|
public async downloadNotaryCertificate(certificateData: CertificateData, filename?: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const pdfBlob = await this.generateNotaryCertificate(certificateData);
|
||||||
|
const defaultFilename = `notary_certificate_${certificateData.metadata.documentUid}_${new Date().toISOString().split('T')[0]}.pdf`;
|
||||||
|
saveAs(pdfBlob, filename || defaultFilename);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading notary certificate:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -113,61 +113,71 @@ export default class MessageBus {
|
|||||||
|
|
||||||
const publicDataDecoded: { [key: string]: any } = {};
|
const publicDataDecoded: { [key: string]: any } = {};
|
||||||
|
|
||||||
for (let stateId = 0; stateId < process.states.length - 1; stateId++) {
|
// We only care about the public data as they are in the last commited state
|
||||||
const state = process.states[stateId];
|
const processTip = process.states[process.states.length - 1].commited_in;
|
||||||
if (!state) {
|
const lastCommitedState = process.states.findLast((state: any) => state.commited_in !== processTip);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const publicDataEncoded = state.public_data;
|
if (!lastCommitedState) {
|
||||||
if (!publicDataEncoded) {
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of Object.keys(publicDataEncoded)) {
|
const publicDataEncoded = lastCommitedState.public_data;
|
||||||
publicDataDecoded[key] = await this.getPublicData(publicDataEncoded[key]);
|
if (!publicDataEncoded) {
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of Object.keys(publicDataEncoded)) {
|
||||||
|
publicDataDecoded[key] = await this.getPublicData(publicDataEncoded[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(publicDataDecoded['uid'] && publicDataDecoded['uid'] === uid && publicDataDecoded['utype'] && publicDataDecoded['utype'] === 'file')) {
|
if (!(publicDataDecoded['uid'] && publicDataDecoded['uid'] === uid && publicDataDecoded['utype'] && publicDataDecoded['utype'] === 'file')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We take the file in it's latest commited state
|
||||||
let file: any;
|
let file: any;
|
||||||
|
|
||||||
for (let stateId = 0; stateId < process.states.length - 1; stateId++) {
|
// Which is the last state that updated file_blob?
|
||||||
const lastState = process.states[stateId];
|
const lastUpdatedFileState = process.states.findLast((state: any) => state.pcd_commitment['file_blob']);
|
||||||
if (!lastState) {
|
|
||||||
|
if (!lastUpdatedFileState) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const processData = await this.getData(processId, lastUpdatedFileState.state_id);
|
||||||
|
const isEmpty = Object.keys(processData).length === 0;
|
||||||
|
if (isEmpty) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastStateId = lastState.state_id;
|
const publicDataEncoded = lastUpdatedFileState.public_data;
|
||||||
if (!lastStateId) {
|
if (!publicDataEncoded) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const publicDataDecoded: { [key: string]: any } = {};
|
||||||
try {
|
for (const key of Object.keys(publicDataEncoded)) {
|
||||||
const processData = await this.getData(processId, lastStateId);
|
publicDataDecoded[key] = await this.getPublicData(publicDataEncoded[key]);
|
||||||
const isEmpty = Object.keys(processData).length === 0;
|
|
||||||
if (isEmpty) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file) {
|
|
||||||
file = {
|
|
||||||
processId,
|
|
||||||
lastStateId,
|
|
||||||
processData,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
for (const key of Object.keys(processData)) {
|
|
||||||
file.processData[key] = processData[key];
|
|
||||||
}
|
|
||||||
file.lastStateId = lastStateId;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
file = {
|
||||||
|
processId,
|
||||||
|
lastUpdatedFileState,
|
||||||
|
processData,
|
||||||
|
publicDataDecoded,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
for (const key of Object.keys(processData)) {
|
||||||
|
file.processData[key] = processData[key];
|
||||||
|
}
|
||||||
|
file.lastUpdatedFileState = lastUpdatedFileState;
|
||||||
|
for (const key of Object.keys(publicDataDecoded)) {
|
||||||
|
file.publicDataDecoded[key] = publicDataDecoded[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
files.push(file);
|
files.push(file);
|
||||||
@ -647,7 +657,7 @@ export default class MessageBus {
|
|||||||
console.error('[MessageBus] sendMessage: iframe not found');
|
console.error('[MessageBus] sendMessage: iframe not found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('[MessageBus] sendMessage:', message);
|
// console.log('[MessageBus] sendMessage:', message);
|
||||||
iframe.contentWindow?.postMessage(message, targetOrigin);
|
iframe.contentWindow?.postMessage(message, targetOrigin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,7 +694,7 @@ export default class MessageBus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const message = event.data;
|
const message = event.data;
|
||||||
console.log('[MessageBus] handleMessage:', message);
|
// console.log('[MessageBus] handleMessage:', message);
|
||||||
|
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case 'LISTENING':
|
case 'LISTENING':
|
||||||
|
Loading…
x
Reference in New Issue
Block a user