Compare commits
8 Commits
00ce32ace0
...
22310a5846
Author | SHA1 | Date | |
---|---|---|---|
![]() |
22310a5846 | ||
![]() |
e87383c441 | ||
![]() |
0928d1e427 | ||
![]() |
95af55adf7 | ||
![]() |
e2169e9d29 | ||
![]() |
b781524b7f | ||
![]() |
07197ee289 | ||
c939065562 |
@ -1,4 +1,3 @@
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import MessageBus from 'src/sdk/MessageBus';
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import MessageBus from 'src/sdk/MessageBus';
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import MessageBus from 'src/sdk/MessageBus';
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import MessageBus from 'src/sdk/MessageBus';
|
||||
|
@ -1,8 +1,8 @@
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import MessageBus from 'src/sdk/MessageBus';
|
||||
import User from 'src/sdk/User';
|
||||
import { FileData } from '../../../../front/Api/Entities/types';
|
||||
|
||||
export default class FileService {
|
||||
|
||||
@ -10,7 +10,7 @@ export default class FileService {
|
||||
|
||||
private constructor() { }
|
||||
|
||||
public static createFile(fileData: any, validatorId: string): Promise<any> {
|
||||
public static createFile(fileData: FileData, validatorId: string): Promise<any> {
|
||||
const ownerId = User.getInstance().getPairingId()!;
|
||||
|
||||
const processData: any = {
|
||||
@ -82,7 +82,7 @@ export default class FileService {
|
||||
return this.messageBus.getFileByUid(uid);
|
||||
}
|
||||
|
||||
public static updateFile(process: any, newData: any): Promise<void> {
|
||||
public static updateFile(process: any, newData: Partial<FileData> & { isDeleted?: string }): Promise<void> {
|
||||
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
|
||||
this.messageBus.updateProcess(process.processId, { updated_at: new Date().toISOString(), ...newData }, [], null).then((processUpdated: any) => {
|
||||
const newStateId: string = processUpdated.diffs[0]?.state_id;
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import MessageBus from 'src/sdk/MessageBus';
|
||||
|
45
src/common/Api/LeCoffreApi/sdk/Loader/LoaderService.ts
Normal file
45
src/common/Api/LeCoffreApi/sdk/Loader/LoaderService.ts
Normal file
@ -0,0 +1,45 @@
|
||||
class LoaderService {
|
||||
private static instance: LoaderService;
|
||||
private _isVisible: boolean = false;
|
||||
private _callbacks: Array<(isVisible: boolean) => void> = [];
|
||||
|
||||
private constructor() { }
|
||||
|
||||
public static getInstance(): LoaderService {
|
||||
if (!LoaderService.instance) {
|
||||
LoaderService.instance = new LoaderService();
|
||||
}
|
||||
return LoaderService.instance;
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
this._isVisible = true;
|
||||
this._notifySubscribers();
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
this._isVisible = false;
|
||||
this._notifySubscribers();
|
||||
}
|
||||
|
||||
public get isVisible(): boolean {
|
||||
return this._isVisible;
|
||||
}
|
||||
|
||||
public subscribe(callback: (isVisible: boolean) => void): () => void {
|
||||
this._callbacks.push(callback);
|
||||
|
||||
// Return unsubscribe function
|
||||
return () => {
|
||||
this._callbacks = this._callbacks.filter(cb => cb !== callback);
|
||||
};
|
||||
}
|
||||
|
||||
private _notifySubscribers(): void {
|
||||
this._callbacks.forEach(callback => {
|
||||
callback(this._isVisible);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default LoaderService;
|
38
src/common/Api/LeCoffreApi/sdk/Loader/classes.module.scss
Normal file
38
src/common/Api/LeCoffreApi/sdk/Loader/classes.module.scss
Normal file
@ -0,0 +1,38 @@
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
|
||||
.loader-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loader {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: white;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-top: 1rem;
|
||||
font-size: 1rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
animation: spin 1s infinite linear;
|
||||
}
|
37
src/common/Api/LeCoffreApi/sdk/Loader/index.tsx
Normal file
37
src/common/Api/LeCoffreApi/sdk/Loader/index.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { ArrowPathIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
import LoaderService from "./LoaderService";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
// Composant fusionné qui gère à la fois l'abonnement au service et l'affichage
|
||||
const Loader: React.FC = () => {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// S'abonner aux changements d'état du loader
|
||||
const unsubscribe = LoaderService.getInstance().subscribe((visible) => {
|
||||
setIsVisible(visible);
|
||||
});
|
||||
|
||||
// Nettoyage de l'abonnement
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Ne rien afficher si le loader n'est pas visible
|
||||
if (!isVisible) return null;
|
||||
|
||||
// Affichage du loader avec overlay
|
||||
return (
|
||||
<div className={classes["loader-container"]}>
|
||||
<div className={classes["loader"]}>
|
||||
<ArrowPathIcon className={classes["spinner"]} width={40} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Loader;
|
@ -1,4 +1,3 @@
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import MessageBus from 'src/sdk/MessageBus';
|
||||
|
109
src/common/Api/LeCoffreApi/sdk/RoleService.ts
Normal file
109
src/common/Api/LeCoffreApi/sdk/RoleService.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import MessageBus from 'src/sdk/MessageBus';
|
||||
import User from 'src/sdk/User';
|
||||
|
||||
export default class RoleService {
|
||||
|
||||
private static readonly messageBus: MessageBus = MessageBus.getInstance();
|
||||
|
||||
private constructor() { }
|
||||
|
||||
public static createRole(roleData: any, validatorId: string): Promise<any> {
|
||||
const ownerId = User.getInstance().getPairingId()!;
|
||||
|
||||
const processData: any = {
|
||||
uid: uuidv4(),
|
||||
utype: 'role',
|
||||
isDeleted: 'false',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
...roleData,
|
||||
};
|
||||
|
||||
const privateFields: string[] = Object.keys(processData);
|
||||
privateFields.splice(privateFields.indexOf('uid'), 1);
|
||||
privateFields.splice(privateFields.indexOf('utype'), 1);
|
||||
privateFields.splice(privateFields.indexOf('isDeleted'), 1);
|
||||
|
||||
const roles: any = {
|
||||
demiurge: {
|
||||
members: [...[ownerId], validatorId],
|
||||
validation_rules: [],
|
||||
storages: []
|
||||
},
|
||||
owner: {
|
||||
members: [ownerId],
|
||||
validation_rules: [
|
||||
{
|
||||
quorum: 0.5,
|
||||
fields: [...privateFields, 'roles', 'uid', 'utype'],
|
||||
min_sig_member: 1,
|
||||
},
|
||||
],
|
||||
storages: []
|
||||
},
|
||||
validator: {
|
||||
members: [validatorId],
|
||||
validation_rules: [
|
||||
{
|
||||
quorum: 0.5,
|
||||
fields: ['idCertified', 'roles'],
|
||||
min_sig_member: 1,
|
||||
},
|
||||
{
|
||||
quorum: 0.0,
|
||||
fields: [...privateFields],
|
||||
min_sig_member: 0,
|
||||
},
|
||||
],
|
||||
storages: []
|
||||
},
|
||||
apophis: {
|
||||
members: [ownerId],
|
||||
validation_rules: [],
|
||||
storages: []
|
||||
}
|
||||
};
|
||||
|
||||
return new Promise<any>((resolve: (processCreated: any) => void, reject: (error: string) => void) => {
|
||||
this.messageBus.createProcess(processData, privateFields, roles).then((processCreated: any) => {
|
||||
this.messageBus.notifyUpdate(processCreated.processId, processCreated.process.states[0].state_id).then(() => {
|
||||
this.messageBus.validateState(processCreated.processId, processCreated.process.states[0].state_id).then((_stateValidated: any) => {
|
||||
resolve(processCreated);
|
||||
}).catch(reject);
|
||||
}).catch(reject);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
public static getRoles(): Promise<any[]> {
|
||||
return this.messageBus.getProcessesDecoded((publicValues: any) => publicValues['uid'] && publicValues['utype'] && publicValues['utype'] === 'role' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false');
|
||||
}
|
||||
|
||||
public static getRoleByUid(uid: string): Promise<any> {
|
||||
return new Promise<any>((resolve: (process: any) => void, reject: (error: string) => void) => {
|
||||
this.messageBus.getProcessesDecoded((publicValues: any) => publicValues['uid'] && publicValues['uid'] === uid && publicValues['utype'] && publicValues['utype'] === 'role' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false').then(async (processes: any[]) => {
|
||||
if (processes.length === 0) {
|
||||
resolve(null);
|
||||
} else {
|
||||
const process: any = processes[0];
|
||||
resolve(process);
|
||||
}
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
public static updateRole(process: any, newData: any): Promise<void> {
|
||||
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
|
||||
this.messageBus.updateProcess(process.processId, { updated_at: new Date().toISOString(), ...newData }, [], null).then((processUpdated: any) => {
|
||||
const newStateId: string = processUpdated.diffs[0]?.state_id;
|
||||
this.messageBus.notifyUpdate(process.processId, newStateId).then(() => {
|
||||
this.messageBus.validateState(process.processId, newStateId).then((_stateValidated) => {
|
||||
resolve();
|
||||
}).catch(reject);
|
||||
}).catch(reject);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
}
|
2
src/front/Api/Entities/index.ts
Normal file
2
src/front/Api/Entities/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './types';
|
||||
export * from './rule';
|
21
src/front/Api/Entities/types.ts
Normal file
21
src/front/Api/Entities/types.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* FileBlob interface representing a file's binary data and metadata
|
||||
* Used for file transmission and storage in the application
|
||||
*/
|
||||
export interface FileBlob {
|
||||
/** MIME type of the file (e.g., "application/pdf", "image/jpeg") */
|
||||
type: string;
|
||||
/** Binary data of the file as Uint8Array */
|
||||
data: Uint8Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* FileData interface representing a complete file object with blob and metadata
|
||||
* Used when creating or updating files in the system
|
||||
*/
|
||||
export interface FileData {
|
||||
/** The file blob containing type and binary data */
|
||||
file_blob: FileBlob;
|
||||
/** The name of the file */
|
||||
file_name: string;
|
||||
}
|
@ -213,9 +213,9 @@ export default function DragAndDrop(props: IProps) {
|
||||
</div>
|
||||
{documentFiles.length > 0 && (
|
||||
<div className={classes["documents"]}>
|
||||
{documentFiles.map((documentFile) => (
|
||||
{documentFiles.map((documentFile, index) => (
|
||||
<DocumentFileElement
|
||||
key={documentFile.id}
|
||||
key={documentFile.uid || `${documentFile.id}-${index}`}
|
||||
isLoading={documentFile.isLoading}
|
||||
file={documentFile.file}
|
||||
onRemove={() => handleRemove(documentFile)}
|
||||
|
@ -31,6 +31,7 @@ export default function Navigation() {
|
||||
*/
|
||||
const anchors = [] as any[];
|
||||
|
||||
/* TODO: review
|
||||
try {
|
||||
for (const anchor of anchors) {
|
||||
await OfficeFolderAnchors.getInstance().getByUid(anchor.folder?.uid as string);
|
||||
@ -38,6 +39,7 @@ export default function Navigation() {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
*/
|
||||
}, []);
|
||||
|
||||
const getNotifications = useCallback(async () => {
|
||||
|
@ -7,6 +7,8 @@ import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDas
|
||||
import { OfficeRole } from "le-coffre-resources/dist/Notary";
|
||||
import OfficeRoles, { IGetRolesParams } from "@Front/Api/LeCoffreApi/Admin/OfficeRoles/OfficeRoles";
|
||||
|
||||
import RoleService from "src/common/Api/LeCoffreApi/sdk/RoleService";
|
||||
|
||||
type IProps = IPropsDashboardWithList;
|
||||
|
||||
export default function DefaultRoleDashboard(props: IProps) {
|
||||
@ -23,7 +25,13 @@ export default function DefaultRoleDashboard(props: IProps) {
|
||||
.get(query)
|
||||
.then((roles) => setRoles(roles));
|
||||
*/
|
||||
setRoles([]);
|
||||
|
||||
RoleService.getRoles().then((processes: any[]) => {
|
||||
if (processes.length > 0) {
|
||||
const roles: any[] = processes.map((process: any) => process.processData);
|
||||
setRoles(roles);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onSelectedBlock = (block: IBlock) => {
|
||||
@ -37,10 +45,10 @@ export default function DefaultRoleDashboard(props: IProps) {
|
||||
blocks={
|
||||
roles
|
||||
? roles.map((role) => ({
|
||||
id: role.uid!,
|
||||
primaryText: role.name,
|
||||
isActive: role.uid === roleUid,
|
||||
}))
|
||||
id: role.uid!,
|
||||
primaryText: role.name,
|
||||
isActive: role.uid === roleUid,
|
||||
}))
|
||||
: []
|
||||
}
|
||||
bottomButton={{
|
||||
|
@ -9,6 +9,7 @@ import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||
|
||||
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
|
||||
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||
import { FileBlob, FileData } from "@Front/Api/Entities/types";
|
||||
|
||||
type IProps = {
|
||||
document: any;
|
||||
@ -39,12 +40,12 @@ export default function DepositDocumentComponent(props: IProps) {
|
||||
const arrayBuffer = event.target.result as ArrayBuffer;
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
|
||||
const fileBlob: any = {
|
||||
const fileBlob: FileBlob = {
|
||||
type: file.type,
|
||||
data: uint8Array
|
||||
};
|
||||
|
||||
const fileData: any = {
|
||||
const fileData: FileData = {
|
||||
file_blob: fileBlob,
|
||||
file_name: file.name
|
||||
};
|
||||
@ -82,9 +83,9 @@ export default function DepositDocumentComponent(props: IProps) {
|
||||
(fileUid: string) => {
|
||||
return new Promise<void>(
|
||||
(resolve: () => void) => {
|
||||
FileService.getFileByUid(fileUid).then((process: any) => {
|
||||
if (process) {
|
||||
FileService.updateFile(process, { isDeleted: 'true' }).then(() => {
|
||||
FileService.getFileByUid(fileUid).then((res: any) => {
|
||||
if (res) {
|
||||
FileService.updateFile(res.processId, { isDeleted: 'true' }).then(() => {
|
||||
DocumentService.getDocumentByUid(document.uid!).then((process: any) => {
|
||||
if (process) {
|
||||
const document: any = process.processData;
|
||||
|
@ -7,6 +7,7 @@ import { DocumentNotary } from "le-coffre-resources/dist/Notary";
|
||||
import Image from "next/image";
|
||||
import { NextRouter, useRouter } from "next/router";
|
||||
import React from "react";
|
||||
import { FileBlob } from "@Front/Api/Entities/types";
|
||||
|
||||
import BasePage from "../../Base";
|
||||
import classes from "./classes.module.scss";
|
||||
@ -25,7 +26,7 @@ type IState = {
|
||||
isValidateModalVisible: boolean;
|
||||
refuseText: string;
|
||||
selectedFileIndex: number;
|
||||
selectedFile: any;
|
||||
selectedFile: { uid: string; file_name: string; file_blob: FileBlob } | null;
|
||||
documentNotary: DocumentNotary | null;
|
||||
fileBlob: Blob | null;
|
||||
isLoading: boolean;
|
||||
@ -132,7 +133,7 @@ class ViewDocumentsNotaryClass extends BasePage<IPropsClass, IState> {
|
||||
{
|
||||
documentNotary,
|
||||
selectedFileIndex: 0,
|
||||
selectedFile: documentNotary.files![0]!,
|
||||
selectedFile: documentNotary.files![0] as any,
|
||||
isLoading: false,
|
||||
},
|
||||
() => {
|
||||
|
@ -5,6 +5,7 @@ import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||
import DefaultDeedTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDeedTypeDashboard";
|
||||
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||
import Module from "@Front/Config/Module";
|
||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||
import { DeedType, Office } from "le-coffre-resources/dist/Admin";
|
||||
@ -15,6 +16,7 @@ import classes from "./classes.module.scss";
|
||||
import { validateOrReject, ValidationError } from "class-validator";
|
||||
|
||||
import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
type IProps = {};
|
||||
export default function DeedTypesCreate(props: IProps) {
|
||||
@ -52,7 +54,13 @@ export default function DeedTypesCreate(props: IProps) {
|
||||
};
|
||||
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||
|
||||
LoaderService.getInstance().show();
|
||||
DeedTypeService.createDeedType(deedTypeData, validatorId).then((processCreated: any) => {
|
||||
ToasterService.getInstance().success({
|
||||
title: "Succès !",
|
||||
description: "Type d'acte créé avec succès"
|
||||
});
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
router.push(
|
||||
Module.getInstance()
|
||||
.get()
|
||||
|
@ -14,6 +14,9 @@ import { ValidationError } from "class-validator";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
export default function DeedTypesEdit() {
|
||||
const router = useRouter();
|
||||
let { deedTypeUid } = router.query;
|
||||
@ -27,12 +30,14 @@ export default function DeedTypesEdit() {
|
||||
setHasChanged(false);
|
||||
async function getDeedType() {
|
||||
if (!deedTypeUid) return;
|
||||
const deedType = await DeedTypes.getInstance().getByUid(deedTypeUid as string, {
|
||||
q: {
|
||||
document_types: true,
|
||||
},
|
||||
LoaderService.getInstance().show();
|
||||
DeedTypeService.getDeedTypeByUid(deedTypeUid as string).then((process: any) => {
|
||||
if (process) {
|
||||
const deedType: any = process.processData;
|
||||
setDeedTypeSelected(deedType);
|
||||
}
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
});
|
||||
setDeedTypeSelected(deedType);
|
||||
}
|
||||
|
||||
getDeedType();
|
||||
@ -57,19 +62,19 @@ export default function DeedTypesEdit() {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await DeedTypes.getInstance().put(
|
||||
deedTypeUid as string,
|
||||
DeedType.hydrate<DeedType>({
|
||||
uid: deedTypeUid as string,
|
||||
name: values["name"],
|
||||
description: values["description"],
|
||||
}),
|
||||
);
|
||||
router.push(
|
||||
Module.getInstance()
|
||||
.get()
|
||||
.modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeUid as string),
|
||||
);
|
||||
LoaderService.getInstance().show();
|
||||
DeedTypeService.getDeedTypeByUid(deedTypeUid as string).then((process: any) => {
|
||||
if (process) {
|
||||
DeedTypeService.updateDeedType(process, { name: values["name"], description: values["description"] }).then(() => {
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
router.push(
|
||||
Module.getInstance()
|
||||
.get()
|
||||
.modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeUid as string),
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (validationErrors) {
|
||||
if (!Array.isArray(validationErrors)) return;
|
||||
setValidationError(validationErrors as ValidationError[]);
|
||||
|
@ -20,6 +20,7 @@ import classes from "./classes.module.scss";
|
||||
|
||||
import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService";
|
||||
import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
type IProps = {};
|
||||
export default function DeedTypesInformations(props: IProps) {
|
||||
@ -50,9 +51,11 @@ export default function DeedTypesInformations(props: IProps) {
|
||||
}, []);
|
||||
|
||||
const deleteDeedType = useCallback(async () => {
|
||||
LoaderService.getInstance().show();
|
||||
DeedTypeService.getDeedTypeByUid(deedTypeUid as string).then((process: any) => {
|
||||
if (process) {
|
||||
DeedTypeService.updateDeedType(process, { archived_at: new Date().toISOString() }).then(() => {
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
router.push(Module.getInstance().get().modules.pages.DeedTypes.props.path);
|
||||
});
|
||||
}
|
||||
@ -103,6 +106,7 @@ export default function DeedTypesInformations(props: IProps) {
|
||||
);
|
||||
|
||||
const saveDocumentTypes = useCallback(() => {
|
||||
LoaderService.getInstance().show();
|
||||
DeedTypeService.getDeedTypeByUid(deedTypeUid as string, false).then((process: any) => {
|
||||
if (process) {
|
||||
const deedType: any = process.processData;
|
||||
@ -115,6 +119,7 @@ export default function DeedTypesInformations(props: IProps) {
|
||||
.forEach((selectedDocument: any) => document_types.push(selectedDocument));
|
||||
|
||||
DeedTypeService.updateDeedType(process, { document_types: document_types }).then(() => {
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
closeSaveModal();
|
||||
});
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
|
||||
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||
import Module from "@Front/Config/Module";
|
||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||
import { validateOrReject, ValidationError } from "class-validator";
|
||||
@ -14,6 +15,7 @@ import { useCallback, useState } from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
type IProps = {};
|
||||
export default function DocumentTypesCreate(props: IProps) {
|
||||
@ -45,7 +47,13 @@ export default function DocumentTypesCreate(props: IProps) {
|
||||
};
|
||||
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||
|
||||
LoaderService.getInstance().show();
|
||||
DocumentTypeService.createDocumentType(documentData, validatorId).then((processCreated: any) => {
|
||||
ToasterService.getInstance().success({
|
||||
title: "Succès !",
|
||||
description: "Type de document créé avec succès"
|
||||
});
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
router.push(
|
||||
Module.getInstance()
|
||||
.get()
|
||||
|
@ -1,4 +1,3 @@
|
||||
import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes";
|
||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import Form from "@Front/Components/DesignSystem/Form";
|
||||
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||
@ -13,6 +12,9 @@ import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
export default function DocumentTypesEdit() {
|
||||
const router = useRouter();
|
||||
let { documentTypeUid } = router.query;
|
||||
@ -23,10 +25,14 @@ export default function DocumentTypesEdit() {
|
||||
useEffect(() => {
|
||||
async function getDocumentType() {
|
||||
if (!documentTypeUid) return;
|
||||
const documentType = await DocumentTypes.getInstance().getByUid(documentTypeUid as string, {
|
||||
_count: true,
|
||||
LoaderService.getInstance().show();
|
||||
DocumentTypeService.getDocumentTypeByUid(documentTypeUid as string).then((process: any) => {
|
||||
if (process) {
|
||||
const documentType: any = process.processData;
|
||||
setDocumentTypeSelected(documentType);
|
||||
}
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
});
|
||||
setDocumentTypeSelected(documentType);
|
||||
}
|
||||
|
||||
getDocumentType();
|
||||
@ -46,16 +52,22 @@ export default function DocumentTypesEdit() {
|
||||
setValidationError(validationErrors as ValidationError[]);
|
||||
return;
|
||||
}
|
||||
|
||||
const documentTypeUpdated = await DocumentTypes.getInstance().put(documentTypeUid as string, documentToUpdate);
|
||||
router.push(
|
||||
Module.getInstance()
|
||||
.get()
|
||||
.modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path.replace(
|
||||
"[uid]",
|
||||
documentTypeUpdated.uid ?? "",
|
||||
),
|
||||
);
|
||||
LoaderService.getInstance().show();
|
||||
DocumentTypeService.getDocumentTypeByUid(documentTypeUid as string).then((process: any) => {
|
||||
if (process) {
|
||||
DocumentTypeService.updateDocumentType(process, values).then(() => {
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
router.push(
|
||||
Module.getInstance()
|
||||
.get()
|
||||
.modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path.replace(
|
||||
"[uid]",
|
||||
documentTypeUid as string ?? "",
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (validationErrors: Array<ValidationError> | any) {
|
||||
if (!Array.isArray(validationErrors)) return;
|
||||
setValidationError(validationErrors as ValidationError[]);
|
||||
|
@ -7,6 +7,7 @@ import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||
import BackArrow from "@Front/Components/Elements/BackArrow";
|
||||
import Module from "@Front/Config/Module";
|
||||
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||
import { ValidationError } from "class-validator";
|
||||
import { ECivility } from "le-coffre-resources/dist/Customer/Contact";
|
||||
import { Contact, Customer } from "le-coffre-resources/dist/Notary";
|
||||
@ -17,6 +18,7 @@ import classes from "./classes.module.scss";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService";
|
||||
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||
|
||||
@ -78,6 +80,7 @@ export default function AddClientToFolder(props: IProps) {
|
||||
};
|
||||
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||
|
||||
LoaderService.getInstance().show();
|
||||
CustomerService.createCustomer(customerData, validatorId).then((processCreated: any) => {
|
||||
FolderService.getFolderByUid(folderUid as string, false, false).then((process: any) => {
|
||||
if (process) {
|
||||
@ -88,6 +91,11 @@ export default function AddClientToFolder(props: IProps) {
|
||||
customers.push(processCreated.processData.uid);
|
||||
|
||||
FolderService.updateFolder(process, { customers: customers }).then(() => {
|
||||
ToasterService.getInstance().success({
|
||||
title: "Succès !",
|
||||
description: "Client ajouté avec succès au dossier"
|
||||
});
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
router.push(`/folders/${folderUid}`);
|
||||
});
|
||||
}
|
||||
@ -99,11 +107,17 @@ export default function AddClientToFolder(props: IProps) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
LoaderService.getInstance().show();
|
||||
FolderService.getFolderByUid(folderUid as string, false, false).then((process: any) => {
|
||||
if (process) {
|
||||
const customers: any[] = customersToLink.map((customer: any) => customer.uid);
|
||||
|
||||
FolderService.updateFolder(process, { customers: customers }).then(() => {
|
||||
ToasterService.getInstance().success({
|
||||
title: "Succès !",
|
||||
description: selectedCustomers.length > 1 ? "Clients associés avec succès au dossier" : "Client associé avec succès au dossier"
|
||||
});
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
router.push(`/folders/${folderUid}`);
|
||||
});
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import backgroundImage from "@Assets/images/background_refonte.svg";
|
||||
|
||||
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||
import { DocumentData } from "../FolderInformation/ClientView/DocumentTables/types";
|
||||
|
||||
export default function AskDocuments() {
|
||||
const router = useRouter();
|
||||
@ -61,17 +62,21 @@ export default function AskDocuments() {
|
||||
const documentAsked: [] = values["document_types"] as [];
|
||||
|
||||
for (let i = 0; i < documentAsked.length; i++) {
|
||||
const documentTypeUid = documentAsked[i];
|
||||
if (!documentTypeUid) continue;
|
||||
|
||||
const documentData: any = {
|
||||
folder: {
|
||||
uid: folderUid,
|
||||
uid: folderUid as string,
|
||||
},
|
||||
depositor: {
|
||||
uid: customerUid,
|
||||
uid: customerUid as string,
|
||||
},
|
||||
document_type: {
|
||||
uid: documentAsked[i]
|
||||
uid: documentTypeUid
|
||||
},
|
||||
document_status: EDocumentStatus.ASKED
|
||||
document_status: EDocumentStatus.ASKED,
|
||||
file_uid: null,
|
||||
};
|
||||
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||
|
||||
|
@ -8,6 +8,7 @@ import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||
import BackArrow from "@Front/Components/Elements/BackArrow";
|
||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||
@ -22,6 +23,7 @@ import classes from "./classes.module.scss";
|
||||
|
||||
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||
import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
export default function CreateFolder(): JSX.Element {
|
||||
/**
|
||||
@ -95,7 +97,13 @@ export default function CreateFolder(): JSX.Element {
|
||||
status: EFolderStatus.LIVE
|
||||
};
|
||||
|
||||
LoaderService.getInstance().show();
|
||||
FolderService.createFolder(folderData, [], []).then((processCreated: any) => {
|
||||
ToasterService.getInstance().success({
|
||||
title: "Succès !",
|
||||
description: "Dossier créé avec succès"
|
||||
});
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
const folderUid: string = processCreated.processData.uid;
|
||||
router.push(`/folders/${folderUid}`);
|
||||
});
|
||||
|
@ -14,6 +14,7 @@ import classes from "./classes.module.scss";
|
||||
import DeleteCustomerModal from "./DeleteCustomerModal";
|
||||
|
||||
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
type IProps = {
|
||||
customer: Customer;
|
||||
@ -37,6 +38,7 @@ export default function ClientBox(props: IProps) {
|
||||
|
||||
const handleDelete = useCallback(
|
||||
(customerUid: string) => {
|
||||
LoaderService.getInstance().show();
|
||||
DocumentService.getDocuments().then((processes: any[]) => {
|
||||
if (processes.length > 0) {
|
||||
let documents: any[] = processes.map((process: any) => process.processData);
|
||||
|
@ -1,8 +1,12 @@
|
||||
import Modal from "@Front/Components/DesignSystem/Modal";
|
||||
import React from "react";
|
||||
import { FileBlob } from "@Front/Api/Entities/types";
|
||||
|
||||
type IProps = {
|
||||
file: any;
|
||||
file: {
|
||||
uid: string;
|
||||
file_blob: FileBlob;
|
||||
};
|
||||
url: string;
|
||||
isOpen: boolean;
|
||||
onClose?: () => void;
|
||||
|
@ -8,7 +8,7 @@ import Tag, { ETagColor, ETagVariant } from "@Front/Components/DesignSystem/Tag"
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import Module from "@Front/Config/Module";
|
||||
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 { Document } from "le-coffre-resources/dist/Customer";
|
||||
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
|
||||
@ -24,6 +24,9 @@ import DeleteSentDocumentModal from "./DeleteSentDocumentModal";
|
||||
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||
import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService";
|
||||
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";
|
||||
import MessageBus from "src/sdk/MessageBus";
|
||||
|
||||
type IProps = {
|
||||
customerUid: string;
|
||||
@ -64,6 +67,12 @@ export default function DocumentTables(props: IProps) {
|
||||
// FilterBy folder.uid & depositor.uid
|
||||
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) {
|
||||
document.document_type = (await DocumentTypeService.getDocumentTypeByUid(document.document_type.uid)).processData;
|
||||
|
||||
@ -154,6 +163,77 @@ export default function DocumentTables(props: IProps) {
|
||||
.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",
|
||||
merkleProof: "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;
|
||||
|
||||
MessageBus.getInstance().generateMerkleProof(res.lastUpdatedFileState, 'file_blob').then((proof) => {
|
||||
console.log('[DocumentTables] onDownloadCertificate: proof', proof);
|
||||
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,
|
||||
merkleProof: proof
|
||||
};
|
||||
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(
|
||||
() =>
|
||||
documents
|
||||
@ -233,8 +313,8 @@ export default function DocumentTables(props: IProps) {
|
||||
);
|
||||
|
||||
const validatedDocuments: IRowProps[] = useMemo(
|
||||
() =>
|
||||
documents
|
||||
() => {
|
||||
const validated = documents
|
||||
.map((document) => {
|
||||
if (document.document_status !== EDocumentStatus.VALIDATED) return null;
|
||||
return {
|
||||
@ -266,13 +346,17 @@ export default function DocumentTables(props: IProps) {
|
||||
<IconButton icon={<EyeIcon />} />
|
||||
</Link>
|
||||
<IconButton onClick={() => onDownload(document)} icon={<ArrowDownTrayIcon />} />
|
||||
<IconButton onClick={() => onDownloadCertificate(document)} icon={<DocumentTextIcon />} />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
};
|
||||
})
|
||||
.filter((document) => document !== null) as IRowProps[],
|
||||
[documents, folderUid, onDownload],
|
||||
.filter((document) => document !== null) as IRowProps[];
|
||||
|
||||
return validated;
|
||||
},
|
||||
[documents, folderUid, onDownload, onDownloadCertificate],
|
||||
);
|
||||
|
||||
const refusedDocuments: IRowProps[] = useMemo(
|
||||
|
@ -16,6 +16,7 @@ import DocumentTables from "./DocumentTables";
|
||||
import EmailReminder from "./EmailReminder";
|
||||
|
||||
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
type IProps = {
|
||||
folder: OfficeFolder;
|
||||
@ -62,7 +63,7 @@ export default function ClientView(props: IProps) {
|
||||
const handleClientDelete = useCallback(
|
||||
(customerUid: string) => {
|
||||
if (!folder.uid) return;
|
||||
|
||||
LoaderService.getInstance().show();
|
||||
FolderService.getFolderByUid(folder.uid, false, false).then((process: any) => {
|
||||
if (process) {
|
||||
const folder: any = process.processData;
|
||||
@ -71,6 +72,7 @@ export default function ClientView(props: IProps) {
|
||||
const customers = folder.customers.filter((uid: string) => uid !== customerUid);
|
||||
|
||||
FolderService.updateFolder(process, { customers: customers }).then(() => {
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import { useRouter } from "next/router";
|
||||
import React, { useCallback } from "react";
|
||||
|
||||
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
type IProps = {
|
||||
isOpen: boolean;
|
||||
@ -24,9 +25,13 @@ export default function DeleteFolderModal(props: IProps) {
|
||||
|
||||
return new Promise<void>(
|
||||
(resolve: () => void) => {
|
||||
LoaderService.getInstance().show();
|
||||
FolderService.getFolderByUid(folder.uid!).then((process: any) => {
|
||||
if (process) {
|
||||
FolderService.updateFolder(process, { isDeleted: 'true' }).then(() => resolve());
|
||||
FolderService.updateFolder(process, { isDeleted: 'true' }).then(() => {
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
|
@ -24,6 +24,7 @@ import AnchoringProcessingInfo from "./elements/AnchoringProcessingInfo";
|
||||
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||
import NoteService from "src/common/Api/LeCoffreApi/sdk/NoteService";
|
||||
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
export enum AnchorStatus {
|
||||
"VERIFIED_ON_CHAIN" = "VERIFIED_ON_CHAIN",
|
||||
@ -108,6 +109,7 @@ export default function FolderInformation(props: IProps) {
|
||||
*/
|
||||
|
||||
// TODO: review
|
||||
LoaderService.getInstance().show();
|
||||
return FolderService.getFolderByUid(folderUid).then(async (process: any) => {
|
||||
if (process) {
|
||||
const folder: any = process.processData;
|
||||
@ -141,6 +143,7 @@ export default function FolderInformation(props: IProps) {
|
||||
});
|
||||
|
||||
setFolder(folder);
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
}
|
||||
});
|
||||
}, [folderUid]);
|
||||
|
@ -17,6 +17,7 @@ import { useCallback, useEffect, useState } from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
export default function UpdateClient() {
|
||||
const router = useRouter();
|
||||
@ -35,12 +36,12 @@ export default function UpdateClient() {
|
||||
useEffect(() => {
|
||||
const fetchCustomer = async () => {
|
||||
try {
|
||||
LoaderService.getInstance().show();
|
||||
CustomerService.getCustomerByUid(customerUid as string).then((process: any) => {
|
||||
if (process) {
|
||||
const customer: any = process.processData;
|
||||
if (customer) {
|
||||
setCustomer(customer);
|
||||
}
|
||||
setCustomer(customer);
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
@ -70,10 +71,13 @@ export default function UpdateClient() {
|
||||
|
||||
try {
|
||||
await contact.validateOrReject?.({ groups: ["createCustomer"], forbidUnknownValues: false });
|
||||
|
||||
LoaderService.getInstance().show();
|
||||
CustomerService.getCustomerByUid(customerUid as string).then((process: any) => {
|
||||
if (process) {
|
||||
// TODO: review - address
|
||||
CustomerService.updateCustomer(process, { contact: contact }).then(() => {
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
router.push(backwardPath);
|
||||
});
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||
import { isArray } from "class-validator";
|
||||
|
||||
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
export default function UpdateFolderMetadata() {
|
||||
const router = useRouter();
|
||||
@ -50,12 +51,17 @@ export default function UpdateFolderMetadata() {
|
||||
}
|
||||
|
||||
try {
|
||||
await Folders.getInstance().put(folderUid, newValues);
|
||||
const url = Module.getInstance()
|
||||
.get()
|
||||
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", folderUid);
|
||||
|
||||
router.push(url);
|
||||
LoaderService.getInstance().show();
|
||||
FolderService.getFolderByUid(folderUid).then((process: any) => {
|
||||
if (process) {
|
||||
FolderService.updateFolder(process, { ...values, deed: { uid: values["deed"] } }).then(() => {
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
router.push(Module.getInstance()
|
||||
.get()
|
||||
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", folderUid));
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (backError) {
|
||||
if (!Array.isArray(backError)) return;
|
||||
setValidationError(backError);
|
||||
@ -65,24 +71,12 @@ export default function UpdateFolderMetadata() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!folderUid || isArray(folderUid)) return;
|
||||
|
||||
/* TODO: review
|
||||
const query = {
|
||||
q: {
|
||||
deed: { include: { deed_type: true } },
|
||||
office: true,
|
||||
customers: { include: { contact: true } },
|
||||
},
|
||||
};
|
||||
Folders.getInstance()
|
||||
.getByUid(folderUid, query)
|
||||
.then((folder) => setSelectedFolder(folder));
|
||||
*/
|
||||
|
||||
LoaderService.getInstance().show();
|
||||
FolderService.getFolderByUid(folderUid).then((process: any) => {
|
||||
if (process) {
|
||||
const folder: any = process.processData;
|
||||
setSelectedFolder(folder);
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
}
|
||||
});
|
||||
}, [folderUid]);
|
||||
|
@ -19,6 +19,7 @@ import MessageBox from "@Front/Components/Elements/MessageBox";
|
||||
|
||||
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
|
||||
import { FileBlob } from "@Front/Api/Entities/types";
|
||||
|
||||
type IProps = {};
|
||||
type IPropsClass = {
|
||||
@ -32,7 +33,7 @@ type IState = {
|
||||
isValidateModalVisible: boolean;
|
||||
refuseText: string;
|
||||
selectedFileIndex: number;
|
||||
selectedFile: any; // File | null; TODO: review
|
||||
selectedFile: { uid: string; file_name: string; file_blob: FileBlob } | null;
|
||||
validatedPercentage: number;
|
||||
document: Document | null;
|
||||
fileBlob: Blob | null;
|
||||
@ -237,6 +238,8 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
||||
|
||||
private async getFilePreview(): Promise<void> {
|
||||
try {
|
||||
if (!this.state.selectedFile) return;
|
||||
|
||||
const fileBlob: Blob = new Blob([this.state.selectedFile.file_blob.data], { type: this.state.selectedFile.file_blob.type });
|
||||
this.setState({
|
||||
fileBlob,
|
||||
@ -247,7 +250,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
||||
}
|
||||
|
||||
private downloadFile() {
|
||||
if (!this.state.fileBlob) return;
|
||||
if (!this.state.fileBlob || !this.state.selectedFile) return;
|
||||
|
||||
const url = URL.createObjectURL(this.state.fileBlob);
|
||||
const a = document.createElement('a');
|
||||
|
@ -5,6 +5,7 @@ import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||
import DefaultRolesDashboard from "@Front/Components/LayoutTemplates/DefaultRoleDashboard";
|
||||
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||
import Module from "@Front/Config/Module";
|
||||
import { Office, OfficeRole } from "le-coffre-resources/dist/Admin";
|
||||
import { useRouter } from "next/router";
|
||||
@ -16,6 +17,9 @@ import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
|
||||
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
|
||||
import { ValidationError } from "class-validator";
|
||||
|
||||
import RoleService from "src/common/Api/LeCoffreApi/sdk/RoleService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
type IProps = {};
|
||||
export default function RolesCreate(props: IProps) {
|
||||
const [hasChanged, setHasChanged] = useState<boolean>(false);
|
||||
@ -26,12 +30,17 @@ export default function RolesCreate(props: IProps) {
|
||||
const onSubmitHandler = useCallback(
|
||||
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
||||
const jwt = JwtService.getInstance().decodeJwt();
|
||||
|
||||
// TODO: review
|
||||
const officeId = 'demo_notary_office_id'; //jwt?.office_Id;
|
||||
|
||||
const officeRole = OfficeRole.hydrate<OfficeRole>({
|
||||
name: values["name"],
|
||||
office: Office.hydrate<Office>({
|
||||
uid: jwt?.office_Id,
|
||||
uid: officeId,
|
||||
}),
|
||||
});
|
||||
|
||||
try {
|
||||
await officeRole.validateOrReject?.({ groups: ["createOfficeRole"], forbidUnknownValues: true });
|
||||
} catch (validationErrors: Array<ValidationError> | any) {
|
||||
@ -39,6 +48,29 @@ export default function RolesCreate(props: IProps) {
|
||||
setValidationError(validationErrors as ValidationError[]);
|
||||
return;
|
||||
}
|
||||
|
||||
const roleData: any = {
|
||||
name: values["name"],
|
||||
office: {
|
||||
uid: officeId,
|
||||
}
|
||||
};
|
||||
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||
|
||||
LoaderService.getInstance().show();
|
||||
RoleService.createRole(roleData, validatorId).then((processCreated: any) => {
|
||||
if (processCreated) {
|
||||
ToasterService.getInstance().success({
|
||||
title: "Succès !",
|
||||
description: "Rôle créé avec succès"
|
||||
});
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
const role: any = processCreated.processData;
|
||||
router.push(Module.getInstance().get().modules.pages.Roles.pages.RolesInformations.props.path.replace("[uid]", role.uid!));
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
try {
|
||||
const role = await OfficeRoles.getInstance().post(
|
||||
OfficeRole.hydrate<OfficeRole>({
|
||||
@ -55,6 +87,7 @@ export default function RolesCreate(props: IProps) {
|
||||
setValidationError(validationErrors as ValidationError[]);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
},
|
||||
[router],
|
||||
);
|
||||
|
@ -13,6 +13,9 @@ import React from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
import RulesGroups from "@Front/Api/LeCoffreApi/Admin/RulesGroups/RulesGroups";
|
||||
|
||||
import RoleService from "src/common/Api/LeCoffreApi/sdk/RoleService";
|
||||
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||
|
||||
type RuleGroupsCheckbox = RulesGroup & {
|
||||
checked: boolean;
|
||||
};
|
||||
@ -39,24 +42,60 @@ export default function RolesInformations() {
|
||||
setSelectAll(false);
|
||||
async function getUser() {
|
||||
if (!roleUid) return;
|
||||
|
||||
/*
|
||||
const role = await OfficeRoles.getInstance().getByUid(roleUid as string, {
|
||||
q: {
|
||||
rules: true,
|
||||
},
|
||||
});
|
||||
*/
|
||||
|
||||
LoaderService.getInstance().show();
|
||||
const role: any = await new Promise<any>((resolve: (role: any) => void) => {
|
||||
RoleService.getRoleByUid(roleUid as string).then((process: any) => {
|
||||
if (process) {
|
||||
const role: any = process.processData;
|
||||
resolve(role);
|
||||
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
/* TODO: review
|
||||
const rulesGroups = await RulesGroups.getInstance().get({
|
||||
include: {
|
||||
rules: true,
|
||||
},
|
||||
});
|
||||
*/
|
||||
const rulesGroups: RulesGroup[] = [
|
||||
{
|
||||
uid: 'toto',
|
||||
name: 'toto',
|
||||
rules: [
|
||||
{
|
||||
uid: 'toto',
|
||||
name: 'toto',
|
||||
label: 'toto',
|
||||
namespace: 'toto',
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
}
|
||||
],
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
}
|
||||
];
|
||||
|
||||
if (!role) return;
|
||||
setRoleSelected(role);
|
||||
|
||||
// TODO: review
|
||||
if (!role.rules) return;
|
||||
const rulesCheckboxes = rulesGroups
|
||||
.map((ruleGroup) => {
|
||||
if (ruleGroup.rules?.every((rule) => role.rules?.find((r) => r.uid === rule.uid))) {
|
||||
if (ruleGroup.rules?.every((rule) => role.rules?.find((r: any) => r.uid === rule.uid))) {
|
||||
return { ...ruleGroup, checked: true };
|
||||
}
|
||||
return { ...ruleGroup, checked: false };
|
||||
|
312
src/front/Services/PdfService/index.ts
Normal file
312
src/front/Services/PdfService/index.ts
Normal file
@ -0,0 +1,312 @@
|
||||
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;
|
||||
merkleProof: 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,8 @@ import getConfig from "next/config";
|
||||
import { GoogleTagManager } from "@next/third-parties/google";
|
||||
import { hotjar } from "react-hotjar";
|
||||
|
||||
import Loader from "src/common/Api/LeCoffreApi/sdk/Loader";
|
||||
|
||||
import IframeReference from "src/sdk/IframeReference";
|
||||
import Iframe from "src/sdk/Iframe";
|
||||
import MessageBus from "src/sdk/MessageBus";
|
||||
@ -115,6 +117,7 @@ const MyApp = (({
|
||||
</Component>
|
||||
}
|
||||
{isConnected && <Iframe />}
|
||||
<Loader />
|
||||
</>
|
||||
);
|
||||
}) as AppType;
|
||||
|
@ -6,6 +6,7 @@ import EventBus from './EventBus';
|
||||
import User from './User';
|
||||
|
||||
import MapUtils from './MapUtils';
|
||||
import { FileBlob } from '../front/Api/Entities/types';
|
||||
|
||||
export default class MessageBus {
|
||||
private static instance: MessageBus;
|
||||
@ -113,61 +114,71 @@ export default class MessageBus {
|
||||
|
||||
const publicDataDecoded: { [key: string]: any } = {};
|
||||
|
||||
for (let stateId = 0; stateId < process.states.length - 1; stateId++) {
|
||||
const state = process.states[stateId];
|
||||
if (!state) {
|
||||
continue;
|
||||
}
|
||||
// We only care about the public data as they are in the last commited state
|
||||
const processTip = process.states[process.states.length - 1].commited_in;
|
||||
const lastCommitedState = process.states.findLast((state: any) => state.commited_in !== processTip);
|
||||
|
||||
const publicDataEncoded = state.public_data;
|
||||
if (!publicDataEncoded) {
|
||||
continue;
|
||||
}
|
||||
if (!lastCommitedState) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(publicDataEncoded)) {
|
||||
publicDataDecoded[key] = await this.getPublicData(publicDataEncoded[key]);
|
||||
}
|
||||
const publicDataEncoded = lastCommitedState.public_data;
|
||||
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')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We take the file in it's latest commited state
|
||||
let file: any;
|
||||
|
||||
for (let stateId = 0; stateId < process.states.length - 1; stateId++) {
|
||||
const lastState = process.states[stateId];
|
||||
if (!lastState) {
|
||||
// Which is the last state that updated file_blob?
|
||||
const lastUpdatedFileState = process.states.findLast((state: any) => state.pcd_commitment['file_blob']);
|
||||
|
||||
if (!lastUpdatedFileState) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const processData = await this.getData(processId, lastUpdatedFileState.state_id);
|
||||
const isEmpty = Object.keys(processData).length === 0;
|
||||
if (isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const lastStateId = lastState.state_id;
|
||||
if (!lastStateId) {
|
||||
const publicDataEncoded = lastUpdatedFileState.public_data;
|
||||
if (!publicDataEncoded) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const processData = await this.getData(processId, lastStateId);
|
||||
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);
|
||||
const publicDataDecoded: { [key: string]: any } = {};
|
||||
for (const key of Object.keys(publicDataEncoded)) {
|
||||
publicDataDecoded[key] = await this.getPublicData(publicDataEncoded[key]);
|
||||
}
|
||||
|
||||
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);
|
||||
@ -557,6 +568,84 @@ export default class MessageBus {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a document file using SHA-256
|
||||
* @param fileBlob - The file blob to hash
|
||||
* @returns Promise<string> - The SHA-256 hash of the file
|
||||
*/
|
||||
public hashDocument(fileBlob: FileBlob, commitedIn: string): Promise<string> {
|
||||
return new Promise<string>((resolve: (hash: string) => void, reject: (error: string) => void) => {
|
||||
this.checkToken().then(() => {
|
||||
const messageId = `HASH_VALUE_${uuidv4()}`;
|
||||
|
||||
const unsubscribe = EventBus.getInstance().on('VALUE_HASHED', (responseId: string, hash: string) => {
|
||||
if (responseId !== messageId) {
|
||||
return;
|
||||
}
|
||||
unsubscribe();
|
||||
resolve(hash);
|
||||
});
|
||||
|
||||
const unsubscribeError = EventBus.getInstance().on('ERROR_VALUE_HASHED', (responseId: string, error: string) => {
|
||||
if (responseId !== messageId) {
|
||||
return;
|
||||
}
|
||||
unsubscribeError();
|
||||
reject(error);
|
||||
});
|
||||
|
||||
const user = User.getInstance();
|
||||
const accessToken = user.getAccessToken()!;
|
||||
|
||||
const label = 'file_blob';
|
||||
|
||||
this.sendMessage({
|
||||
type: 'HASH_VALUE',
|
||||
accessToken,
|
||||
commitedIn,
|
||||
label,
|
||||
fileBlob,
|
||||
messageId
|
||||
});
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
public generateMerkleProof(processState: any, attributeName: string): Promise<any> {
|
||||
return new Promise<any>((resolve: (proof: any) => void, reject: (error: string) => void) => {
|
||||
this.checkToken().then(() => {
|
||||
const messageId = `GET_MERKLE_PROOF_${uuidv4()}`;
|
||||
|
||||
const unsubscribe = EventBus.getInstance().on('MERKLE_PROOF_RETRIEVED', (responseId: string, proof: any) => {
|
||||
if (responseId !== messageId) {
|
||||
return;
|
||||
}
|
||||
unsubscribe();
|
||||
resolve(proof);
|
||||
});
|
||||
|
||||
const unsubscribeError = EventBus.getInstance().on('ERROR_MERKLE_PROOF_RETRIEVED', (responseId: string, error: string) => {
|
||||
if (responseId !== messageId) {
|
||||
return;
|
||||
}
|
||||
unsubscribeError();
|
||||
reject(error);
|
||||
});
|
||||
|
||||
const user = User.getInstance();
|
||||
const accessToken = user.getAccessToken()!;
|
||||
|
||||
this.sendMessage({
|
||||
type: 'GET_MERKLE_PROOF',
|
||||
accessToken,
|
||||
processState,
|
||||
attributeName,
|
||||
messageId
|
||||
});
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
private validateToken(): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve: (isValid: boolean) => void, reject: (error: string) => void) => {
|
||||
const messageId = `VALIDATE_TOKEN_${uuidv4()}`;
|
||||
@ -647,7 +736,7 @@ export default class MessageBus {
|
||||
console.error('[MessageBus] sendMessage: iframe not found');
|
||||
return;
|
||||
}
|
||||
console.log('[MessageBus] sendMessage:', message);
|
||||
// console.log('[MessageBus] sendMessage:', message);
|
||||
iframe.contentWindow?.postMessage(message, targetOrigin);
|
||||
}
|
||||
|
||||
@ -684,7 +773,7 @@ export default class MessageBus {
|
||||
}
|
||||
|
||||
const message = event.data;
|
||||
console.log('[MessageBus] handleMessage:', message);
|
||||
// console.log('[MessageBus] handleMessage:', message);
|
||||
|
||||
switch (message.type) {
|
||||
case 'LISTENING':
|
||||
@ -746,6 +835,14 @@ export default class MessageBus {
|
||||
this.doHandleMessage(message.messageId, 'STATE_VALIDATED', message, (message: any) => message.validatedProcess);
|
||||
break;
|
||||
|
||||
case 'VALUE_HASHED': // HASH_VALUE
|
||||
this.doHandleMessage(message.messageId, 'VALUE_HASHED', message, (message: any) => message.hash);
|
||||
break;
|
||||
|
||||
case 'MERKLE_PROOF_RETRIEVED': // GENERATE_MERKLE_PROOF
|
||||
this.doHandleMessage(message.messageId, 'MERKLE_PROOF_RETRIEVED', message, (message: any) => message.proof);
|
||||
break;
|
||||
|
||||
case 'ERROR':
|
||||
console.error('Error:', message);
|
||||
this.errors[message.messageId] = message.error;
|
||||
|
Loading…
x
Reference in New Issue
Block a user