Compare commits
26 Commits
main
...
certificat
Author | SHA1 | Date | |
---|---|---|---|
![]() |
758a32a4d6 | ||
![]() |
6f6d3e8de5 | ||
![]() |
c87ad8fed5 | ||
![]() |
56fe4fbcd3 | ||
![]() |
d94fd9e017 | ||
![]() |
4f76d43f38 | ||
![]() |
7bfe3bcad2 | ||
![]() |
c178b60d51 | ||
![]() |
a450d80600 | ||
![]() |
095c4efba2 | ||
![]() |
723322cc0a | ||
![]() |
d17f4aa8d9 | ||
![]() |
7a137dbe2f | ||
96ed1e50fa | |||
e4c440d6df | |||
d3e13bd801 | |||
ca5a59c51a | |||
c939065562 | |||
6ed6682824 | |||
39c14ff490 | |||
f9abdd31cd | |||
d7e27bbb9a | |||
ccc0a1620c | |||
5ad6465b74 | |||
7435a33fe0 | |||
65f67993ba |
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"[typescript]": {
|
"[typescript]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||||
},
|
},
|
||||||
"[json]": {
|
"[json]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
@ -32,6 +32,6 @@
|
|||||||
"rust-client.disableRustup": true,
|
"rust-client.disableRustup": true,
|
||||||
"rust-client.autoStartRls": false,
|
"rust-client.autoStartRls": false,
|
||||||
"[typescriptreact]": {
|
"[typescriptreact]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,6 +16,7 @@ const nextConfig = {
|
|||||||
NEXT_PUBLIC_DOCAPOSTE_API_URL: process.env.NEXT_PUBLIC_DOCAPOSTE_API_URL,
|
NEXT_PUBLIC_DOCAPOSTE_API_URL: process.env.NEXT_PUBLIC_DOCAPOSTE_API_URL,
|
||||||
NEXT_PUBLIC_HOTJAR_SITE_ID: process.env.NEXT_PUBLIC_HOTJAR_SITE_ID,
|
NEXT_PUBLIC_HOTJAR_SITE_ID: process.env.NEXT_PUBLIC_HOTJAR_SITE_ID,
|
||||||
NEXT_PUBLIC_HOTJAR_VERSION: process.env.NEXT_PUBLIC_HOTJAR_VERSION,
|
NEXT_PUBLIC_HOTJAR_VERSION: process.env.NEXT_PUBLIC_HOTJAR_VERSION,
|
||||||
|
NEXT_PUBLIC_4NK_URL: process.env.NEXT_PUBLIC_4NK_URL,
|
||||||
},
|
},
|
||||||
|
|
||||||
serverRuntimeConfig: {
|
serverRuntimeConfig: {
|
||||||
@ -31,6 +32,7 @@ const nextConfig = {
|
|||||||
NEXT_PUBLIC_DOCAPOSTE_API_URL: process.env.NEXT_PUBLIC_DOCAPOSTE_API_URL,
|
NEXT_PUBLIC_DOCAPOSTE_API_URL: process.env.NEXT_PUBLIC_DOCAPOSTE_API_URL,
|
||||||
NEXT_PUBLIC_HOTJAR_SITE_ID: process.env.NEXT_PUBLIC_HOTJAR_SITE_ID,
|
NEXT_PUBLIC_HOTJAR_SITE_ID: process.env.NEXT_PUBLIC_HOTJAR_SITE_ID,
|
||||||
NEXT_PUBLIC_HOTJAR_VERSION: process.env.NEXT_PUBLIC_HOTJAR_VERSION,
|
NEXT_PUBLIC_HOTJAR_VERSION: process.env.NEXT_PUBLIC_HOTJAR_VERSION,
|
||||||
|
NEXT_PUBLIC_4NK_URL: process.env.NEXT_PUBLIC_4NK_URL,
|
||||||
},
|
},
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
@ -46,6 +48,7 @@ const nextConfig = {
|
|||||||
NEXT_PUBLIC_DOCAPOSTE_API_URL: process.env.NEXT_PUBLIC_DOCAPOSTE_API_URL,
|
NEXT_PUBLIC_DOCAPOSTE_API_URL: process.env.NEXT_PUBLIC_DOCAPOSTE_API_URL,
|
||||||
NEXT_PUBLIC_HOTJAR_SITE_ID: process.env.NEXT_PUBLIC_HOTJAR_SITE_ID,
|
NEXT_PUBLIC_HOTJAR_SITE_ID: process.env.NEXT_PUBLIC_HOTJAR_SITE_ID,
|
||||||
NEXT_PUBLIC_HOTJAR_VERSION: process.env.NEXT_PUBLIC_HOTJAR_VERSION,
|
NEXT_PUBLIC_HOTJAR_VERSION: process.env.NEXT_PUBLIC_HOTJAR_VERSION,
|
||||||
|
NEXT_PUBLIC_4NK_URL: process.env.NEXT_PUBLIC_4NK_URL,
|
||||||
},
|
},
|
||||||
|
|
||||||
// webpack: config => {
|
// webpack: config => {
|
||||||
|
4376
package-lock.json
generated
4376
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -30,8 +30,9 @@
|
|||||||
"heroicons": "^2.1.5",
|
"heroicons": "^2.1.5",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.167",
|
"le-coffre-resources": "file:../lecoffre-ressources",
|
||||||
"next": "^14.2.3",
|
"next": "^14.2.3",
|
||||||
|
"pdf-lib": "^1.17.1",
|
||||||
"prettier": "^2.8.7",
|
"prettier": "^2.8.7",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
109
src/common/Api/LeCoffreApi/sdk/CustomerService.ts
Normal file
109
src/common/Api/LeCoffreApi/sdk/CustomerService.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 CustomerService {
|
||||||
|
|
||||||
|
private static readonly messageBus: MessageBus = MessageBus.getInstance();
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static createCustomer(customerData: any, validatorId: string): Promise<any> {
|
||||||
|
const ownerId = User.getInstance().getPairingId()!;
|
||||||
|
|
||||||
|
const processData: any = {
|
||||||
|
uid: uuidv4(),
|
||||||
|
utype: 'customer',
|
||||||
|
isDeleted: 'false',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
...customerData,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 getCustomers(): Promise<any[]> {
|
||||||
|
return this.messageBus.getProcessesDecoded((publicValues: any) => publicValues['uid'] && publicValues['utype'] && publicValues['utype'] === 'customer' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getCustomerByUid(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'] === 'customer' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false').then((processes: any[]) => {
|
||||||
|
if (processes.length === 0) {
|
||||||
|
resolve(null);
|
||||||
|
} else {
|
||||||
|
const process: any = processes[0];
|
||||||
|
resolve(process);
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static updateCustomer(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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
125
src/common/Api/LeCoffreApi/sdk/DeedTypeService.ts
Normal file
125
src/common/Api/LeCoffreApi/sdk/DeedTypeService.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import MessageBus from 'src/sdk/MessageBus';
|
||||||
|
import User from 'src/sdk/User';
|
||||||
|
|
||||||
|
import DocumentTypeService from './DocumentTypeService';
|
||||||
|
|
||||||
|
export default class DeedTypeService {
|
||||||
|
|
||||||
|
private static readonly messageBus: MessageBus = MessageBus.getInstance();
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static createDeedType(deedTypeData: any, validatorId: string): Promise<any> {
|
||||||
|
const ownerId = User.getInstance().getPairingId()!;
|
||||||
|
|
||||||
|
const processData: any = {
|
||||||
|
uid: uuidv4(),
|
||||||
|
utype: 'deedType',
|
||||||
|
isDeleted: 'false',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
...deedTypeData,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 getDeedTypes(): Promise<any[]> {
|
||||||
|
return this.messageBus.getProcessesDecoded((publicValues: any) => publicValues['uid'] && publicValues['utype'] && publicValues['utype'] === 'deedType' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getDeedTypeByUid(uid: string, includeDocumentTypes: boolean = true): 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'] === 'deedType' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false').then(async (processes: any[]) => {
|
||||||
|
if (processes.length === 0) {
|
||||||
|
resolve(null);
|
||||||
|
} else {
|
||||||
|
const process: any = processes[0];
|
||||||
|
|
||||||
|
if (includeDocumentTypes && process.processData.document_types && process.processData.document_types.length > 0) {
|
||||||
|
process.processData.document_types = await new Promise<any[]>(async (resolve: (document_types: any[]) => void) => {
|
||||||
|
let document_types: any[] = [];
|
||||||
|
for (const document_type of process.processData.document_types) {
|
||||||
|
const p: any = await DocumentTypeService.getDocumentTypeByUid(document_type.uid);
|
||||||
|
document_types.push(p.processData);
|
||||||
|
}
|
||||||
|
// Remove duplicates
|
||||||
|
document_types = document_types.filter((item: any, index: number) => document_types.findIndex((t: any) => t.uid === item.uid) === index);
|
||||||
|
resolve(document_types);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(process);
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static updateDeedType(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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
109
src/common/Api/LeCoffreApi/sdk/DocumentService.ts
Normal file
109
src/common/Api/LeCoffreApi/sdk/DocumentService.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 DocumentService {
|
||||||
|
|
||||||
|
private static readonly messageBus: MessageBus = MessageBus.getInstance();
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static createDocument(documentData: any, validatorId: string): Promise<any> {
|
||||||
|
const ownerId = User.getInstance().getPairingId()!;
|
||||||
|
|
||||||
|
const processData: any = {
|
||||||
|
uid: uuidv4(),
|
||||||
|
utype: 'document',
|
||||||
|
isDeleted: 'false',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
...documentData,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 getDocuments(): Promise<any[]> {
|
||||||
|
return this.messageBus.getProcessesDecoded((publicValues: any) => publicValues['uid'] && publicValues['utype'] && publicValues['utype'] === 'document' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getDocumentByUid(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'] === 'document' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false').then((processes: any[]) => {
|
||||||
|
if (processes.length === 0) {
|
||||||
|
resolve(null);
|
||||||
|
} else {
|
||||||
|
const process: any = processes[0];
|
||||||
|
resolve(process);
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static updateDocument(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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
109
src/common/Api/LeCoffreApi/sdk/DocumentTypeService.ts
Normal file
109
src/common/Api/LeCoffreApi/sdk/DocumentTypeService.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 DocumentTypeService {
|
||||||
|
|
||||||
|
private static readonly messageBus: MessageBus = MessageBus.getInstance();
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static createDocumentType(documentTypeData: any, validatorId: string): Promise<any> {
|
||||||
|
const ownerId = User.getInstance().getPairingId()!;
|
||||||
|
|
||||||
|
const processData: any = {
|
||||||
|
uid: uuidv4(),
|
||||||
|
utype: 'documentType',
|
||||||
|
isDeleted: 'false',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
...documentTypeData,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 getDocumentTypes(): Promise<any[]> {
|
||||||
|
return this.messageBus.getProcessesDecoded((publicValues: any) => publicValues['uid'] && publicValues['utype'] && publicValues['utype'] === 'documentType' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getDocumentTypeByUid(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'] === 'documentType' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false').then((processes: any[]) => {
|
||||||
|
if (processes.length === 0) {
|
||||||
|
resolve(null);
|
||||||
|
} else {
|
||||||
|
const process: any = processes[0];
|
||||||
|
resolve(process);
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static updateDocumentType(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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
97
src/common/Api/LeCoffreApi/sdk/FileService.ts
Normal file
97
src/common/Api/LeCoffreApi/sdk/FileService.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
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 {
|
||||||
|
|
||||||
|
private static readonly messageBus: MessageBus = MessageBus.getInstance();
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static createFile(fileData: FileData, validatorId: string): Promise<any> {
|
||||||
|
const ownerId = User.getInstance().getPairingId()!;
|
||||||
|
|
||||||
|
const processData: any = {
|
||||||
|
uid: uuidv4(),
|
||||||
|
utype: 'file',
|
||||||
|
isDeleted: 'false',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
...fileData,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 getFileByUid(uid: string): Promise<any> {
|
||||||
|
return this.messageBus.getFileByUid(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
this.messageBus.notifyUpdate(process.processId, newStateId).then(() => {
|
||||||
|
this.messageBus.validateState(process.processId, newStateId).then((_stateValidated) => {
|
||||||
|
resolve();
|
||||||
|
}).catch(reject);
|
||||||
|
}).catch(reject);
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
137
src/common/Api/LeCoffreApi/sdk/FolderService.ts
Normal file
137
src/common/Api/LeCoffreApi/sdk/FolderService.ts
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import MessageBus from 'src/sdk/MessageBus';
|
||||||
|
import User from 'src/sdk/User';
|
||||||
|
|
||||||
|
import CustomerService from './CustomerService';
|
||||||
|
import DeedTypeService from './DeedTypeService';
|
||||||
|
|
||||||
|
export default class FolderService {
|
||||||
|
|
||||||
|
private static readonly messageBus: MessageBus = MessageBus.getInstance();
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static createFolder(folderData: any, stakeholdersId: string[], customersId: string[]): Promise<any> {
|
||||||
|
const ownerId = User.getInstance().getPairingId()!;
|
||||||
|
|
||||||
|
const processData: any = {
|
||||||
|
uid: uuidv4(),
|
||||||
|
utype: 'folder',
|
||||||
|
isDeleted: 'false',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
...folderData,
|
||||||
|
};
|
||||||
|
|
||||||
|
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],
|
||||||
|
validation_rules: [],
|
||||||
|
storages: []
|
||||||
|
},
|
||||||
|
owner: {
|
||||||
|
members: [ownerId],
|
||||||
|
validation_rules: [
|
||||||
|
{
|
||||||
|
quorum: 0.5,
|
||||||
|
fields: [...privateFields, 'roles', 'uid', 'utype'],
|
||||||
|
min_sig_member: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
storages: []
|
||||||
|
},
|
||||||
|
stakeholders: {
|
||||||
|
members: stakeholdersId,
|
||||||
|
validation_rules: [
|
||||||
|
{
|
||||||
|
quorum: 0.5,
|
||||||
|
fields: ['documents', 'motes'],
|
||||||
|
min_sig_member: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
storages: []
|
||||||
|
},
|
||||||
|
customers: {
|
||||||
|
members: customersId,
|
||||||
|
validation_rules: [
|
||||||
|
{
|
||||||
|
quorum: 0.0,
|
||||||
|
fields: privateFields,
|
||||||
|
min_sig_member: 0.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 getFolders(): Promise<any[]> {
|
||||||
|
return this.messageBus.getProcessesDecoded((publicValues: any) => publicValues['uid'] && publicValues['utype'] && publicValues['utype'] === 'folder' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getFolderByUid(uid: string, includeCustomers: boolean = true, includeDeedType: boolean = true): 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'] === 'folder' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false').then(async (processes: any[]) => {
|
||||||
|
if (processes.length === 0) {
|
||||||
|
resolve(null);
|
||||||
|
} else {
|
||||||
|
const process: any = processes[0];
|
||||||
|
|
||||||
|
if (includeCustomers && process.processData.customers && process.processData.customers.length > 0) {
|
||||||
|
process.processData.customers = await new Promise<any[]>(async (resolve: (customers: any[]) => void) => {
|
||||||
|
const customers: any[] = [];
|
||||||
|
for (const uid of process.processData.customers) {
|
||||||
|
const p: any = await CustomerService.getCustomerByUid(uid);
|
||||||
|
customers.push(p.processData);
|
||||||
|
}
|
||||||
|
resolve(customers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeDeedType && process.processData.deed && process.processData.deed.deed_type) {
|
||||||
|
const p: any = await DeedTypeService.getDeedTypeByUid(process.processData.deed.deed_type.uid);
|
||||||
|
process.processData.deed.deed_type = p.processData;
|
||||||
|
// Remove duplicates
|
||||||
|
process.processData.deed.document_types = p.processData.document_types.filter((item: any, index: number) => p.processData.document_types.findIndex((t: any) => t.uid === item.uid) === index);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(process);
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static updateFolder(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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
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;
|
109
src/common/Api/LeCoffreApi/sdk/NoteService.ts
Normal file
109
src/common/Api/LeCoffreApi/sdk/NoteService.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 NoteService {
|
||||||
|
|
||||||
|
private static readonly messageBus: MessageBus = MessageBus.getInstance();
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static createNote(noteData: any, validatorId: string): Promise<any> {
|
||||||
|
const ownerId = User.getInstance().getPairingId()!;
|
||||||
|
|
||||||
|
const processData: any = {
|
||||||
|
uid: uuidv4(),
|
||||||
|
utype: 'note',
|
||||||
|
isDeleted: 'false',
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
...noteData,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 getNotes(): Promise<any[]> {
|
||||||
|
return this.messageBus.getProcessesDecoded((publicValues: any) => publicValues['uid'] && publicValues['utype'] && publicValues['utype'] === 'note' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getNoteByUid(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'] === 'note' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false').then((processes: any[]) => {
|
||||||
|
if (processes.length === 0) {
|
||||||
|
resolve(null);
|
||||||
|
} else {
|
||||||
|
const process: any = processes[0];
|
||||||
|
resolve(process);
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static updateNote(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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -41,8 +41,11 @@ export default class Auth extends BaseApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getIdnotJwt(autorizationCode: string | string[]): Promise<{ accessToken: string; refreshToken: string }> {
|
public async getIdnotJwt(autorizationCode: string | string[]): Promise<{ accessToken: string; refreshToken: string }> {
|
||||||
const variables = FrontendVariables.getInstance();
|
// const variables = FrontendVariables.getInstance();
|
||||||
const baseBackUrl = variables.BACK_API_PROTOCOL + variables.BACK_API_HOST;
|
|
||||||
|
// TODO: review
|
||||||
|
const baseBackUrl = 'http://localhost:8080'//variables.BACK_API_PROTOCOL + variables.BACK_API_HOST;
|
||||||
|
|
||||||
const url = new URL(`${baseBackUrl}/api/v1/idnot/user/${autorizationCode}`);
|
const url = new URL(`${baseBackUrl}/api/v1/idnot/user/${autorizationCode}`);
|
||||||
try {
|
try {
|
||||||
return await this.postRequest<{ accessToken: string; refreshToken: string }>(url);
|
return await this.postRequest<{ accessToken: string; refreshToken: string }>(url);
|
||||||
|
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;
|
||||||
|
}
|
@ -14,10 +14,12 @@ import classNames from "classnames";
|
|||||||
|
|
||||||
import Button, { EButtonstyletype, EButtonVariant } from "../Button";
|
import Button, { EButtonstyletype, EButtonVariant } from "../Button";
|
||||||
import Confirm from "../OldModal/Confirm";
|
import Confirm from "../OldModal/Confirm";
|
||||||
import Documents from "@Front/Api/LeCoffreApi/Customer/Documents/Documents";
|
|
||||||
import Files from "@Front/Api/LeCoffreApi/Customer/Files/Files";
|
|
||||||
import Alert from "../OldModal/Alert";
|
import Alert from "../OldModal/Alert";
|
||||||
|
|
||||||
|
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||||
|
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
onChange?: (files: File[]) => void;
|
onChange?: (files: File[]) => void;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -196,7 +198,7 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override componentDidMount(): void {}
|
public override componentDidMount(): void { }
|
||||||
|
|
||||||
private onCloseAlertUpload() {
|
private onCloseAlertUpload() {
|
||||||
this.setState({ showFailedUploaded: null });
|
this.setState({ showFailedUploaded: null });
|
||||||
@ -206,18 +208,30 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
|
|||||||
this.setState({
|
this.setState({
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
});
|
});
|
||||||
const filesArray = this.state.currentFiles;
|
|
||||||
|
|
||||||
|
const filesArray = this.state.currentFiles;
|
||||||
if (!filesArray) return;
|
if (!filesArray) return;
|
||||||
let documentCreated: Document = {} as Document;
|
|
||||||
|
LoaderService.getInstance().show();
|
||||||
|
let documentCreated: any;
|
||||||
try {
|
try {
|
||||||
documentCreated = await Documents.getInstance().post({
|
documentCreated = await new Promise<any>((resolve: (document: any) => void) => {
|
||||||
|
const documentTypeData: any = {
|
||||||
folder: {
|
folder: {
|
||||||
uid: this.props.folder_uid,
|
uid: this.props.folder_uid,
|
||||||
},
|
},
|
||||||
depositor: {
|
depositor: {
|
||||||
uid: this.props.customer_uid,
|
uid: this.props.customer_uid,
|
||||||
},
|
}
|
||||||
|
};
|
||||||
|
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||||
|
|
||||||
|
DocumentService.createDocument(documentTypeData, validatorId).then((processCreated: any) => {
|
||||||
|
if (processCreated) {
|
||||||
|
const document: any = processCreated.processData;
|
||||||
|
resolve(document);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState({ showFailedDocument: "Le dossier est vérifié aucune modification n'est acceptée", isLoading: false });
|
this.setState({ showFailedDocument: "Le dossier est vérifié aucune modification n'est acceptée", isLoading: false });
|
||||||
@ -225,16 +239,46 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < filesArray.length; i++) {
|
for (let i = 0; i < filesArray.length; i++) {
|
||||||
const formData = new FormData();
|
const file = filesArray[i]!.file;
|
||||||
formData.append("file", filesArray[i]!.file, filesArray[i]!.fileName);
|
await new Promise<void>((resolve: () => void) => {
|
||||||
const query = JSON.stringify({ document: { uid: documentCreated.uid } });
|
const reader = new FileReader();
|
||||||
formData.append("q", query);
|
reader.onload = (event) => {
|
||||||
try {
|
if (event.target?.result) {
|
||||||
await Files.getInstance().post(formData);
|
const arrayBuffer = event.target.result as ArrayBuffer;
|
||||||
} catch (e) {
|
const uint8Array = new Uint8Array(arrayBuffer);
|
||||||
this.setState({ showFailedUploaded: "Le fichier ne correspond pas aux critères demandés", isLoading: false });
|
|
||||||
return;
|
const fileBlob: any = {
|
||||||
|
type: file.type,
|
||||||
|
data: uint8Array
|
||||||
|
};
|
||||||
|
|
||||||
|
const fileData: any = {
|
||||||
|
file_blob: fileBlob,
|
||||||
|
file_name: file.name
|
||||||
|
};
|
||||||
|
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||||
|
|
||||||
|
FileService.createFile(fileData, validatorId).then((processCreated: any) => {
|
||||||
|
const fileUid: string = processCreated.processData.uid;
|
||||||
|
|
||||||
|
DocumentService.getDocumentByUid(documentCreated.uid).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
const document: any = process.processData;
|
||||||
|
|
||||||
|
let files: any[] = document.files;
|
||||||
|
if (!files) {
|
||||||
|
files = [];
|
||||||
}
|
}
|
||||||
|
files.push({ uid: fileUid });
|
||||||
|
|
||||||
|
DocumentService.updateDocument(process, { files: files, document_status: EDocumentStatus.DEPOSITED }).then(() => resolve());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -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)}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
|
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
|
||||||
import Notifications from "@Front/Api/LeCoffreApi/Notary/Notifications/Notifications";
|
|
||||||
import OfficeFolderAnchors from "@Front/Api/LeCoffreApi/Notary/OfficeFolderAnchors/OfficeFolderAnchors";
|
|
||||||
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
|
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import Toasts from "@Front/Stores/Toasts";
|
import Toasts from "@Front/Stores/Toasts";
|
||||||
@ -17,6 +15,7 @@ export default function Navigation() {
|
|||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
const getAnchoringStatus = useCallback(async () => {
|
const getAnchoringStatus = useCallback(async () => {
|
||||||
|
/* TODO: review
|
||||||
const anchors = await OfficeFolderAnchors.getInstance().get({
|
const anchors = await OfficeFolderAnchors.getInstance().get({
|
||||||
where: {
|
where: {
|
||||||
status: {
|
status: {
|
||||||
@ -27,7 +26,10 @@ export default function Navigation() {
|
|||||||
folder: true,
|
folder: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
// const anchors = [] as any[];
|
||||||
|
|
||||||
|
/* TODO: review
|
||||||
try {
|
try {
|
||||||
for (const anchor of anchors) {
|
for (const anchor of anchors) {
|
||||||
await OfficeFolderAnchors.getInstance().getByUid(anchor.folder?.uid as string);
|
await OfficeFolderAnchors.getInstance().getByUid(anchor.folder?.uid as string);
|
||||||
@ -35,10 +37,13 @@ export default function Navigation() {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getNotifications = useCallback(async () => {
|
const getNotifications = useCallback(async () => {
|
||||||
//await getAnchoringStatus();
|
//await getAnchoringStatus();
|
||||||
|
|
||||||
|
/* TODO: review
|
||||||
const notifications = await Notifications.getInstance().get({
|
const notifications = await Notifications.getInstance().get({
|
||||||
where: {
|
where: {
|
||||||
read: false,
|
read: false,
|
||||||
@ -50,6 +55,9 @@ export default function Navigation() {
|
|||||||
notification: { created_at: "desc" },
|
notification: { created_at: "desc" },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
const notifications = [] as any[];
|
||||||
|
|
||||||
notifications.forEach((notification) => {
|
notifications.forEach((notification) => {
|
||||||
Toasts.getInstance().open({
|
Toasts.getInstance().open({
|
||||||
title: notification.notification.message,
|
title: notification.notification.message,
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
import LogoIcon from "@Assets/logo_standard_neutral.svg";
|
import LogoIcon from "@Assets/logo_standard_neutral.svg";
|
||||||
import Stripe from "@Front/Api/LeCoffreApi/Admin/Stripe/Stripe";
|
// import Stripe from "@Front/Api/LeCoffreApi/Admin/Stripe/Stripe";
|
||||||
import Subscriptions from "@Front/Api/LeCoffreApi/Admin/Subscriptions/Subscriptions";
|
// import Subscriptions from "@Front/Api/LeCoffreApi/Admin/Subscriptions/Subscriptions";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
// import JwtService from "@Front/Services/JwtService/JwtService";
|
||||||
import { InformationCircleIcon, LifebuoyIcon } from "@heroicons/react/24/outline";
|
import { LifebuoyIcon } from "@heroicons/react/24/outline";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect } from "react";
|
||||||
|
|
||||||
import IconButton from "../IconButton";
|
import IconButton from "../IconButton";
|
||||||
import Typography, { ETypo, ETypoColor } from "../Typography";
|
|
||||||
import BurgerMenu from "./BurgerMenu";
|
import BurgerMenu from "./BurgerMenu";
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import LogoCielNatureIcon from "./logo-ciel-notaires.jpeg";
|
import LogoCielNatureIcon from "./logo-ciel-notaires.jpeg";
|
||||||
@ -32,9 +31,10 @@ export default function Header(props: IProps) {
|
|||||||
const { pathname } = router;
|
const { pathname } = router;
|
||||||
const isOnCustomerLoginPage = Module.getInstance().get().modules.pages.CustomersLogin.props.path === pathname;
|
const isOnCustomerLoginPage = Module.getInstance().get().modules.pages.CustomersLogin.props.path === pathname;
|
||||||
|
|
||||||
const [cancelAt, setCancelAt] = useState<Date | null>(null);
|
// const [cancelAt, setCancelAt] = useState<Date | null>(null);
|
||||||
|
|
||||||
const loadSubscription = useCallback(async () => {
|
const loadSubscription = useCallback(async () => {
|
||||||
|
/* TODO: review
|
||||||
const jwt = JwtService.getInstance().decodeJwt();
|
const jwt = JwtService.getInstance().decodeJwt();
|
||||||
const subscription = await Subscriptions.getInstance().get({ where: { office: { uid: jwt?.office_Id } } });
|
const subscription = await Subscriptions.getInstance().get({ where: { office: { uid: jwt?.office_Id } } });
|
||||||
if (subscription[0]) {
|
if (subscription[0]) {
|
||||||
@ -43,6 +43,7 @@ export default function Header(props: IProps) {
|
|||||||
setCancelAt(new Date(stripeSubscription.cancel_at! * 1000));
|
setCancelAt(new Date(stripeSubscription.cancel_at! * 1000));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -84,7 +85,7 @@ export default function Header(props: IProps) {
|
|||||||
)}
|
)}
|
||||||
{isOnCustomerLoginPage && <Image width={70} height={70} alt="ciel-nature" src={LogoCielNatureIcon}></Image>}
|
{isOnCustomerLoginPage && <Image width={70} height={70} alt="ciel-nature" src={LogoCielNatureIcon}></Image>}
|
||||||
</div>
|
</div>
|
||||||
{cancelAt && (
|
{/* {cancelAt && (
|
||||||
<div className={classes["subscription-line"]}>
|
<div className={classes["subscription-line"]}>
|
||||||
<InformationCircleIcon height="24" />
|
<InformationCircleIcon height="24" />
|
||||||
<Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_GENERIC_BLACK}>
|
<Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_GENERIC_BLACK}>
|
||||||
@ -92,7 +93,7 @@ export default function Header(props: IProps) {
|
|||||||
{cancelAt.toLocaleDateString()}.
|
{cancelAt.toLocaleDateString()}.
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ export default function Rules(props: IProps) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const [isShowing, setIsShowing] = React.useState(false);
|
const [isShowing, setIsShowing] = React.useState(false);
|
||||||
const [hasJwt, setHasJwt] = React.useState(false);
|
// const [hasJwt, setHasJwt] = React.useState(false);
|
||||||
|
|
||||||
const getShowValue = useCallback(() => {
|
const getShowValue = useCallback(() => {
|
||||||
if (props.mode === RulesMode.NECESSARY) {
|
if (props.mode === RulesMode.NECESSARY) {
|
||||||
@ -30,8 +30,9 @@ export default function Rules(props: IProps) {
|
|||||||
}, [props.mode, props.rules]);
|
}, [props.mode, props.rules]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!JwtService.getInstance().decodeJwt()) return;
|
// TODO: review
|
||||||
setHasJwt(true);
|
//if (!JwtService.getInstance().decodeJwt()) return;
|
||||||
|
// setHasJwt(true);
|
||||||
setIsShowing(getShowValue());
|
setIsShowing(getShowValue());
|
||||||
}, [getShowValue, isShowing]);
|
}, [getShowValue, isShowing]);
|
||||||
|
|
||||||
@ -40,7 +41,8 @@ export default function Rules(props: IProps) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasJwt || !isShowing) return null;
|
// TODO: review
|
||||||
|
//if (!hasJwt || !isShowing) return null;
|
||||||
|
|
||||||
return props.children;
|
return props.children;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,14 @@ export default function Tabs<T>({ onSelect, tabs: propsTabs }: IProps<T>) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
tabs.current = propsTabs;
|
tabs.current = propsTabs;
|
||||||
|
|
||||||
|
// TODO: review
|
||||||
|
setTimeout(() => {
|
||||||
|
calculateVisibleElements();
|
||||||
|
if (tabs.current && tabs.current.length > 0 && tabs.current[0]) {
|
||||||
|
setSelectedTab(tabs.current[0].value);
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
}, [propsTabs]);
|
}, [propsTabs]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -5,8 +5,8 @@ import Module from "@Front/Config/Module";
|
|||||||
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
||||||
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
||||||
import User from "le-coffre-resources/dist/Notary";
|
import User from "le-coffre-resources/dist/Notary";
|
||||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
// import JwtService from "@Front/Services/JwtService/JwtService";
|
||||||
import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/Admin/Users/Users";
|
// import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/Admin/Users/Users";
|
||||||
|
|
||||||
type IProps = IPropsDashboardWithList;
|
type IProps = IPropsDashboardWithList;
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ export default function DefaultCollaboratorDashboard(props: IProps) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { collaboratorUid } = router.query;
|
const { collaboratorUid } = router.query;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
/* TODO: review
|
||||||
const jwt = JwtService.getInstance().decodeJwt();
|
const jwt = JwtService.getInstance().decodeJwt();
|
||||||
if (!jwt) return;
|
if (!jwt) return;
|
||||||
const query: IGetUsersparams = {
|
const query: IGetUsersparams = {
|
||||||
@ -28,10 +29,11 @@ export default function DefaultCollaboratorDashboard(props: IProps) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Users.getInstance()
|
Users.getInstance()
|
||||||
.get(query)
|
.get(query)
|
||||||
.then((users) => setCollaborators(users));
|
.then((users) => setCollaborators(users));
|
||||||
|
*/
|
||||||
|
setCollaborators([]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onSelectedBlock = (block: IBlock) => {
|
const onSelectedBlock = (block: IBlock) => {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import Folders from "@Front/Api/LeCoffreApi/Customer/Folders/Folders";
|
|
||||||
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||||
@ -8,17 +7,20 @@ import React, { useEffect, useState } from "react";
|
|||||||
|
|
||||||
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
||||||
|
|
||||||
|
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||||
|
|
||||||
type IProps = IPropsDashboardWithList & {};
|
type IProps = IPropsDashboardWithList & {};
|
||||||
|
|
||||||
export default function DefaultCustomerDashboard(props: IProps) {
|
export default function DefaultCustomerDashboard(props: IProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { folderUid } = router.query;
|
const { folderUid, profileUid } = router.query;
|
||||||
const [folders, setFolders] = useState<OfficeFolder[]>([]);
|
const [folders, setFolders] = useState<OfficeFolder[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const jwt = JwtService.getInstance().decodeCustomerJwt();
|
const jwt = JwtService.getInstance().decodeCustomerJwt();
|
||||||
if (!jwt) return;
|
if (!jwt) return;
|
||||||
|
|
||||||
|
/*
|
||||||
Folders.getInstance()
|
Folders.getInstance()
|
||||||
.get({
|
.get({
|
||||||
q: {
|
q: {
|
||||||
@ -42,6 +44,14 @@ export default function DefaultCustomerDashboard(props: IProps) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((folders) => setFolders(folders));
|
.then((folders) => setFolders(folders));
|
||||||
|
*/
|
||||||
|
|
||||||
|
FolderService.getFolders().then((processes: any[]) => {
|
||||||
|
if (processes.length > 0) {
|
||||||
|
const folders: any[] = processes.map((process: any) => process.processData);
|
||||||
|
setFolders(folders);
|
||||||
|
}
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onSelectedBlock = (block: IBlock) => {
|
const onSelectedBlock = (block: IBlock) => {
|
||||||
@ -50,10 +60,12 @@ export default function DefaultCustomerDashboard(props: IProps) {
|
|||||||
router.push(
|
router.push(
|
||||||
Module.getInstance()
|
Module.getInstance()
|
||||||
.get()
|
.get()
|
||||||
.modules.pages.ClientDashboard.props.path.replace("[folderUid]", folder.uid ?? ""),
|
.modules.pages.ClientDashboard.props.path
|
||||||
|
.replace("[folderUid]", folder.uid ?? "")
|
||||||
|
.replace("[profileUid]", profileUid as string ?? ""),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
return <DefaultDashboardWithList {...props} onSelectedBlock={onSelectedBlock} blocks={getBlocks(folders)} headerConnected={false}/>;
|
return <DefaultDashboardWithList {...props} onSelectedBlock={onSelectedBlock} blocks={getBlocks(folders)} headerConnected={false} />;
|
||||||
|
|
||||||
function getBlocks(folders: OfficeFolder[]): IBlock[] {
|
function getBlocks(folders: OfficeFolder[]): IBlock[] {
|
||||||
return folders.map((folder) => {
|
return folders.map((folder) => {
|
||||||
|
@ -5,7 +5,8 @@ import Module from "@Front/Config/Module";
|
|||||||
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
||||||
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
||||||
import { DeedType } from "le-coffre-resources/dist/Notary";
|
import { DeedType } from "le-coffre-resources/dist/Notary";
|
||||||
import DeedTypes, { IGetDeedTypesParams } from "@Front/Api/LeCoffreApi/Notary/DeedTypes/DeedTypes";
|
|
||||||
|
import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService";
|
||||||
|
|
||||||
type IProps = IPropsDashboardWithList;
|
type IProps = IPropsDashboardWithList;
|
||||||
|
|
||||||
@ -13,19 +14,19 @@ export default function DefaultDeedTypeDashboard(props: IProps) {
|
|||||||
const [deedTypes, setDeedTypes] = React.useState<DeedType[] | null>(null);
|
const [deedTypes, setDeedTypes] = React.useState<DeedType[] | null>(null);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { deedTypeUid } = router.query;
|
const { deedTypeUid } = router.query;
|
||||||
useEffect(() => {
|
|
||||||
const query: IGetDeedTypesParams = {
|
|
||||||
where: {
|
|
||||||
archived_at: null,
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
name: "asc",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
DeedTypes.getInstance()
|
useEffect(() => {
|
||||||
.get(query)
|
DeedTypeService.getDeedTypes().then((processes: any) => {
|
||||||
.then((deedTypes) => setDeedTypes(deedTypes));
|
let deedTypes = processes.map((process: any) => process.processData);
|
||||||
|
|
||||||
|
// FilterBy archived_at = null or not defined
|
||||||
|
deedTypes = deedTypes.filter((deedType: any) => !deedType.archived_at);
|
||||||
|
|
||||||
|
// OrderBy name asc
|
||||||
|
deedTypes.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
setDeedTypes(deedTypes);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onSelectedBlock = (block: IBlock) => {
|
const onSelectedBlock = (block: IBlock) => {
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
||||||
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
||||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||||
import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes";
|
|
||||||
import { DocumentType } from "le-coffre-resources/dist/Notary";
|
import { DocumentType } from "le-coffre-resources/dist/Notary";
|
||||||
|
|
||||||
|
import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService";
|
||||||
|
|
||||||
type IProps = IPropsDashboardWithList;
|
type IProps = IPropsDashboardWithList;
|
||||||
|
|
||||||
export default function DefaultDocumentTypeDashboard(props: IProps) {
|
export default function DefaultDocumentTypeDashboard(props: IProps) {
|
||||||
@ -17,16 +17,23 @@ export default function DefaultDocumentTypeDashboard(props: IProps) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const jwt = JwtService.getInstance().decodeJwt();
|
const jwt = JwtService.getInstance().decodeJwt();
|
||||||
if (!jwt) return;
|
if (!jwt) return;
|
||||||
DocumentTypes.getInstance()
|
|
||||||
.get({
|
// TODO: review
|
||||||
where: {
|
const officeId = 'demo_notary_office_id'; // jwt.office_Id;
|
||||||
office_uid: jwt.office_Id,
|
|
||||||
},
|
DocumentTypeService.getDocumentTypes().then((processes: any[]) => {
|
||||||
orderBy: {
|
if (processes.length > 0) {
|
||||||
name: "asc",
|
let documents: any[] = processes.map((process: any) => process.processData);
|
||||||
},
|
|
||||||
})
|
// FilterBy office.uid
|
||||||
.then((documentTypes) => setDocumentTypes(documentTypes));
|
documents = documents.filter((document: any) => document.office.uid === officeId);
|
||||||
|
|
||||||
|
// OrderBy name asc
|
||||||
|
documents = documents.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
setDocumentTypes(documents);
|
||||||
|
}
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onSelectedBlock = (block: IBlock) => {
|
const onSelectedBlock = (block: IBlock) => {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import Folders, { IGetFoldersParams } from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
|
||||||
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
|
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
|
||||||
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||||
import React, { useCallback, useEffect } from "react";
|
import React, { useCallback, useEffect } from "react";
|
||||||
@ -9,6 +8,8 @@ import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
||||||
|
|
||||||
|
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||||
|
|
||||||
type IProps = IPropsDashboardWithList & {
|
type IProps = IPropsDashboardWithList & {
|
||||||
isArchived?: boolean;
|
isArchived?: boolean;
|
||||||
};
|
};
|
||||||
@ -76,6 +77,8 @@ export default function DefaultNotaryDashboard(props: IProps) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let targetedStatus: EFolderStatus = EFolderStatus["LIVE" as keyof typeof EFolderStatus];
|
let targetedStatus: EFolderStatus = EFolderStatus["LIVE" as keyof typeof EFolderStatus];
|
||||||
if (isArchived) targetedStatus = EFolderStatus.ARCHIVED;
|
if (isArchived) targetedStatus = EFolderStatus.ARCHIVED;
|
||||||
|
|
||||||
|
/* TODO: review
|
||||||
const query: IGetFoldersParams = {
|
const query: IGetFoldersParams = {
|
||||||
q: {
|
q: {
|
||||||
where: { status: targetedStatus },
|
where: { status: targetedStatus },
|
||||||
@ -110,6 +113,18 @@ export default function DefaultNotaryDashboard(props: IProps) {
|
|||||||
Folders.getInstance()
|
Folders.getInstance()
|
||||||
.get(query)
|
.get(query)
|
||||||
.then((folders) => setFolders(folders));
|
.then((folders) => setFolders(folders));
|
||||||
|
*/
|
||||||
|
|
||||||
|
FolderService.getFolders().then((processes: any[]) => {
|
||||||
|
if (processes.length > 0) {
|
||||||
|
let folders: any[] = processes.map((process: any) => process.processData);
|
||||||
|
|
||||||
|
// FilterBy status
|
||||||
|
folders = folders.filter((folder: any) => folder.status === targetedStatus);
|
||||||
|
|
||||||
|
setFolders(folders);
|
||||||
|
}
|
||||||
|
});
|
||||||
}, [isArchived]);
|
}, [isArchived]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -5,7 +5,7 @@ import { useRouter } from "next/router";
|
|||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
||||||
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
||||||
import Offices from "@Front/Api/LeCoffreApi/SuperAdmin/Offices/Offices";
|
// import Offices from "@Front/Api/LeCoffreApi/SuperAdmin/Offices/Offices";
|
||||||
|
|
||||||
type IProps = IPropsDashboardWithList;
|
type IProps = IPropsDashboardWithList;
|
||||||
|
|
||||||
@ -14,9 +14,12 @@ export default function DefaultOfficeDashboard(props: IProps) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { officeUid } = router.query;
|
const { officeUid } = router.query;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
/* TODO: review
|
||||||
Offices.getInstance()
|
Offices.getInstance()
|
||||||
.get()
|
.get()
|
||||||
.then((offices) => setOffices(offices));
|
.then((offices) => setOffices(offices));
|
||||||
|
*/
|
||||||
|
setOffices([]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onSelectedBlock = (block: IBlock) => {
|
const onSelectedBlock = (block: IBlock) => {
|
||||||
|
@ -5,7 +5,9 @@ import Module from "@Front/Config/Module";
|
|||||||
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
import { IBlock } from "@Front/Components/DesignSystem/SearchBlockList/BlockList/Block";
|
||||||
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDashboardWithList";
|
||||||
import { OfficeRole } from "le-coffre-resources/dist/Notary";
|
import { OfficeRole } from "le-coffre-resources/dist/Notary";
|
||||||
import OfficeRoles, { IGetRolesParams } from "@Front/Api/LeCoffreApi/Admin/OfficeRoles/OfficeRoles";
|
// import OfficeRoles, { IGetRolesParams } from "@Front/Api/LeCoffreApi/Admin/OfficeRoles/OfficeRoles";
|
||||||
|
|
||||||
|
import RoleService from "src/common/Api/LeCoffreApi/sdk/RoleService";
|
||||||
|
|
||||||
type IProps = IPropsDashboardWithList;
|
type IProps = IPropsDashboardWithList;
|
||||||
|
|
||||||
@ -14,6 +16,7 @@ export default function DefaultRoleDashboard(props: IProps) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { roleUid } = router.query;
|
const { roleUid } = router.query;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
/* TODO: review
|
||||||
const query: IGetRolesParams = {
|
const query: IGetRolesParams = {
|
||||||
include: { rules: true },
|
include: { rules: true },
|
||||||
};
|
};
|
||||||
@ -21,6 +24,14 @@ export default function DefaultRoleDashboard(props: IProps) {
|
|||||||
OfficeRoles.getInstance()
|
OfficeRoles.getInstance()
|
||||||
.get(query)
|
.get(query)
|
||||||
.then((roles) => setRoles(roles));
|
.then((roles) => setRoles(roles));
|
||||||
|
*/
|
||||||
|
|
||||||
|
RoleService.getRoles().then((processes: any[]) => {
|
||||||
|
if (processes.length > 0) {
|
||||||
|
const roles: any[] = processes.map((process: any) => process.processData);
|
||||||
|
setRoles(roles);
|
||||||
|
}
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onSelectedBlock = (block: IBlock) => {
|
const onSelectedBlock = (block: IBlock) => {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
|
// import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
|
||||||
import User from "le-coffre-resources/dist/SuperAdmin";
|
import User from "le-coffre-resources/dist/SuperAdmin";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
@ -14,13 +14,15 @@ export default function DefaultUserDashboard(props: IProps) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { userUid } = router.query;
|
const { userUid } = router.query;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
/* TODO: review
|
||||||
const query: IGetUsersparams = {
|
const query: IGetUsersparams = {
|
||||||
include: { contact: true, office_membership: true },
|
include: { contact: true, office_membership: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
Users.getInstance()
|
Users.getInstance()
|
||||||
.get(query)
|
.get(query)
|
||||||
.then((users) => setUsers(users));
|
.then((users) => setUsers(users));
|
||||||
|
*/
|
||||||
|
setUsers([]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onSelectedBlock = (block: IBlock) => {
|
const onSelectedBlock = (block: IBlock) => {
|
||||||
|
@ -3,10 +3,10 @@ import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Ty
|
|||||||
import { ArrowDownTrayIcon } from "@heroicons/react/24/outline";
|
import { ArrowDownTrayIcon } from "@heroicons/react/24/outline";
|
||||||
import Customer from "le-coffre-resources/dist/Customer";
|
import Customer from "le-coffre-resources/dist/Customer";
|
||||||
import { OfficeFolder as OfficeFolderNotary } from "le-coffre-resources/dist/Notary";
|
import { OfficeFolder as OfficeFolderNotary } from "le-coffre-resources/dist/Notary";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo } from "react";
|
||||||
|
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import OfficeRib from "@Front/Api/LeCoffreApi/Customer/OfficeRib/OfficeRib";
|
// import OfficeRib from "@Front/Api/LeCoffreApi/Customer/OfficeRib/OfficeRib";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
folder: OfficeFolderNotary;
|
folder: OfficeFolderNotary;
|
||||||
@ -16,12 +16,23 @@ type IProps = {
|
|||||||
export default function ContactBox(props: IProps) {
|
export default function ContactBox(props: IProps) {
|
||||||
const { folder, customer } = props;
|
const { folder, customer } = props;
|
||||||
|
|
||||||
const [ribUrl, setRibUrl] = useState<string | null>(null);
|
// const [ribUrl, setRibUrl] = useState<string | null>(null);
|
||||||
|
const ribUrl = null;
|
||||||
|
|
||||||
|
// TODO: review
|
||||||
|
const stakeholder = {
|
||||||
|
cell_phone_number: "0606060606",
|
||||||
|
phone_number: "0606060606",
|
||||||
|
email: "test@lecoffre.fr",
|
||||||
|
};
|
||||||
|
|
||||||
const notaryContact = useMemo(
|
const notaryContact = useMemo(
|
||||||
() =>
|
() =>
|
||||||
folder?.stakeholders!.find((stakeholder) => stakeholder.office_role?.name === "Notaire")?.contact ??
|
/*folder?.stakeholders!.find((stakeholder) => stakeholder.office_role?.name === "Notaire")?.contact ??
|
||||||
folder?.stakeholders![0]!.contact,
|
folder?.stakeholders![0]!.contact*/
|
||||||
|
|
||||||
|
// TODO: review
|
||||||
|
stakeholder,
|
||||||
[folder],
|
[folder],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -37,9 +48,11 @@ export default function ContactBox(props: IProps) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!folder?.office?.uid) return;
|
if (!folder?.office?.uid) return;
|
||||||
|
/* TODO: review
|
||||||
OfficeRib.getInstance()
|
OfficeRib.getInstance()
|
||||||
.getRibStream(folder.office.uid)
|
.getRibStream(folder.office.uid)
|
||||||
.then((blob) => setRibUrl(URL.createObjectURL(blob)));
|
.then((blob) => setRibUrl(URL.createObjectURL(blob)));
|
||||||
|
*/
|
||||||
}, [folder]);
|
}, [folder]);
|
||||||
|
|
||||||
const downloadRib = useCallback(async () => {
|
const downloadRib = useCallback(async () => {
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
import DragAndDrop, { IDocumentFileWithUid } from "@Front/Components/DesignSystem/DragAndDrop";
|
import DragAndDrop, { IDocumentFileWithUid } from "@Front/Components/DesignSystem/DragAndDrop";
|
||||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||||
import { Document } from "le-coffre-resources/dist/Customer";
|
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
|
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import Files from "@Front/Api/LeCoffreApi/Customer/Files/Files";
|
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
|
||||||
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
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";
|
||||||
|
import WatermarkService from "@Front/Services/WatermarkService";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
document: Document;
|
document: any;
|
||||||
onChange: () => void;
|
onChange: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -19,34 +23,138 @@ export default function DepositDocumentComponent(props: IProps) {
|
|||||||
const [refused_reason, setRefusedReason] = useState<string | null>(null);
|
const [refused_reason, setRefusedReason] = useState<string | null>(null);
|
||||||
|
|
||||||
const defaultFiles: IDocumentFileWithUid[] = useMemo(() => {
|
const defaultFiles: IDocumentFileWithUid[] = useMemo(() => {
|
||||||
const filesNotArchived = document.files?.filter((file) => !file.archived_at) ?? [];
|
const filesNotArchived = document.files?.filter((file: any) => !file.archived_at) ?? [];
|
||||||
return filesNotArchived.map((file) => ({
|
return filesNotArchived.map((file: any) => ({
|
||||||
id: file.uid!,
|
id: file.uid!,
|
||||||
file: new File([""], file.file_name!, { type: file.mimetype }),
|
file: new File([""], file.file_name!, { type: file.file_blob.type }),
|
||||||
uid: file.uid!,
|
uid: file.uid!,
|
||||||
}));
|
}));
|
||||||
}, [document.files]);
|
}, [document.files]);
|
||||||
|
|
||||||
const addFile = useCallback(
|
const addFile = useCallback(
|
||||||
(file: File) => {
|
async (file: File) => {
|
||||||
const formData = new FormData();
|
try {
|
||||||
const safeFileName = file.name.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
// Add watermark to the file before processing
|
||||||
formData.append("file", file, safeFileName);
|
const watermarkedFile = await WatermarkService.getInstance().addWatermark(file);
|
||||||
const query = JSON.stringify({ document: { uid: document.uid } });
|
|
||||||
formData.append("q", query);
|
return new Promise<void>(
|
||||||
return Files.getInstance()
|
(resolve: () => void) => {
|
||||||
.post(formData)
|
const reader = new FileReader();
|
||||||
|
reader.onload = (event) => {
|
||||||
|
if (event.target?.result) {
|
||||||
|
const arrayBuffer = event.target.result as ArrayBuffer;
|
||||||
|
const uint8Array = new Uint8Array(arrayBuffer);
|
||||||
|
|
||||||
|
const fileBlob: FileBlob = {
|
||||||
|
type: watermarkedFile.type,
|
||||||
|
data: uint8Array
|
||||||
|
};
|
||||||
|
|
||||||
|
const fileData: FileData = {
|
||||||
|
file_blob: fileBlob,
|
||||||
|
file_name: watermarkedFile.name
|
||||||
|
};
|
||||||
|
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||||
|
|
||||||
|
FileService.createFile(fileData, validatorId).then((processCreated: any) => {
|
||||||
|
const fileUid: string = processCreated.processData.uid;
|
||||||
|
|
||||||
|
DocumentService.getDocumentByUid(document.uid!).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
const document: any = process.processData;
|
||||||
|
|
||||||
|
let files: any[] = document.files;
|
||||||
|
if (!files) {
|
||||||
|
files = [];
|
||||||
|
}
|
||||||
|
files.push({ uid: fileUid });
|
||||||
|
|
||||||
|
DocumentService.updateDocument(process, { files: files, document_status: EDocumentStatus.DEPOSITED }).then(() => resolve());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(watermarkedFile);
|
||||||
|
})
|
||||||
.then(onChange)
|
.then(onChange)
|
||||||
.then(() => ToasterService.getInstance().success({ title: "Succès !", description: "Fichier uploadé avec succès!" }))
|
.then(() => ToasterService.getInstance().success({ title: "Succès !", description: "Fichier uploadé avec succès!" }))
|
||||||
.catch((error) => ToasterService.getInstance().error({ title: "Erreur !", description: error.message }));
|
.catch((error) => ToasterService.getInstance().error({ title: "Erreur !", description: error.message }));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error processing file with watermark:', error);
|
||||||
|
// If watermarking fails, proceed with original file
|
||||||
|
return new Promise<void>(
|
||||||
|
(resolve: () => void) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (event) => {
|
||||||
|
if (event.target?.result) {
|
||||||
|
const arrayBuffer = event.target.result as ArrayBuffer;
|
||||||
|
const uint8Array = new Uint8Array(arrayBuffer);
|
||||||
|
|
||||||
|
const fileBlob: FileBlob = {
|
||||||
|
type: file.type,
|
||||||
|
data: uint8Array
|
||||||
|
};
|
||||||
|
|
||||||
|
const fileData: FileData = {
|
||||||
|
file_blob: fileBlob,
|
||||||
|
file_name: file.name
|
||||||
|
};
|
||||||
|
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||||
|
|
||||||
|
FileService.createFile(fileData, validatorId).then((processCreated: any) => {
|
||||||
|
const fileUid: string = processCreated.processData.uid;
|
||||||
|
|
||||||
|
DocumentService.getDocumentByUid(document.uid!).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
const document: any = process.processData;
|
||||||
|
|
||||||
|
let files: any[] = document.files;
|
||||||
|
if (!files) {
|
||||||
|
files = [];
|
||||||
|
}
|
||||||
|
files.push({ uid: fileUid });
|
||||||
|
|
||||||
|
DocumentService.updateDocument(process, { files: files, document_status: EDocumentStatus.DEPOSITED }).then(() => resolve());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
})
|
||||||
|
.then(onChange)
|
||||||
|
.then(() => ToasterService.getInstance().success({ title: "Succès !", description: "Fichier uploadé avec succès!" }))
|
||||||
|
.catch((error) => ToasterService.getInstance().error({ title: "Erreur !", description: error.message }));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[document.uid, onChange],
|
[document.uid, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const deleteFile = useCallback(
|
const deleteFile = useCallback(
|
||||||
(filedUid: string) => {
|
(fileUid: string) => {
|
||||||
return Files.getInstance()
|
return new Promise<void>(
|
||||||
.delete(filedUid)
|
(resolve: () => void) => {
|
||||||
|
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;
|
||||||
|
|
||||||
|
let files: any[] = document.files;
|
||||||
|
if (!files) {
|
||||||
|
files = [];
|
||||||
|
}
|
||||||
|
files = files.filter((file: any) => file.uid !== fileUid);
|
||||||
|
|
||||||
|
DocumentService.updateDocument(process, { files: files, document_status: EDocumentStatus.ASKED }).then(() => resolve());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
.then(onChange)
|
.then(onChange)
|
||||||
.then(() => ToasterService.getInstance().success({ title: "Succès !", description: "Fichier supprimé avec succès!" }))
|
.then(() => ToasterService.getInstance().success({ title: "Succès !", description: "Fichier supprimé avec succès!" }))
|
||||||
.catch((error) => ToasterService.getInstance().error({ title: "Erreur !", description: error.message }));
|
.catch((error) => ToasterService.getInstance().error({ title: "Erreur !", description: error.message }));
|
||||||
@ -55,7 +163,8 @@ export default function DepositDocumentComponent(props: IProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const onOpenModal = useCallback(async () => {
|
const onOpenModal = useCallback(async () => {
|
||||||
const refused_reason = document.document_history?.find((history) => history.document_status === "REFUSED")?.refused_reason;
|
if (document.document_status !== "REFUSED") return;
|
||||||
|
const refused_reason = document.refused_reason;
|
||||||
if (!refused_reason) return;
|
if (!refused_reason) return;
|
||||||
setRefusedReason(refused_reason);
|
setRefusedReason(refused_reason);
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import DocumentsNotary from "@Front/Api/LeCoffreApi/Customer/DocumentsNotary/DocumentsNotary";
|
|
||||||
import FilesNotary from "@Front/Api/LeCoffreApi/Customer/FilesNotary/Files";
|
|
||||||
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
import IconButton from "@Front/Components/DesignSystem/IconButton";
|
import IconButton from "@Front/Components/DesignSystem/IconButton";
|
||||||
import Table from "@Front/Components/DesignSystem/Table";
|
import Table from "@Front/Components/DesignSystem/Table";
|
||||||
@ -8,7 +6,7 @@ import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Ty
|
|||||||
import BackArrow from "@Front/Components/Elements/BackArrow";
|
import BackArrow from "@Front/Components/Elements/BackArrow";
|
||||||
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
|
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import JwtService, { ICustomerJwtPayload } from "@Front/Services/JwtService/JwtService";
|
// import JwtService, { ICustomerJwtPayload } from "@Front/Services/JwtService/JwtService";
|
||||||
import { ArrowDownTrayIcon, EyeIcon } from "@heroicons/react/24/outline";
|
import { ArrowDownTrayIcon, EyeIcon } from "@heroicons/react/24/outline";
|
||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
import JSZip from "jszip";
|
import JSZip from "jszip";
|
||||||
@ -16,10 +14,14 @@ import { useRouter } from "next/router";
|
|||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Folders from "@Front/Api/LeCoffreApi/Customer/Folders/Folders";
|
|
||||||
import Customer from "le-coffre-resources/dist/Customer";
|
import Customer from "le-coffre-resources/dist/Customer";
|
||||||
import { DocumentNotary } from "le-coffre-resources/dist/Notary";
|
import { DocumentNotary } from "le-coffre-resources/dist/Notary";
|
||||||
|
|
||||||
|
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||||
|
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||||
|
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
|
||||||
|
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
|
||||||
const header: readonly IHead[] = [
|
const header: readonly IHead[] = [
|
||||||
{
|
{
|
||||||
key: "name",
|
key: "name",
|
||||||
@ -42,11 +44,31 @@ export default function ReceivedDocuments() {
|
|||||||
const [customer, setCustomer] = useState<Customer | null>(null);
|
const [customer, setCustomer] = useState<Customer | null>(null);
|
||||||
|
|
||||||
const fetchFolderAndCustomer = useCallback(async () => {
|
const fetchFolderAndCustomer = useCallback(async () => {
|
||||||
let jwt: ICustomerJwtPayload | undefined;
|
// let jwt: ICustomerJwtPayload | undefined;
|
||||||
if (typeof document !== "undefined") {
|
// if (typeof document !== "undefined") {
|
||||||
jwt = JwtService.getInstance().decodeCustomerJwt();
|
// jwt = JwtService.getInstance().decodeCustomerJwt();
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// TODO: review
|
||||||
|
LoaderService.getInstance().show();
|
||||||
|
const folder: any = await new Promise<any>((resolve: (folder: any) => void) => {
|
||||||
|
FolderService.getFolderByUid(folderUid as string).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
const folder: any = process.processData;
|
||||||
|
resolve(folder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//const customer = folder?.customers?.find((customer) => customer.contact?.email === jwt?.email);
|
||||||
|
const customer = folder?.customers?.[0];
|
||||||
|
if (!customer) throw new Error("Customer not found");
|
||||||
|
|
||||||
|
setCustomer(customer);
|
||||||
|
|
||||||
|
return { folder, customer };
|
||||||
|
|
||||||
|
/*
|
||||||
const folder = await Folders.getInstance().getByUid(folderUid as string, {
|
const folder = await Folders.getInstance().getByUid(folderUid as string, {
|
||||||
q: {
|
q: {
|
||||||
office: true,
|
office: true,
|
||||||
@ -76,32 +98,58 @@ export default function ReceivedDocuments() {
|
|||||||
setCustomer(customer);
|
setCustomer(customer);
|
||||||
|
|
||||||
return { folder, customer };
|
return { folder, customer };
|
||||||
|
*/
|
||||||
}, [folderUid]);
|
}, [folderUid]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchFolderAndCustomer();
|
fetchFolderAndCustomer();
|
||||||
|
}, [folderUid]); // Ne dépend que de folderUid
|
||||||
|
|
||||||
|
// Effet séparé pour charger les documents lorsque customer change
|
||||||
|
useEffect(() => {
|
||||||
const customerUid = customer?.uid;
|
const customerUid = customer?.uid;
|
||||||
if (!folderUid || !customerUid) return;
|
if (!folderUid || !customerUid) return;
|
||||||
DocumentsNotary.getInstance()
|
|
||||||
.get({ where: { folder: { uid: folderUid }, customer: { uid: customerUid } }, include: { files: true } })
|
|
||||||
.then((documentsNotary) => setDocumentsNotary(documentsNotary));
|
|
||||||
}, [folderUid, customer, fetchFolderAndCustomer]);
|
|
||||||
|
|
||||||
const onDownload = useCallback((doc: DocumentNotary) => {
|
DocumentService.getDocuments().then(async (processes: any[]) => {
|
||||||
|
if (processes.length > 0) {
|
||||||
|
let documents: any[] = processes.map((process: any) => process.processData);
|
||||||
|
|
||||||
|
// FilterBy folder.uid & customer.uid
|
||||||
|
documents = documents.filter((document: any) => document.folder.uid === folderUid && document.customer && document.customer.uid === customerUid);
|
||||||
|
|
||||||
|
for (const document of documents) {
|
||||||
|
if (document.files && document.files.length > 0) {
|
||||||
|
const files: any[] = [];
|
||||||
|
for (const file of document.files) {
|
||||||
|
files.push((await FileService.getFileByUid(file.uid)).processData);
|
||||||
|
}
|
||||||
|
document.files = files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDocumentsNotary(documents);
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [folderUid, customer]);
|
||||||
|
|
||||||
|
const onDownload = useCallback((doc: any) => {
|
||||||
const file = doc.files?.[0];
|
const file = doc.files?.[0];
|
||||||
if (!file || !file?.uid || !doc.uid) return;
|
if (!file) return;
|
||||||
|
|
||||||
return FilesNotary.getInstance()
|
return new Promise<void>((resolve: () => void) => {
|
||||||
.download(file.uid, doc.uid)
|
const blob = new Blob([file.file_blob.data], { type: file.file_blob.type });
|
||||||
.then((blob) => {
|
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = document.createElement("a");
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = file.file_name ?? "file";
|
a.download = file.file_name;
|
||||||
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
})
|
|
||||||
.catch((e) => console.warn(e));
|
resolve();
|
||||||
|
}).catch((e) => console.warn(e));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onDownloadAll = useCallback(async () => {
|
const onDownloadAll = useCallback(async () => {
|
||||||
@ -110,16 +158,14 @@ export default function ReceivedDocuments() {
|
|||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
const folder = zip.folder("documents") || zip;
|
const folder = zip.folder("documents") || zip;
|
||||||
|
|
||||||
const downloadPromises = documentsNotary.map(async (doc) => {
|
documentsNotary.map((doc: any) => {
|
||||||
const file = doc.files?.[0];
|
const file = doc.files?.[0];
|
||||||
if (file && file.uid && doc.uid) {
|
if (file) {
|
||||||
const blob = await FilesNotary.getInstance().download(file.uid, doc.uid);
|
const blob = new Blob([file.file_blob.data], { type: file.file_blob.type });
|
||||||
folder.file(file.file_name ?? "file", blob);
|
folder.file(file.file_name ?? "file", blob);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(downloadPromises);
|
|
||||||
|
|
||||||
zip.generateAsync({ type: "blob" })
|
zip.generateAsync({ type: "blob" })
|
||||||
.then((blob: any) => {
|
.then((blob: any) => {
|
||||||
saveAs(blob, "documents.zip");
|
saveAs(blob, "documents.zip");
|
||||||
@ -160,9 +206,6 @@ function buildRows(
|
|||||||
folderUid: string | string[],
|
folderUid: string | string[],
|
||||||
onDownloadFileNotary: (doc: DocumentNotary) => void,
|
onDownloadFileNotary: (doc: DocumentNotary) => void,
|
||||||
): IRowProps[] {
|
): IRowProps[] {
|
||||||
console.log(documentsNotary);
|
|
||||||
console.log(folderUid);
|
|
||||||
|
|
||||||
return documentsNotary.map((documentNotary) => ({
|
return documentsNotary.map((documentNotary) => ({
|
||||||
key: documentNotary.uid ?? "",
|
key: documentNotary.uid ?? "",
|
||||||
name: formatName(documentNotary.files?.[0]?.file_name?.split(".")?.[0] ?? "") || "_",
|
name: formatName(documentNotary.files?.[0]?.file_name?.split(".")?.[0] ?? "") || "_",
|
||||||
|
@ -3,16 +3,20 @@ import RightArrowIcon from "@Assets/Icons/right-arrow.svg";
|
|||||||
import Button from "@Front/Components/DesignSystem/Button";
|
import Button from "@Front/Components/DesignSystem/Button";
|
||||||
import FilePreview from "@Front/Components/DesignSystem/FilePreview";
|
import FilePreview from "@Front/Components/DesignSystem/FilePreview";
|
||||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||||
import { DocumentNotary, File } from "le-coffre-resources/dist/Notary";
|
import { DocumentNotary } from "le-coffre-resources/dist/Notary";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { NextRouter, useRouter } from "next/router";
|
import { NextRouter, useRouter } from "next/router";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { FileBlob } from "@Front/Api/Entities/types";
|
||||||
|
|
||||||
import BasePage from "../../Base";
|
import BasePage from "../../Base";
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import DocumentsNotary from "@Front/Api/LeCoffreApi/Customer/DocumentsNotary/DocumentsNotary";
|
|
||||||
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
|
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
|
||||||
import FilesNotary from "@Front/Api/LeCoffreApi/Customer/FilesNotary/Files";
|
|
||||||
|
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||||
|
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
|
||||||
|
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
|
||||||
type IProps = {};
|
type IProps = {};
|
||||||
type IPropsClass = {
|
type IPropsClass = {
|
||||||
@ -25,7 +29,7 @@ type IState = {
|
|||||||
isValidateModalVisible: boolean;
|
isValidateModalVisible: boolean;
|
||||||
refuseText: string;
|
refuseText: string;
|
||||||
selectedFileIndex: number;
|
selectedFileIndex: number;
|
||||||
selectedFile: File | null;
|
selectedFile: { uid: string; file_name: string; file_blob: FileBlob } | null;
|
||||||
documentNotary: DocumentNotary | null;
|
documentNotary: DocumentNotary | null;
|
||||||
fileBlob: Blob | null;
|
fileBlob: Blob | null;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
@ -71,10 +75,10 @@ class ViewDocumentsNotaryClass extends BasePage<IPropsClass, IState> {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={classes["file-container"]}>
|
<div className={classes["file-container"]}>
|
||||||
{this.state.selectedFile.mimetype === "application/pdf" ||
|
{this.state.selectedFile.file_blob.type === "application/pdf" ||
|
||||||
this.state.selectedFile.mimetype === "image/jpeg" ||
|
this.state.selectedFile.file_blob.type === "image/jpeg" ||
|
||||||
this.state.selectedFile.mimetype === "image/png" ||
|
this.state.selectedFile.file_blob.type === "image/png" ||
|
||||||
this.state.selectedFile.mimetype === "image/jpg" ? (
|
this.state.selectedFile.file_blob.type === "image/jpg" ? (
|
||||||
<FilePreview
|
<FilePreview
|
||||||
href={this.state.fileBlob ? URL.createObjectURL(this.state.fileBlob) : ""}
|
href={this.state.fileBlob ? URL.createObjectURL(this.state.fileBlob) : ""}
|
||||||
fileName={this.state.selectedFile.file_name}
|
fileName={this.state.selectedFile.file_name}
|
||||||
@ -123,16 +127,31 @@ class ViewDocumentsNotaryClass extends BasePage<IPropsClass, IState> {
|
|||||||
|
|
||||||
override async componentDidMount() {
|
override async componentDidMount() {
|
||||||
try {
|
try {
|
||||||
const documentNotary = await DocumentsNotary.getInstance().getByUid(this.props.documentUid, {
|
LoaderService.getInstance().show();
|
||||||
files: true,
|
const documentNotary: any = await new Promise<any>((resolve: (document: any) => void) => {
|
||||||
folder: true,
|
DocumentService.getDocumentByUid(this.props.documentUid).then(async (process: any) => {
|
||||||
depositor: true,
|
if (process) {
|
||||||
|
const document: any = process.processData;
|
||||||
|
|
||||||
|
if (document.files && document.files.length > 0) {
|
||||||
|
const files: any[] = [];
|
||||||
|
for (const file of document.files) {
|
||||||
|
files.push((await FileService.getFileByUid(file.uid)).processData);
|
||||||
|
}
|
||||||
|
document.files = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(document);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
documentNotary,
|
documentNotary,
|
||||||
selectedFileIndex: 0,
|
selectedFileIndex: 0,
|
||||||
selectedFile: documentNotary.files![0]!,
|
selectedFile: documentNotary.files![0] as any,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
@ -149,8 +168,7 @@ class ViewDocumentsNotaryClass extends BasePage<IPropsClass, IState> {
|
|||||||
|
|
||||||
private async getFilePreview(): Promise<void> {
|
private async getFilePreview(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const fileBlob: Blob = await FilesNotary.getInstance().download(this.state.selectedFile?.uid as string, this.props.documentUid);
|
const fileBlob: Blob = new Blob([this.state.selectedFile.file_blob.data], { type: this.state.selectedFile.file_blob.type });
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
fileBlob,
|
fileBlob,
|
||||||
});
|
});
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/Customer/Documents/Documents";
|
|
||||||
|
|
||||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||||
|
|
||||||
import Customer, { Document, DocumentType } from "le-coffre-resources/dist/Customer";
|
import Customer, { Document, DocumentType } from "le-coffre-resources/dist/Customer";
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { DocumentNotary, type OfficeFolder as OfficeFolderNotary } from "le-coffre-resources/dist/Notary";
|
import { DocumentNotary, OfficeFolder as OfficeFolderNotary } from "le-coffre-resources/dist/Notary";
|
||||||
|
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import JwtService, { ICustomerJwtPayload } from "@Front/Services/JwtService/JwtService";
|
import JwtService, { ICustomerJwtPayload } from "@Front/Services/JwtService/JwtService";
|
||||||
import Folders from "@Front/Api/LeCoffreApi/Customer/Folders/Folders";
|
|
||||||
|
|
||||||
import Tag, { ETagColor } from "@Front/Components/DesignSystem/Tag";
|
import Tag, { ETagColor } from "@Front/Components/DesignSystem/Tag";
|
||||||
import DefaultCustomerDashboard from "@Front/Components/LayoutTemplates/DefaultCustomerDashboard";
|
import DefaultCustomerDashboard from "@Front/Components/LayoutTemplates/DefaultCustomerDashboard";
|
||||||
@ -21,28 +19,90 @@ import Module from "@Front/Config/Module";
|
|||||||
import Separator, { ESeperatorColor, ESeperatorDirection } from "@Front/Components/DesignSystem/Separator";
|
import Separator, { ESeperatorColor, ESeperatorDirection } from "@Front/Components/DesignSystem/Separator";
|
||||||
import NotificationBox from "@Front/Components/DesignSystem/NotificationBox";
|
import NotificationBox from "@Front/Components/DesignSystem/NotificationBox";
|
||||||
import ContactBox from "./ContactBox";
|
import ContactBox from "./ContactBox";
|
||||||
import DocumentsNotary from "@Front/Api/LeCoffreApi/Customer/DocumentsNotary/DocumentsNotary";
|
|
||||||
import { EDocumentNotaryStatus } from "le-coffre-resources/dist/Notary/DocumentNotary";
|
import { EDocumentNotaryStatus } from "le-coffre-resources/dist/Notary/DocumentNotary";
|
||||||
import DepositOtherDocument from "@Front/Components/DesignSystem/DepositOtherDocument";
|
import DepositOtherDocument from "@Front/Components/DesignSystem/DepositOtherDocument";
|
||||||
|
|
||||||
|
import Modal from "@Front/Components/DesignSystem/Modal";
|
||||||
|
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||||
|
|
||||||
|
import AuthModal from "src/sdk/AuthModal";
|
||||||
|
|
||||||
|
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||||
|
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 LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
|
||||||
type IProps = {};
|
type IProps = {};
|
||||||
|
|
||||||
export default function ClientDashboard(props: IProps) {
|
export default function ClientDashboard(props: IProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
let { folderUid } = router.query;
|
let { folderUid, profileUid } = router.query;
|
||||||
const [documents, setDocuments] = useState<Document[] | null>(null);
|
const [documents, setDocuments] = useState<Document[] | null>(null);
|
||||||
|
|
||||||
const [customer, setCustomer] = useState<Customer | null>(null);
|
const [customer, setCustomer] = useState<Customer | null>(null);
|
||||||
const [folder, setFolder] = useState<OfficeFolderNotary | null>(null);
|
const [folder, setFolder] = useState<OfficeFolderNotary | null>(null);
|
||||||
|
|
||||||
const [documentsNotary, setDocumentsNotary] = useState<DocumentNotary[]>([]);
|
const [documentsNotary, setDocumentsNotary] = useState<DocumentNotary[]>([]);
|
||||||
const [isAddDocumentModalVisible, setIsAddDocumentModalVisible] = useState<boolean>(false);
|
const [isAddDocumentModalVisible, setIsAddDocumentModalVisible] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
|
||||||
|
const [isSmsModalOpen, setIsSmsModalOpen] = useState(false);
|
||||||
|
const [smsCode, setSmsCode] = useState("");
|
||||||
|
const [smsError, setSmsError] = useState("");
|
||||||
|
|
||||||
|
const verifySmsCode = useCallback(() => {
|
||||||
|
if (smsCode === "1234") {
|
||||||
|
setIsSmsModalOpen(false);
|
||||||
|
} else {
|
||||||
|
setSmsError("Code incorrect. Le code valide est 1234.");
|
||||||
|
}
|
||||||
|
}, [smsCode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyPress = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === "Enter" && isSmsModalOpen) {
|
||||||
|
verifySmsCode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("keypress", handleKeyPress);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("keypress", handleKeyPress);
|
||||||
|
};
|
||||||
|
}, [isSmsModalOpen, smsCode, verifySmsCode]);
|
||||||
|
|
||||||
const fetchFolderAndCustomer = useCallback(async () => {
|
const fetchFolderAndCustomer = useCallback(async () => {
|
||||||
let jwt: ICustomerJwtPayload | undefined;
|
let jwt: ICustomerJwtPayload | undefined;
|
||||||
if (typeof document !== "undefined") {
|
if (typeof document !== "undefined") {
|
||||||
jwt = JwtService.getInstance().decodeCustomerJwt();
|
jwt = JwtService.getInstance().decodeCustomerJwt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: review
|
||||||
|
LoaderService.getInstance().show();
|
||||||
|
const { folder, customer } = await new Promise<any>((resolve) => {
|
||||||
|
FolderService.getFolderByUid(folderUid as string).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
const folder: any = process.processData;
|
||||||
|
|
||||||
|
const customers: any[] = folder.customers;
|
||||||
|
const customer: any = customers.find((customer: any) => customer.uid === profileUid as string);
|
||||||
|
if (customer) {
|
||||||
|
resolve({ folder: folder, customer });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setCustomer(customer);
|
||||||
|
setFolder(folder);
|
||||||
|
|
||||||
|
setIsAuthModalOpen(true);
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
|
||||||
|
return { folder, customer };
|
||||||
|
|
||||||
|
/*
|
||||||
const folder = await Folders.getInstance().getByUid(folderUid as string, {
|
const folder = await Folders.getInstance().getByUid(folderUid as string, {
|
||||||
q: {
|
q: {
|
||||||
office: true,
|
office: true,
|
||||||
@ -73,32 +133,40 @@ export default function ClientDashboard(props: IProps) {
|
|||||||
setCustomer(customer);
|
setCustomer(customer);
|
||||||
|
|
||||||
return { folder, customer };
|
return { folder, customer };
|
||||||
|
*/
|
||||||
}, [folderUid]);
|
}, [folderUid]);
|
||||||
|
|
||||||
const fetchDocuments = useCallback(
|
const fetchDocuments = useCallback(
|
||||||
(customerUid: string | undefined) => {
|
async (customerUid: string | undefined) => {
|
||||||
const query: IGetDocumentsparams = {
|
LoaderService.getInstance().show();
|
||||||
where: { depositor: { uid: customerUid }, folder_uid: folderUid as string },
|
return new Promise<void>((resolve: () => void) => {
|
||||||
include: {
|
DocumentService.getDocuments().then(async (processes: any[]) => {
|
||||||
files: true,
|
if (processes.length > 0) {
|
||||||
document_history: true,
|
let documents: any[] = processes.map((process: any) => process.processData);
|
||||||
document_type: true,
|
|
||||||
depositor: true,
|
|
||||||
folder: {
|
|
||||||
include: {
|
|
||||||
customers: {
|
|
||||||
include: {
|
|
||||||
contact: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return Documents.getInstance()
|
// FilterBy folder.uid & depositor.uid
|
||||||
.get(query)
|
documents = documents.filter((document: any) => document.folder.uid === folderUid && document.depositor && document.depositor.uid === customerUid);
|
||||||
.then((documents) => setDocuments(documents));
|
|
||||||
|
for (const document of documents) {
|
||||||
|
if (document.document_type) {
|
||||||
|
document.document_type = (await DocumentTypeService.getDocumentTypeByUid(document.document_type.uid)).processData;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.files && document.files.length > 0) {
|
||||||
|
const files: any[] = [];
|
||||||
|
for (const file of document.files) {
|
||||||
|
files.push((await FileService.getFileByUid(file.uid)).processData);
|
||||||
|
}
|
||||||
|
document.files = files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDocuments(documents);
|
||||||
|
}
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[folderUid],
|
[folderUid],
|
||||||
);
|
);
|
||||||
@ -110,9 +178,29 @@ export default function ClientDashboard(props: IProps) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const customerUid = customer?.uid;
|
const customerUid = customer?.uid;
|
||||||
if (!folderUid || !customerUid) return;
|
if (!folderUid || !customerUid) return;
|
||||||
DocumentsNotary.getInstance()
|
|
||||||
.get({ where: { folder: { uid: folderUid }, customer: { uid: customerUid } }, include: { files: true } })
|
LoaderService.getInstance().show();
|
||||||
.then((documentsNotary) => setDocumentsNotary(documentsNotary));
|
DocumentService.getDocuments().then(async (processes: any[]) => {
|
||||||
|
if (processes.length > 0) {
|
||||||
|
let documents: any[] = processes.map((process: any) => process.processData);
|
||||||
|
|
||||||
|
// FilterBy folder.uid & customer.uid
|
||||||
|
documents = documents.filter((document: any) => document.folder.uid === folderUid && document.customer && document.customer.uid === customerUid);
|
||||||
|
|
||||||
|
for (const document of documents) {
|
||||||
|
if (document.files && document.files.length > 0) {
|
||||||
|
const files: any[] = [];
|
||||||
|
for (const file of document.files) {
|
||||||
|
files.push((await FileService.getFileByUid(file.uid)).processData);
|
||||||
|
}
|
||||||
|
document.files = files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDocumentsNotary(documents);
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
}, [folderUid, customer?.uid]);
|
}, [folderUid, customer?.uid]);
|
||||||
|
|
||||||
const documentsNotaryNotRead = useMemo(
|
const documentsNotaryNotRead = useMemo(
|
||||||
@ -244,6 +332,57 @@ export default function ClientDashboard(props: IProps) {
|
|||||||
Ajouter d'autres documents
|
Ajouter d'autres documents
|
||||||
</Button>
|
</Button>
|
||||||
{isAddDocumentModalVisible && renderBox()}
|
{isAddDocumentModalVisible && renderBox()}
|
||||||
|
|
||||||
|
{isAuthModalOpen && <AuthModal
|
||||||
|
isOpen={isAuthModalOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsAuthModalOpen(false);
|
||||||
|
setIsSmsModalOpen(true);
|
||||||
|
}}
|
||||||
|
/>}
|
||||||
|
|
||||||
|
{isSmsModalOpen && (
|
||||||
|
<Modal
|
||||||
|
isOpen={isSmsModalOpen}
|
||||||
|
onClose={() => setIsSmsModalOpen(false)}
|
||||||
|
title="Vérification SMS"
|
||||||
|
>
|
||||||
|
<div className={classes["sms-modal-content"]}>
|
||||||
|
<Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.TEXT_PRIMARY}>
|
||||||
|
Veuillez saisir le code à 4 chiffres que vous avez reçu par SMS
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
name="smsCode"
|
||||||
|
placeholder="Code SMS à 4 chiffres"
|
||||||
|
value={smsCode}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
// Only allow digits
|
||||||
|
if (value === "" || /^\d+$/.test(value)) {
|
||||||
|
setSmsCode(value);
|
||||||
|
setSmsError("");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{smsError && (
|
||||||
|
<Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.TEXT_ACCENT}>
|
||||||
|
{smsError}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div style={{ marginTop: "20px" }}>
|
||||||
|
<Button
|
||||||
|
variant={EButtonVariant.PRIMARY}
|
||||||
|
onClick={verifySmsCode}
|
||||||
|
>
|
||||||
|
Vérifier
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</DefaultCustomerDashboard>
|
</DefaultCustomerDashboard>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import DeedTypes from "@Front/Api/LeCoffreApi/Notary/DeedTypes/DeedTypes";
|
|
||||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
import Form from "@Front/Components/DesignSystem/Form";
|
import Form from "@Front/Components/DesignSystem/Form";
|
||||||
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||||
@ -6,6 +5,7 @@ import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
|||||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||||
import DefaultDeedTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDeedTypeDashboard";
|
import DefaultDeedTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDeedTypeDashboard";
|
||||||
|
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||||
import { DeedType, Office } from "le-coffre-resources/dist/Admin";
|
import { DeedType, Office } from "le-coffre-resources/dist/Admin";
|
||||||
@ -15,6 +15,9 @@ import { useCallback, useState } from "react";
|
|||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import { validateOrReject, ValidationError } from "class-validator";
|
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 = {};
|
type IProps = {};
|
||||||
export default function DeedTypesCreate(props: IProps) {
|
export default function DeedTypesCreate(props: IProps) {
|
||||||
const [hasChanged, setHasChanged] = useState<boolean>(false);
|
const [hasChanged, setHasChanged] = useState<boolean>(false);
|
||||||
@ -25,12 +28,14 @@ export default function DeedTypesCreate(props: IProps) {
|
|||||||
const onSubmitHandler = useCallback(
|
const onSubmitHandler = useCallback(
|
||||||
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
||||||
try {
|
try {
|
||||||
const jwt = JwtService.getInstance().decodeJwt();
|
// TODO: review
|
||||||
|
const officeId = 'demo_notary_office_id'; //JwtService.getInstance().decodeJwt()?.office_Id;
|
||||||
|
|
||||||
const deedType = DeedType.hydrate<DeedType>({
|
const deedType = DeedType.hydrate<DeedType>({
|
||||||
name: values["name"],
|
name: values["name"],
|
||||||
description: values["description"],
|
description: values["description"],
|
||||||
office: Office.hydrate<Office>({
|
office: Office.hydrate<Office>({
|
||||||
uid: jwt?.office_Id,
|
uid: officeId,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
@ -40,21 +45,28 @@ export default function DeedTypesCreate(props: IProps) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deedTypeCreated = await DeedTypes.getInstance().post(
|
const deedTypeData: any = {
|
||||||
DeedType.hydrate<DeedType>({
|
|
||||||
name: values["name"],
|
name: values["name"],
|
||||||
description: values["description"],
|
description: values["description"],
|
||||||
office: Office.hydrate<Office>({
|
office: {
|
||||||
uid: jwt?.office_Id,
|
uid: officeId,
|
||||||
}),
|
}
|
||||||
}),
|
};
|
||||||
);
|
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(
|
router.push(
|
||||||
Module.getInstance()
|
Module.getInstance()
|
||||||
.get()
|
.get()
|
||||||
.modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeCreated.uid!),
|
.modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", processCreated.processData.uid),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
} catch (validationErrors: Array<ValidationError> | any) {
|
} catch (validationErrors: Array<ValidationError> | any) {
|
||||||
setValidationError(validationErrors as ValidationError[]);
|
setValidationError(validationErrors as ValidationError[]);
|
||||||
return;
|
return;
|
||||||
|
@ -14,6 +14,9 @@ import { ValidationError } from "class-validator";
|
|||||||
|
|
||||||
import classes from "./classes.module.scss";
|
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() {
|
export default function DeedTypesEdit() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
let { deedTypeUid } = router.query;
|
let { deedTypeUid } = router.query;
|
||||||
@ -27,13 +30,15 @@ export default function DeedTypesEdit() {
|
|||||||
setHasChanged(false);
|
setHasChanged(false);
|
||||||
async function getDeedType() {
|
async function getDeedType() {
|
||||||
if (!deedTypeUid) return;
|
if (!deedTypeUid) return;
|
||||||
const deedType = await DeedTypes.getInstance().getByUid(deedTypeUid as string, {
|
LoaderService.getInstance().show();
|
||||||
q: {
|
DeedTypeService.getDeedTypeByUid(deedTypeUid as string).then((process: any) => {
|
||||||
document_types: true,
|
if (process) {
|
||||||
},
|
const deedType: any = process.processData;
|
||||||
});
|
|
||||||
setDeedTypeSelected(deedType);
|
setDeedTypeSelected(deedType);
|
||||||
}
|
}
|
||||||
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getDeedType();
|
getDeedType();
|
||||||
}, [deedTypeUid]);
|
}, [deedTypeUid]);
|
||||||
@ -57,19 +62,19 @@ export default function DeedTypesEdit() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await DeedTypes.getInstance().put(
|
LoaderService.getInstance().show();
|
||||||
deedTypeUid as string,
|
DeedTypeService.getDeedTypeByUid(deedTypeUid as string).then((process: any) => {
|
||||||
DeedType.hydrate<DeedType>({
|
if (process) {
|
||||||
uid: deedTypeUid as string,
|
DeedTypeService.updateDeedType(process, { name: values["name"], description: values["description"] }).then(() => {
|
||||||
name: values["name"],
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
description: values["description"],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
router.push(
|
router.push(
|
||||||
Module.getInstance()
|
Module.getInstance()
|
||||||
.get()
|
.get()
|
||||||
.modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeUid as string),
|
.modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeUid as string),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (validationErrors) {
|
} catch (validationErrors) {
|
||||||
if (!Array.isArray(validationErrors)) return;
|
if (!Array.isArray(validationErrors)) return;
|
||||||
setValidationError(validationErrors as ValidationError[]);
|
setValidationError(validationErrors as ValidationError[]);
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import ChevronIcon from "@Assets/Icons/chevron.svg";
|
import ChevronIcon from "@Assets/Icons/chevron.svg";
|
||||||
import PenICon from "@Assets/Icons/pen.svg";
|
import PenICon from "@Assets/Icons/pen.svg";
|
||||||
import DeedTypes from "@Front/Api/LeCoffreApi/Notary/DeedTypes/DeedTypes";
|
|
||||||
import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes";
|
|
||||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
import { IOption } from "@Front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption";
|
import { IOption } from "@Front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption";
|
||||||
import Form from "@Front/Components/DesignSystem/Form";
|
import Form from "@Front/Components/DesignSystem/Form";
|
||||||
@ -20,6 +18,10 @@ import { useCallback, useEffect, useState } from "react";
|
|||||||
|
|
||||||
import classes from "./classes.module.scss";
|
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 = {};
|
type IProps = {};
|
||||||
export default function DeedTypesInformations(props: IProps) {
|
export default function DeedTypesInformations(props: IProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -49,42 +51,48 @@ export default function DeedTypesInformations(props: IProps) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const deleteDeedType = useCallback(async () => {
|
const deleteDeedType = useCallback(async () => {
|
||||||
await DeedTypes.getInstance().put(
|
LoaderService.getInstance().show();
|
||||||
deedTypeUid as string,
|
DeedTypeService.getDeedTypeByUid(deedTypeUid as string).then((process: any) => {
|
||||||
DeedType.hydrate<DeedType>({
|
if (process) {
|
||||||
uid: deedTypeUid as string,
|
DeedTypeService.updateDeedType(process, { archived_at: new Date().toISOString() }).then(() => {
|
||||||
archived_at: new Date(),
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
}),
|
|
||||||
);
|
|
||||||
router.push(Module.getInstance().get().modules.pages.DeedTypes.props.path);
|
router.push(Module.getInstance().get().modules.pages.DeedTypes.props.path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}, [deedTypeUid, router]);
|
}, [deedTypeUid, router]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getDeedType() {
|
async function getDeedType() {
|
||||||
if (!deedTypeUid) return;
|
if (!deedTypeUid) return;
|
||||||
const deedType = await DeedTypes.getInstance().getByUid(deedTypeUid as string, {
|
|
||||||
q: {
|
DeedTypeService.getDeedTypeByUid(deedTypeUid as string).then((process: any) => {
|
||||||
document_types: true,
|
if (process) {
|
||||||
},
|
const deedType: any = process.processData;
|
||||||
});
|
|
||||||
setDeedTypeSelected(deedType);
|
setDeedTypeSelected(deedType);
|
||||||
|
|
||||||
if (!deedType.document_types) return;
|
if (!deedType.document_types) return;
|
||||||
const documentsOptions: IOption[] = deedType.document_types
|
const documentsOptions: IOption[] = deedType.document_types
|
||||||
?.map((documentType) => {
|
?.map((documentType: any) => {
|
||||||
return {
|
return {
|
||||||
label: documentType.name,
|
label: documentType.name,
|
||||||
id: documentType.uid ?? "",
|
id: documentType.uid ?? "",
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => a.label.localeCompare(b.label));
|
.sort((a: any, b: any) => a.label.localeCompare(b.label));
|
||||||
setSelectedDocuments(documentsOptions);
|
setSelectedDocuments(documentsOptions);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function getDocuments() {
|
async function getDocuments() {
|
||||||
const documents = await DocumentTypes.getInstance().get({});
|
DocumentTypeService.getDocumentTypes().then((processes: any[]) => {
|
||||||
|
if (processes.length) {
|
||||||
|
const documents: any[] = processes.map((process: any) => process.processData);
|
||||||
setAvailableDocuments(documents);
|
setAvailableDocuments(documents);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getDocuments();
|
getDocuments();
|
||||||
getDeedType();
|
getDeedType();
|
||||||
@ -97,12 +105,25 @@ export default function DeedTypesInformations(props: IProps) {
|
|||||||
[openSaveModal],
|
[openSaveModal],
|
||||||
);
|
);
|
||||||
|
|
||||||
const saveDocumentTypes = useCallback(async () => {
|
const saveDocumentTypes = useCallback(() => {
|
||||||
await DeedTypes.getInstance().put(deedTypeUid as string, {
|
LoaderService.getInstance().show();
|
||||||
uid: deedTypeUid as string,
|
DeedTypeService.getDeedTypeByUid(deedTypeUid as string, false).then((process: any) => {
|
||||||
document_types: selectedDocuments.map((document) => DocumentType.hydrate<DocumentType>({ uid: document.id as string })),
|
if (process) {
|
||||||
});
|
const deedType: any = process.processData;
|
||||||
|
|
||||||
|
let document_types: any[] = deedType.document_types;
|
||||||
|
if (!document_types) {
|
||||||
|
document_types = [];
|
||||||
|
}
|
||||||
|
selectedDocuments.map((selectedDocument: any) => ({ uid: selectedDocument.id as string }))
|
||||||
|
.forEach((selectedDocument: any) => document_types.push(selectedDocument));
|
||||||
|
|
||||||
|
DeedTypeService.updateDeedType(process, { document_types: document_types }).then(() => {
|
||||||
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
closeSaveModal();
|
closeSaveModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}, [closeSaveModal, deedTypeUid, selectedDocuments]);
|
}, [closeSaveModal, deedTypeUid, selectedDocuments]);
|
||||||
|
|
||||||
const onDocumentChangeHandler = useCallback((options: IOption[] | null) => {
|
const onDocumentChangeHandler = useCallback((options: IOption[] | null) => {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes";
|
|
||||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
import Form from "@Front/Components/DesignSystem/Form";
|
import Form from "@Front/Components/DesignSystem/Form";
|
||||||
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||||
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
|
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
|
||||||
|
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||||
import { validateOrReject, ValidationError } from "class-validator";
|
import { validateOrReject, ValidationError } from "class-validator";
|
||||||
@ -14,6 +14,9 @@ import { useCallback, useState } from "react";
|
|||||||
|
|
||||||
import classes from "./classes.module.scss";
|
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 = {};
|
type IProps = {};
|
||||||
export default function DocumentTypesCreate(props: IProps) {
|
export default function DocumentTypesCreate(props: IProps) {
|
||||||
const [validationError, setValidationError] = useState<ValidationError[]>([]);
|
const [validationError, setValidationError] = useState<ValidationError[]>([]);
|
||||||
@ -24,21 +27,39 @@ export default function DocumentTypesCreate(props: IProps) {
|
|||||||
try {
|
try {
|
||||||
const jwt = JwtService.getInstance().decodeJwt();
|
const jwt = JwtService.getInstance().decodeJwt();
|
||||||
if (!jwt) return;
|
if (!jwt) return;
|
||||||
const office = Office.hydrate<Office>({
|
|
||||||
uid: jwt.office_Id,
|
|
||||||
});
|
|
||||||
const documentToCreate = DocumentType.hydrate<DocumentType>({
|
|
||||||
...values,
|
|
||||||
office: office,
|
|
||||||
});
|
|
||||||
await validateOrReject(documentToCreate, { groups: ["createDocumentType"] });
|
|
||||||
const documentTypeCreated = await DocumentTypes.getInstance().post(documentToCreate);
|
|
||||||
|
|
||||||
|
// TODO: review
|
||||||
|
const officeId = 'demo_notary_office_id'; // jwt.office_Id;
|
||||||
|
|
||||||
|
const documentFormModel = DocumentType.hydrate<DocumentType>({
|
||||||
|
...values,
|
||||||
|
office: Office.hydrate<Office>({
|
||||||
|
uid: officeId,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
await validateOrReject(documentFormModel, { groups: ["createDocumentType"] });
|
||||||
|
|
||||||
|
const documentTypeData: any = {
|
||||||
|
...values,
|
||||||
|
office: {
|
||||||
|
uid: officeId,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||||
|
|
||||||
|
LoaderService.getInstance().show();
|
||||||
|
DocumentTypeService.createDocumentType(documentTypeData, 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(
|
router.push(
|
||||||
Module.getInstance()
|
Module.getInstance()
|
||||||
.get()
|
.get()
|
||||||
.modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path.replace("[uid]", documentTypeCreated.uid!),
|
.modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path.replace("[uid]", processCreated.processData.uid),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Array) {
|
if (e instanceof Array) {
|
||||||
setValidationError(e);
|
setValidationError(e);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes";
|
|
||||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
import Form from "@Front/Components/DesignSystem/Form";
|
import Form from "@Front/Components/DesignSystem/Form";
|
||||||
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
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 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() {
|
export default function DocumentTypesEdit() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
let { documentTypeUid } = router.query;
|
let { documentTypeUid } = router.query;
|
||||||
@ -23,11 +25,15 @@ export default function DocumentTypesEdit() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getDocumentType() {
|
async function getDocumentType() {
|
||||||
if (!documentTypeUid) return;
|
if (!documentTypeUid) return;
|
||||||
const documentType = await DocumentTypes.getInstance().getByUid(documentTypeUid as string, {
|
LoaderService.getInstance().show();
|
||||||
_count: true,
|
DocumentTypeService.getDocumentTypeByUid(documentTypeUid as string).then((process: any) => {
|
||||||
});
|
if (process) {
|
||||||
|
const documentType: any = process.processData;
|
||||||
setDocumentTypeSelected(documentType);
|
setDocumentTypeSelected(documentType);
|
||||||
}
|
}
|
||||||
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getDocumentType();
|
getDocumentType();
|
||||||
}, [documentTypeUid]);
|
}, [documentTypeUid]);
|
||||||
@ -46,16 +52,22 @@ export default function DocumentTypesEdit() {
|
|||||||
setValidationError(validationErrors as ValidationError[]);
|
setValidationError(validationErrors as ValidationError[]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
LoaderService.getInstance().show();
|
||||||
const documentTypeUpdated = await DocumentTypes.getInstance().put(documentTypeUid as string, documentToUpdate);
|
DocumentTypeService.getDocumentTypeByUid(documentTypeUid as string).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
DocumentTypeService.updateDocumentType(process, values).then(() => {
|
||||||
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
router.push(
|
router.push(
|
||||||
Module.getInstance()
|
Module.getInstance()
|
||||||
.get()
|
.get()
|
||||||
.modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path.replace(
|
.modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path.replace(
|
||||||
"[uid]",
|
"[uid]",
|
||||||
documentTypeUpdated.uid ?? "",
|
documentTypeUid as string ?? "",
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (validationErrors: Array<ValidationError> | any) {
|
} catch (validationErrors: Array<ValidationError> | any) {
|
||||||
if (!Array.isArray(validationErrors)) return;
|
if (!Array.isArray(validationErrors)) return;
|
||||||
setValidationError(validationErrors as ValidationError[]);
|
setValidationError(validationErrors as ValidationError[]);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import ChevronIcon from "@Assets/Icons/chevron.svg";
|
import ChevronIcon from "@Assets/Icons/chevron.svg";
|
||||||
import PenICon from "@Assets/Icons/pen.svg";
|
import PenICon from "@Assets/Icons/pen.svg";
|
||||||
import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes";
|
|
||||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||||
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
|
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
@ -13,6 +12,8 @@ import { useEffect, useState } from "react";
|
|||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
|
|
||||||
|
import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService";
|
||||||
|
|
||||||
export default function DocumentTypesInformations() {
|
export default function DocumentTypesInformations() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
let { documentTypeUid } = router.query;
|
let { documentTypeUid } = router.query;
|
||||||
@ -22,12 +23,14 @@ export default function DocumentTypesInformations() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getDocument() {
|
async function getDocument() {
|
||||||
if (!documentTypeUid) return;
|
if (!documentTypeUid) return;
|
||||||
const document = await DocumentTypes.getInstance().getByUid(documentTypeUid as string, {
|
|
||||||
_count: true,
|
DocumentTypeService.getDocumentTypeByUid(documentTypeUid as string).then((process: any) => {
|
||||||
});
|
if (process) {
|
||||||
if (!document) return;
|
const document: any = process.processData;
|
||||||
setDocumentSelected(document);
|
setDocumentSelected(document);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getDocument();
|
getDocument();
|
||||||
}, [documentTypeUid]);
|
}, [documentTypeUid]);
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import Customers from "@Front/Api/LeCoffreApi/Notary/Customers/Customers";
|
|
||||||
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
|
||||||
import AutocompleteMultiSelect from "@Front/Components/DesignSystem/AutocompleteMultiSelect";
|
import AutocompleteMultiSelect from "@Front/Components/DesignSystem/AutocompleteMultiSelect";
|
||||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
import { IOption } from "@Front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption";
|
import { IOption } from "@Front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption";
|
||||||
@ -9,9 +7,10 @@ import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
|||||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||||
import BackArrow from "@Front/Components/Elements/BackArrow";
|
import BackArrow from "@Front/Components/Elements/BackArrow";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
|
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||||
import { ValidationError } from "class-validator";
|
import { ValidationError } from "class-validator";
|
||||||
import { ECivility } from "le-coffre-resources/dist/Customer/Contact";
|
import { ECivility } from "le-coffre-resources/dist/Customer/Contact";
|
||||||
import { Contact, Customer, OfficeFolder } from "le-coffre-resources/dist/Notary";
|
import { Contact, Customer } from "le-coffre-resources/dist/Notary";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import backgroundImage from "@Assets/images/background_refonte.svg";
|
import backgroundImage from "@Assets/images/background_refonte.svg";
|
||||||
@ -19,6 +18,10 @@ import classes from "./classes.module.scss";
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
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";
|
||||||
|
|
||||||
enum ESelectedOption {
|
enum ESelectedOption {
|
||||||
EXISTING_CUSTOMER = "existing_customer",
|
EXISTING_CUSTOMER = "existing_customer",
|
||||||
NEW_CUSTOMER = "new_customer",
|
NEW_CUSTOMER = "new_customer",
|
||||||
@ -63,34 +66,62 @@ export default function AddClientToFolder(props: IProps) {
|
|||||||
values["cell_phone_number"] = "+33" + values["cell_phone_number"].substring(1);
|
values["cell_phone_number"] = "+33" + values["cell_phone_number"].substring(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const contactToCreate = Contact.hydrate<Customer>(values);
|
const contactFormModel = Contact.hydrate<Customer>(values);
|
||||||
await contactToCreate.validateOrReject?.({ groups: ["createCustomer"], forbidUnknownValues: false });
|
await contactFormModel.validateOrReject?.({ groups: ["createCustomer"], forbidUnknownValues: false });
|
||||||
} catch (validationErrors) {
|
} catch (validationErrors) {
|
||||||
setValidationError(validationErrors as ValidationError[]);
|
setValidationError(validationErrors as ValidationError[]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const customer: Customer = await Customers.getInstance().post({
|
// TODO: review
|
||||||
contact: values,
|
const customerData: any = {
|
||||||
|
contact: values
|
||||||
|
};
|
||||||
|
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) {
|
||||||
|
let customers: any[] = process.processData.customers;
|
||||||
|
if (!customers) {
|
||||||
|
customers = [];
|
||||||
|
}
|
||||||
|
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}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
if (!customer.uid) return;
|
|
||||||
customersToLink?.push({ uid: customer.uid } as Partial<Customer>);
|
|
||||||
} catch (backError) {
|
} catch (backError) {
|
||||||
if (!Array.isArray(backError)) return;
|
if (!Array.isArray(backError)) return;
|
||||||
setValidationError(backError as ValidationError[]);
|
setValidationError(backError as ValidationError[]);
|
||||||
return;
|
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);
|
||||||
|
|
||||||
if (customersToLink) {
|
FolderService.updateFolder(process, { customers: customers }).then(() => {
|
||||||
const body = OfficeFolder.hydrate<OfficeFolder>({
|
ToasterService.getInstance().success({
|
||||||
customers: customersToLink.map((customer) => {
|
title: "Succès !",
|
||||||
return Customer.hydrate<Customer>(customer);
|
description: selectedCustomers.length > 1 ? "Clients associés avec succès au dossier" : "Client associé avec succès au dossier"
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
await Folders.getInstance().put(folderUid as string, body);
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
router.push(`/folders/${folderUid}`);
|
router.push(`/folders/${folderUid}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[existingCustomers, folderUid, router, selectedCustomers, selectedOption],
|
[existingCustomers, folderUid, router, selectedCustomers, selectedOption],
|
||||||
@ -98,24 +129,23 @@ export default function AddClientToFolder(props: IProps) {
|
|||||||
|
|
||||||
const getFolderPreSelectedCustomers = useCallback(
|
const getFolderPreSelectedCustomers = useCallback(
|
||||||
async (folderUid: string): Promise<IOption[] | undefined> => {
|
async (folderUid: string): Promise<IOption[] | undefined> => {
|
||||||
const query = {
|
|
||||||
q: {
|
|
||||||
customers: {
|
|
||||||
include: {
|
|
||||||
contact: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let preExistingCustomers: IOption[] = [];
|
let preExistingCustomers: IOption[] = [];
|
||||||
try {
|
try {
|
||||||
const folder = await Folders.getInstance().getByUid(folderUid, query);
|
preExistingCustomers = await new Promise(resolve => {
|
||||||
preExistingCustomers = folder.customers!.map((customer) => {
|
FolderService.getFolderByUid(folderUid as string).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
const folder: any = process.processData;
|
||||||
|
const preExistingCustomers: IOption[] = folder.customers
|
||||||
|
.map((customer: any) => {
|
||||||
return {
|
return {
|
||||||
label: customer.contact?.first_name + " " + customer.contact?.last_name,
|
label: customer.contact?.first_name + " " + customer.contact?.last_name,
|
||||||
id: customer.uid ?? "",
|
id: customer.uid ?? "",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
resolve(preExistingCustomers);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
router.push(Module.getInstance().get().modules.pages["404"].props.path);
|
router.push(Module.getInstance().get().modules.pages["404"].props.path);
|
||||||
return;
|
return;
|
||||||
@ -126,11 +156,13 @@ export default function AddClientToFolder(props: IProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const loadCustomers = useCallback(async () => {
|
const loadCustomers = useCallback(async () => {
|
||||||
const query = {};
|
LoaderService.getInstance().show();
|
||||||
const availableCustomers = await Customers.getInstance().get(query);
|
CustomerService.getCustomers().then(async (processes: any[]) => {
|
||||||
let preExistingCustomers: IOption[] | undefined = await getFolderPreSelectedCustomers(folderUid as string);
|
const availableCustomers: any[] = processes.map((process: any) => process.processData);
|
||||||
const existingCustomers = preExistingCustomers ?? [];
|
|
||||||
|
|
||||||
|
const preExistingCustomers: IOption[] | undefined = await getFolderPreSelectedCustomers(folderUid as string);
|
||||||
|
|
||||||
|
const existingCustomers = preExistingCustomers ?? [];
|
||||||
existingCustomers.forEach((customer) => {
|
existingCustomers.forEach((customer) => {
|
||||||
const index = availableCustomers.findIndex((availableCustomer) => availableCustomer.uid === customer.id);
|
const index = availableCustomers.findIndex((availableCustomer) => availableCustomer.uid === customer.id);
|
||||||
if (index !== -1) availableCustomers.splice(index, 1);
|
if (index !== -1) availableCustomers.splice(index, 1);
|
||||||
@ -145,6 +177,9 @@ export default function AddClientToFolder(props: IProps) {
|
|||||||
setExistingCustomers(existingCustomers);
|
setExistingCustomers(existingCustomers);
|
||||||
setIsLoaded(true);
|
setIsLoaded(true);
|
||||||
setSelectedOption(selectedOption);
|
setSelectedOption(selectedOption);
|
||||||
|
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
});
|
||||||
}, [folderUid, getFolderPreSelectedCustomers]);
|
}, [folderUid, getFolderPreSelectedCustomers]);
|
||||||
|
|
||||||
const getSelectedOptions = useCallback((): IOption[] => {
|
const getSelectedOptions = useCallback((): IOption[] => {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import Deeds from "@Front/Api/LeCoffreApi/Notary/Deeds/Deeds";
|
|
||||||
import DocumentTypes from "@Front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes";
|
|
||||||
import { IOption } from "@Front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption";
|
import { IOption } from "@Front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption";
|
||||||
import AutocompleteMultiSelectField from "@Front/Components/DesignSystem/Form/AutocompleteMultiSelectField";
|
import AutocompleteMultiSelectField from "@Front/Components/DesignSystem/Form/AutocompleteMultiSelectField";
|
||||||
import { IOption as IFormOption } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
import { IOption as IFormOption } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
||||||
@ -7,11 +5,15 @@ import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
|||||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||||
import Modal from "@Front/Components/DesignSystem/Modal";
|
import Modal from "@Front/Components/DesignSystem/Modal";
|
||||||
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
||||||
import { DocumentType, OfficeFolder } from "le-coffre-resources/dist/Notary";
|
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||||
import { ChangeEvent, useCallback, useEffect, useState } from "react";
|
import { ChangeEvent, useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
|
|
||||||
|
import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService";
|
||||||
|
import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService";
|
||||||
|
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
isCreateDocumentModalVisible: boolean;
|
isCreateDocumentModalVisible: boolean;
|
||||||
closeModal: () => void;
|
closeModal: () => void;
|
||||||
@ -29,7 +31,9 @@ export default function ParameterDocuments(props: IProps) {
|
|||||||
const [formattedOptions, setFormattedOptions] = useState<IOption[]>([]);
|
const [formattedOptions, setFormattedOptions] = useState<IOption[]>([]);
|
||||||
|
|
||||||
const getAvailableDocuments = useCallback(async () => {
|
const getAvailableDocuments = useCallback(async () => {
|
||||||
const documents = await DocumentTypes.getInstance().get({});
|
DocumentTypeService.getDocumentTypes().then((processes: any[]) => {
|
||||||
|
if (processes.length > 0) {
|
||||||
|
const documents: any[] = processes.map((process: any) => process.processData);
|
||||||
|
|
||||||
const formattedOptions: IOption[] = documents
|
const formattedOptions: IOption[] = documents
|
||||||
.filter((document) => {
|
.filter((document) => {
|
||||||
@ -41,8 +45,11 @@ export default function ParameterDocuments(props: IProps) {
|
|||||||
id: document.uid ?? "",
|
id: document.uid ?? "",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
formattedOptions.sort((a, b) => (a.label > b.label ? 1 : -1));
|
formattedOptions.sort((a, b) => (a.label > b.label ? 1 : -1));
|
||||||
setFormattedOptions(formattedOptions);
|
setFormattedOptions(formattedOptions);
|
||||||
|
}
|
||||||
|
});
|
||||||
}, [props.folder.deed?.document_types]);
|
}, [props.folder.deed?.document_types]);
|
||||||
|
|
||||||
const onVisibleDescriptionChange = (event: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
const onVisibleDescriptionChange = (event: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
||||||
@ -68,18 +75,38 @@ export default function ParameterDocuments(props: IProps) {
|
|||||||
const addDocument = useCallback(async () => {
|
const addDocument = useCallback(async () => {
|
||||||
if (addOrEditDocument === "add") {
|
if (addOrEditDocument === "add") {
|
||||||
try {
|
try {
|
||||||
const documentType = await DocumentTypes.getInstance().post({
|
LoaderService.getInstance().show();
|
||||||
|
|
||||||
|
const documentTypeData: any = {
|
||||||
name: documentName,
|
name: documentName,
|
||||||
private_description: visibleDescription,
|
private_description: visibleDescription,
|
||||||
office: {
|
office: {
|
||||||
uid: props.folder.office!.uid!,
|
uid: props.folder.office!.uid!,
|
||||||
},
|
},
|
||||||
public_description: visibleDescription,
|
public_description: visibleDescription,
|
||||||
|
};
|
||||||
|
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||||
|
|
||||||
|
const documentType: any = await new Promise<any>((resolve: (documentType: any) => void) => {
|
||||||
|
DocumentTypeService.createDocumentType(documentTypeData, validatorId).then((processCreated: any) => {
|
||||||
|
const documentType: any = processCreated.processData;
|
||||||
|
resolve(documentType);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const oldDocumentsType = props.folder.deed?.document_types!;
|
const oldDocumentsType = props.folder.deed?.document_types!;
|
||||||
await Deeds.getInstance().put(props.folder.deed?.uid!, {
|
|
||||||
document_types: [...oldDocumentsType, documentType],
|
await new Promise<void>((resolve: () => void) => {
|
||||||
|
DeedTypeService.getDeedTypeByUid(props.folder.deed?.deed_type?.uid!).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
DeedTypeService.updateDeedType(process, {
|
||||||
|
document_types: [
|
||||||
|
...oldDocumentsType.map((document: any) => ({ uid: document.uid })),
|
||||||
|
{ uid: documentType.uid }
|
||||||
|
]
|
||||||
|
}).then(() => resolve());
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a new document type in the format expected by the parent component
|
// Create a new document type in the format expected by the parent component
|
||||||
@ -93,28 +120,44 @@ export default function ParameterDocuments(props: IProps) {
|
|||||||
props.onDocumentsUpdated([newDocumentType]);
|
props.onDocumentsUpdated([newDocumentType]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
handleClose();
|
handleClose();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
LoaderService.getInstance().show();
|
||||||
|
|
||||||
const oldDocumentsType = props.folder.deed?.document_types!;
|
const oldDocumentsType = props.folder.deed?.document_types!;
|
||||||
await Deeds.getInstance().put(props.folder.deed?.uid!, {
|
await new Promise<void>((resolve: () => void) => {
|
||||||
|
DeedTypeService.getDeedTypeByUid(props.folder.deed?.deed_type?.uid!).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
DeedTypeService.updateDeedType(process, {
|
||||||
document_types: [
|
document_types: [
|
||||||
...oldDocumentsType,
|
...oldDocumentsType.map((document: any) => ({ uid: document.uid })),
|
||||||
...selectedDocuments.map((document) => DocumentType.hydrate<DocumentType>({ uid: document.id as string })),
|
...selectedDocuments.map((document: any) => ({ uid: document.id as string }))
|
||||||
],
|
]
|
||||||
|
}).then(() => resolve());
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get the full document details for the selected documents
|
// Get the full document details for the selected documents
|
||||||
const documentsById = await Promise.all(
|
const documentsById = await Promise.all(
|
||||||
selectedDocuments.map(async (doc) => {
|
selectedDocuments.map(async (doc) => {
|
||||||
const fullDoc = await DocumentTypes.getInstance().getByUid(doc.id as string);
|
const documentType: any = await new Promise<any>((resolve: (documentType: any) => void) => {
|
||||||
|
DocumentTypeService.getDocumentTypeByUid(doc.id as string).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
const documentType: any = process.processData;
|
||||||
|
resolve(documentType);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
label: fullDoc.name!,
|
label: documentType.name!,
|
||||||
value: fullDoc.uid!,
|
value: documentType.uid!,
|
||||||
description: fullDoc.private_description!,
|
description: documentType.private_description!,
|
||||||
} as IFormOption;
|
} as IFormOption;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -123,6 +166,7 @@ export default function ParameterDocuments(props: IProps) {
|
|||||||
props.onDocumentsUpdated(documentsById);
|
props.onDocumentsUpdated(documentsById);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
handleClose();
|
handleClose();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import Documents from "@Front/Api/LeCoffreApi/Notary/Documents/Documents";
|
|
||||||
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
|
||||||
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
import CheckBox from "@Front/Components/DesignSystem/CheckBox";
|
import CheckBox from "@Front/Components/DesignSystem/CheckBox";
|
||||||
import Form from "@Front/Components/DesignSystem/Form";
|
import Form from "@Front/Components/DesignSystem/Form";
|
||||||
@ -8,6 +6,7 @@ import BackArrow from "@Front/Components/Elements/BackArrow";
|
|||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import { PlusIcon } from "@heroicons/react/24/outline";
|
import { PlusIcon } from "@heroicons/react/24/outline";
|
||||||
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||||
|
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { useCallback, useEffect, useState, useRef } from "react";
|
import React, { useCallback, useEffect, useState, useRef } from "react";
|
||||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||||
@ -16,6 +15,11 @@ import ParameterDocuments from "./ParameterDocuments";
|
|||||||
import { IOption } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
import { IOption } from "@Front/Components/DesignSystem/Form/SelectFieldOld";
|
||||||
import backgroundImage from "@Assets/images/background_refonte.svg";
|
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 LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
import { DocumentData } from "../FolderInformation/ClientView/DocumentTables/types";
|
||||||
|
|
||||||
export default function AskDocuments() {
|
export default function AskDocuments() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
let { folderUid, customerUid } = router.query;
|
let { folderUid, customerUid } = router.query;
|
||||||
@ -55,21 +59,30 @@ export default function AskDocuments() {
|
|||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
|
// TODO: review
|
||||||
|
LoaderService.getInstance().show();
|
||||||
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++) {
|
||||||
await Documents.getInstance().post({
|
const documentTypeUid = documentAsked[i];
|
||||||
|
if (!documentTypeUid) continue;
|
||||||
|
|
||||||
|
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,
|
||||||
}
|
file_uid: null,
|
||||||
|
};
|
||||||
|
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||||
|
|
||||||
|
await DocumentService.createDocument(documentData, validatorId);
|
||||||
|
}
|
||||||
router.push(
|
router.push(
|
||||||
Module.getInstance()
|
Module.getInstance()
|
||||||
.get()
|
.get()
|
||||||
@ -119,25 +132,15 @@ export default function AskDocuments() {
|
|||||||
|
|
||||||
const loadData = useCallback(async () => {
|
const loadData = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const folder = await Folders.getInstance().getByUid(folderUid as string, {
|
LoaderService.getInstance().show();
|
||||||
q: {
|
FolderService.getFolderByUid(folderUid as string).then(async (process: any) => {
|
||||||
deed: {
|
if (process) {
|
||||||
include: {
|
const folder: any = process.processData;
|
||||||
document_types: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
office: true,
|
|
||||||
documents: {
|
|
||||||
include: {
|
|
||||||
depositor: true,
|
|
||||||
document_type: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (!folder) return;
|
|
||||||
setFolder(folder);
|
setFolder(folder);
|
||||||
setDocumentTypes(await getAvailableDocuments(folder));
|
setDocumentTypes(await getAvailableDocuments(folder));
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,11 @@ import BasePage from "../../Base";
|
|||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import Customer from "le-coffre-resources/dist/Customer";
|
import Customer from "le-coffre-resources/dist/Customer";
|
||||||
import Note from "le-coffre-resources/dist/Customer/Note";
|
import Note from "le-coffre-resources/dist/Customer/Note";
|
||||||
import Folders from "@Front/Api/LeCoffreApi/Customer/Folders/Folders";
|
|
||||||
import Notes from "@Front/Api/LeCoffreApi/Customer/Notes/Notes";
|
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||||
import Customers from "@Front/Api/LeCoffreApi/Notary/Customers/Customers";
|
import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService";
|
||||||
|
import NoteService from "src/common/Api/LeCoffreApi/sdk/NoteService";
|
||||||
|
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
|
||||||
type IProps = {};
|
type IProps = {};
|
||||||
|
|
||||||
@ -76,9 +78,29 @@ class CreateCustomerNoteClass extends BasePage<IPropsClass, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override async componentDidMount() {
|
public override async componentDidMount() {
|
||||||
|
/* TODO: review
|
||||||
// const note = await Notes.getInstance().getByUid(this.props.noteUid, query);
|
// const note = await Notes.getInstance().getByUid(this.props.noteUid, query);
|
||||||
const folder = await Folders.getInstance().getByUid(this.props.folderUid, { note: true });
|
const folder = await Folders.getInstance().getByUid(this.props.folderUid, { note: true });
|
||||||
const customer = await Customers.getInstance().getByUid(this.props.customerUid);
|
const customer = await Customers.getInstance().getByUid(this.props.customerUid);
|
||||||
|
*/
|
||||||
|
|
||||||
|
LoaderService.getInstance().show();
|
||||||
|
|
||||||
|
const folder: any = await FolderService.getFolderByUid(this.props.folderUid).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
const folder: any = process.processData;
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const customer: any = await CustomerService.getCustomerByUid(this.props.customerUid).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
const customer: any = process.processData;
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
|
||||||
//get the note of the folder that has customer_uid = this.props.customer.uid
|
//get the note of the folder that has customer_uid = this.props.customer.uid
|
||||||
// const folderNote = folder.notes?.find((note) => note.customer?.uid === this.props.customerUid);
|
// const folderNote = folder.notes?.find((note) => note.customer?.uid === this.props.customerUid);
|
||||||
@ -91,14 +113,23 @@ class CreateCustomerNoteClass extends BasePage<IPropsClass, IState> {
|
|||||||
if (!this.state.folder || !this.state.customer) {
|
if (!this.state.folder || !this.state.customer) {
|
||||||
throw new Error("Folder or customer not found");
|
throw new Error("Folder or customer not found");
|
||||||
}
|
}
|
||||||
const note = {
|
|
||||||
content: values["content"],
|
|
||||||
folder: this.state.folder,
|
|
||||||
customer: this.state.customer,
|
|
||||||
};
|
|
||||||
|
|
||||||
await Notes.getInstance().post(note);
|
// TODO: review
|
||||||
|
const noteData: any = {
|
||||||
|
content: values["content"],
|
||||||
|
folder: {
|
||||||
|
uid: this.state.folder.uid
|
||||||
|
},
|
||||||
|
customer: {
|
||||||
|
uid: this.state.customer.uid
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||||
|
|
||||||
|
LoaderService.getInstance().show();
|
||||||
|
NoteService.createNote(noteData, validatorId).then(() => {
|
||||||
this.props.router.push(this.backwardPath);
|
this.props.router.push(this.backwardPath);
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import backgroundImage from "@Assets/images/background_refonte.svg";
|
import backgroundImage from "@Assets/images/background_refonte.svg";
|
||||||
import DeedTypes from "@Front/Api/LeCoffreApi/Notary/DeedTypes/DeedTypes";
|
|
||||||
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
|
||||||
import Users from "@Front/Api/LeCoffreApi/Notary/Users/Users";
|
|
||||||
import Button from "@Front/Components/DesignSystem/Button";
|
import Button from "@Front/Components/DesignSystem/Button";
|
||||||
import { IOption } from "@Front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption";
|
import { IOption } from "@Front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption";
|
||||||
import Form from "@Front/Components/DesignSystem/Form";
|
import Form from "@Front/Components/DesignSystem/Form";
|
||||||
@ -11,18 +8,23 @@ import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
|||||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||||
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
||||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||||
|
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||||
import BackArrow from "@Front/Components/Elements/BackArrow";
|
import BackArrow from "@Front/Components/Elements/BackArrow";
|
||||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||||
import { ValidationError } from "class-validator/types/validation/ValidationError";
|
import { ValidationError } from "class-validator/types/validation/ValidationError";
|
||||||
import { Deed, Office, OfficeFolder } from "le-coffre-resources/dist/Notary";
|
import { Deed, Office, OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||||
import User from "le-coffre-resources/dist/Notary";
|
import User from "le-coffre-resources/dist/Notary";
|
||||||
|
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
|
||||||
import { DeedType } from "le-coffre-resources/dist/Notary";
|
import { DeedType } from "le-coffre-resources/dist/Notary";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import classes from "./classes.module.scss";
|
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 {
|
export default function CreateFolder(): JSX.Element {
|
||||||
/**
|
/**
|
||||||
* State
|
* State
|
||||||
@ -48,9 +50,11 @@ export default function CreateFolder(): JSX.Element {
|
|||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const officeId = JwtService.getInstance().decodeJwt()?.office_Id;
|
// TODO: review
|
||||||
|
console.log(JwtService.getInstance().decodeJwt());
|
||||||
|
const officeId = 'demo_notary_office_id'; //JwtService.getInstance().decodeJwt()?.office_Id;
|
||||||
|
|
||||||
const officeFolderForm = OfficeFolder.hydrate<OfficeFolder>({
|
const officeFolderModel = OfficeFolder.hydrate<OfficeFolder>({
|
||||||
folder_number: values["folder_number"],
|
folder_number: values["folder_number"],
|
||||||
name: values["name"],
|
name: values["name"],
|
||||||
description: values["description"],
|
description: values["description"],
|
||||||
@ -67,16 +71,42 @@ export default function CreateFolder(): JSX.Element {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: true });
|
await officeFolderModel.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: true });
|
||||||
} catch (validationErrors) {
|
} catch (validationErrors) {
|
||||||
setValidationError(validationErrors as ValidationError[]);
|
setValidationError(validationErrors as ValidationError[]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newOfficeFolder = await Folders.getInstance().post(officeFolderForm);
|
const folderData: any = {
|
||||||
if (!newOfficeFolder) return;
|
folder_number: values["folder_number"],
|
||||||
router.push(`/folders/${newOfficeFolder.uid}`);
|
name: values["name"],
|
||||||
|
deed: {
|
||||||
|
deed_type: {
|
||||||
|
uid: values["deed"],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
description: values["description"],
|
||||||
|
office: {
|
||||||
|
uid: officeId
|
||||||
|
},
|
||||||
|
customers: [],
|
||||||
|
documents: [],
|
||||||
|
notes: [],
|
||||||
|
stakeholders: folderAccessType === "whole_office" ? availableCollaborators : selectedCollaborators,
|
||||||
|
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}`);
|
||||||
|
});
|
||||||
} catch (backError) {
|
} catch (backError) {
|
||||||
if (!Array.isArray(backError)) return;
|
if (!Array.isArray(backError)) return;
|
||||||
setValidationError(backError as ValidationError[]);
|
setValidationError(backError as ValidationError[]);
|
||||||
@ -100,9 +130,18 @@ export default function CreateFolder(): JSX.Element {
|
|||||||
* UseEffect
|
* UseEffect
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
DeedTypes.getInstance()
|
DeedTypeService.getDeedTypes().then((processes: any[]) => {
|
||||||
.get({ where: { archived_at: null } })
|
if (processes.length > 0) {
|
||||||
.then((deedTypes) => setAvailableDeedTypes(deedTypes));
|
let deedTypes: any[] = processes.map((process: any) => process.processData);
|
||||||
|
|
||||||
|
// FilterBy archived_at = null or not defined
|
||||||
|
deedTypes = deedTypes.filter((deedType: any) => !deedType.archived_at);
|
||||||
|
|
||||||
|
setAvailableDeedTypes(deedTypes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* TODO: review
|
||||||
// no need to pass query 'where' param here, default query for notaries include only users which are in the same office as the caller
|
// no need to pass query 'where' param here, default query for notaries include only users which are in the same office as the caller
|
||||||
Users.getInstance()
|
Users.getInstance()
|
||||||
.get({
|
.get({
|
||||||
@ -110,12 +149,15 @@ export default function CreateFolder(): JSX.Element {
|
|||||||
})
|
})
|
||||||
.then((users) => {
|
.then((users) => {
|
||||||
setAvailableCollaborators(users);
|
setAvailableCollaborators(users);
|
||||||
|
/ *
|
||||||
// set default selected collaborators to the connected user
|
// set default selected collaborators to the connected user
|
||||||
const currentUser = users.find((user) => user.uid === JwtService.getInstance().decodeJwt()?.userId);
|
const currentUser = users.find((user) => user.uid === JwtService.getInstance().decodeJwt()?.userId);
|
||||||
if (currentUser) {
|
if (currentUser) {
|
||||||
setSelectedCollaborators([currentUser]);
|
setSelectedCollaborators([currentUser]);
|
||||||
}
|
}
|
||||||
|
* /
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,171 @@
|
|||||||
|
@import "@Themes/constants.scss";
|
||||||
|
|
||||||
|
.root {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-2xl, 40px);
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: var(--spacing-xl, 32px);
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-md, 16px);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-2xl, 40px);
|
||||||
|
|
||||||
|
.drag-drop-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-xl, 32px);
|
||||||
|
|
||||||
|
@media (min-width: $screen-m) {
|
||||||
|
flex-direction: row;
|
||||||
|
gap: var(--spacing-lg, 24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-drop-box {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 200px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-sm, 8px);
|
||||||
|
|
||||||
|
// Force the DragAndDrop component to take full width and height
|
||||||
|
> div {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: 200px !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
|
||||||
|
// Override the fit-content width from DragAndDrop
|
||||||
|
&.root {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: 200px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info {
|
||||||
|
padding: var(--spacing-sm, 8px) var(--spacing-md, 16px);
|
||||||
|
background-color: var(--color-success-50, #f0f9ff);
|
||||||
|
border: 1px solid var(--color-success-200, #bae6fd);
|
||||||
|
border-radius: var(--radius-md, 8px);
|
||||||
|
margin-top: var(--spacing-sm, 8px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
text-align: center;
|
||||||
|
padding: var(--spacing-md, 16px);
|
||||||
|
background-color: var(--color-warning-50, #fffbeb);
|
||||||
|
border: 1px solid var(--color-warning-200, #fde68a);
|
||||||
|
border-radius: var(--radius-md, 8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.verification-result {
|
||||||
|
text-align: center;
|
||||||
|
padding: var(--spacing-lg, 24px);
|
||||||
|
border-radius: var(--radius-md, 8px);
|
||||||
|
border: 2px solid;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-md, 16px);
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background-color: var(--color-success-50, #f0fdf4);
|
||||||
|
border-color: var(--color-success-200, #bbf7d0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
background-color: var(--color-error-50, #fef2f2);
|
||||||
|
border-color: var(--color-error-200, #fecaca);
|
||||||
|
}
|
||||||
|
|
||||||
|
.verification-details {
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
padding: var(--spacing-md, 16px);
|
||||||
|
border-radius: var(--radius-sm, 4px);
|
||||||
|
font-family: monospace;
|
||||||
|
white-space: pre-line;
|
||||||
|
text-align: left;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.merkle-proof-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-md, 16px);
|
||||||
|
padding: var(--spacing-lg, 24px);
|
||||||
|
background-color: rgba(0, 0, 0, 0.02);
|
||||||
|
border-radius: var(--radius-md, 8px);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-sm, 8px);
|
||||||
|
|
||||||
|
.qr-code {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
border: 2px solid var(--color-neutral-200, #e5e7eb);
|
||||||
|
border-radius: var(--radius-sm, 4px);
|
||||||
|
padding: var(--spacing-sm, 8px);
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-description {
|
||||||
|
text-align: center;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-loading {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
border: 2px dashed var(--color-neutral-300, #d1d5db);
|
||||||
|
border-radius: var(--radius-sm, 4px);
|
||||||
|
background-color: var(--color-neutral-50, #f9fafb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-error {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
border: 2px solid var(--color-error-200, #fecaca);
|
||||||
|
border-radius: var(--radius-sm, 4px);
|
||||||
|
background-color: var(--color-error-50, #fef2f2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-lg, 24px);
|
||||||
|
|
||||||
|
@media (max-width: $screen-s) {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-md, 16px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,260 @@
|
|||||||
|
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
|
import DragAndDrop from "@Front/Components/DesignSystem/DragAndDrop";
|
||||||
|
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||||
|
import Module from "@Front/Config/Module";
|
||||||
|
import PdfService from "@Front/Services/PdfService";
|
||||||
|
import { FileBlob } from "@Front/Api/Entities/types";
|
||||||
|
import { ShieldCheckIcon } from "@heroicons/react/24/outline";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import MessageBus from "src/sdk/MessageBus";
|
||||||
|
|
||||||
|
import classes from "./classes.module.scss";
|
||||||
|
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||||
|
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
folderUid: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a File object to FileBlob
|
||||||
|
* @param file - The File object to convert
|
||||||
|
* @returns Promise<FileBlob> - The converted FileBlob
|
||||||
|
*/
|
||||||
|
const convertFileToFileBlob = async (file: File): Promise<FileBlob> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
const arrayBuffer = reader.result as ArrayBuffer;
|
||||||
|
const uint8Array = new Uint8Array(arrayBuffer);
|
||||||
|
resolve({
|
||||||
|
type: file.type,
|
||||||
|
data: uint8Array
|
||||||
|
});
|
||||||
|
};
|
||||||
|
reader.onerror = () => reject(new Error('Failed to read file'));
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default function DocumentVerification(props: IProps) {
|
||||||
|
const { folderUid } = props;
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const [documentToVerify, setDocumentToVerify] = useState<File | null>(null);
|
||||||
|
const [validationCertificate, setValidationCertificate] = useState<File | null>(null);
|
||||||
|
const [isVerifying, setIsVerifying] = useState(false);
|
||||||
|
const [verificationResult, setVerificationResult] = useState<{
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
details?: string;
|
||||||
|
merkleProof?: string;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
|
const handleDocumentToVerifyChange = (files: File[]) => {
|
||||||
|
if (files.length > 0 && files[0]) {
|
||||||
|
setDocumentToVerify(files[0]);
|
||||||
|
} else {
|
||||||
|
setDocumentToVerify(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleValidationCertificateChange = (files: File[]) => {
|
||||||
|
if (files.length > 0 && files[0]) {
|
||||||
|
setValidationCertificate(files[0]);
|
||||||
|
} else {
|
||||||
|
setValidationCertificate(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleVerifyDocuments = async () => {
|
||||||
|
if (!documentToVerify || !validationCertificate) {
|
||||||
|
console.error("Both documents are required for verification");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsVerifying(true);
|
||||||
|
setVerificationResult(null);
|
||||||
|
|
||||||
|
const messageBus = MessageBus.getInstance();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Here the things we need to verify:
|
||||||
|
// - we can produce the same hash from the document provided than what is in the validation certificate
|
||||||
|
// - the merkle proof is valid with that hash
|
||||||
|
// - the root of the merkle tree is a state id from a commited state in the process
|
||||||
|
// - that process is a file process linked to the right folder
|
||||||
|
// Step 1: Parse the validation certificate
|
||||||
|
const validationData = await PdfService.getInstance().parseCertificate(validationCertificate);
|
||||||
|
|
||||||
|
// Step 2: Convert File to FileBlob and hash the document using MessageBus
|
||||||
|
const fileBlob = await convertFileToFileBlob(documentToVerify);
|
||||||
|
|
||||||
|
await messageBus.isReady();
|
||||||
|
const documentHash = await messageBus.hashDocument(fileBlob, validationData.commitmentId);
|
||||||
|
|
||||||
|
// Step 3: Compare hashes
|
||||||
|
const hashesMatch = documentHash.toLowerCase() === validationData.documentHash.toLowerCase();
|
||||||
|
|
||||||
|
if (!hashesMatch) {
|
||||||
|
throw new Error('Hash du document invalide, le document fourni n\'est pas celui qui a été certifié');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Verify the merkle proof
|
||||||
|
const merkleProof = validationData.merkleProof;
|
||||||
|
const merkleProofValid = await messageBus.verifyMerkleProof(merkleProof, documentHash);
|
||||||
|
|
||||||
|
if (!merkleProofValid) {
|
||||||
|
throw new Error('Preuve de Merkle invalide, le document n\'a pas été certifié là où le certificat le prétend');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: Verify that this file process depends on the right folder process
|
||||||
|
// First pin all the validated documents related to the folder
|
||||||
|
const documentProcesses = await DocumentService.getDocuments();
|
||||||
|
|
||||||
|
const documents = documentProcesses.filter((process: any) =>
|
||||||
|
process.processData.document_status === "VALIDATED" &&
|
||||||
|
process.processData.folder.uid === folderUid
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!documents || documents.length === 0) {
|
||||||
|
throw new Error(`Aucune demande de document trouvé pour le dossier ${folderUid}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6: verify that the merkle proof match the last commited state for the file process
|
||||||
|
const stateId = JSON.parse(validationData.merkleProof)['root'];
|
||||||
|
|
||||||
|
let stateIdExists = false;
|
||||||
|
for (const doc of documents) {
|
||||||
|
const processData = doc.processData;
|
||||||
|
|
||||||
|
for (const file of processData.files) {
|
||||||
|
const fileUid = file.uid;
|
||||||
|
const fileProcess = await FileService.getFileByUid(fileUid);
|
||||||
|
const lastUpdatedStateId = fileProcess.lastUpdatedFileState.state_id;
|
||||||
|
|
||||||
|
stateIdExists = lastUpdatedStateId === stateId; // we assume that last state is the validated document, that seems reasonable
|
||||||
|
if (stateIdExists) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateIdExists) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stateIdExists) {
|
||||||
|
throw new Error('La preuve fournie ne correspond à aucun document demandé pour ce dossier.');
|
||||||
|
}
|
||||||
|
|
||||||
|
setVerificationResult({
|
||||||
|
success: true,
|
||||||
|
message: "✅ Vérification réussie ! Le document est authentique et intègre.",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Verification failed:", error);
|
||||||
|
setVerificationResult({
|
||||||
|
success: false,
|
||||||
|
message: `❌ Erreur lors de la vérification: ${error}`,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsVerifying(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBackToFolder = () => {
|
||||||
|
const folderPath = Module.getInstance()
|
||||||
|
.get()
|
||||||
|
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", folderUid);
|
||||||
|
router.push(folderPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
const bothDocumentsPresent = documentToVerify && validationCertificate;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes["root"]}>
|
||||||
|
<div className={classes["header"]}>
|
||||||
|
<Typography typo={ETypo.TITLE_H2} color={ETypoColor.TEXT_PRIMARY}>
|
||||||
|
Vérification de Documents
|
||||||
|
</Typography>
|
||||||
|
<Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.TEXT_SECONDARY}>
|
||||||
|
Vérifiez l'intégrité et l'authenticité de vos documents
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={classes["content"]}>
|
||||||
|
<div className={classes["drag-drop-container"]}>
|
||||||
|
<div className={classes["drag-drop-box"]}>
|
||||||
|
<DragAndDrop
|
||||||
|
title="Document à valider"
|
||||||
|
description="Glissez-déposez ou cliquez pour sélectionner le document que vous souhaitez vérifier"
|
||||||
|
onChange={handleDocumentToVerifyChange}
|
||||||
|
/>
|
||||||
|
{documentToVerify && (
|
||||||
|
<div className={classes["file-info"]}>
|
||||||
|
<Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_SUCCESS_500}>
|
||||||
|
✓ {documentToVerify.name}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={classes["drag-drop-box"]}>
|
||||||
|
<DragAndDrop
|
||||||
|
title="Certificat de validation"
|
||||||
|
description="Glissez-déposez ou cliquez pour sélectionner le certificat de validation correspondant"
|
||||||
|
onChange={handleValidationCertificateChange}
|
||||||
|
/>
|
||||||
|
{validationCertificate && (
|
||||||
|
<div className={classes["file-info"]}>
|
||||||
|
<Typography typo={ETypo.TEXT_SM_REGULAR} color={ETypoColor.COLOR_SUCCESS_500}>
|
||||||
|
✓ {validationCertificate.name}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!bothDocumentsPresent && (
|
||||||
|
<div className={classes["warning"]}>
|
||||||
|
<Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_WARNING_500}>
|
||||||
|
⚠️ Veuillez sélectionner les deux documents pour procéder à la vérification
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{verificationResult && (
|
||||||
|
<div className={`${classes["verification-result"]} ${classes[verificationResult.success ? "success" : "error"]}`}>
|
||||||
|
<Typography typo={ETypo.TEXT_LG_REGULAR} color={verificationResult.success ? ETypoColor.COLOR_SUCCESS_500 : ETypoColor.COLOR_ERROR_500}>
|
||||||
|
{verificationResult.message}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={classes["actions"]}>
|
||||||
|
<Button
|
||||||
|
variant={EButtonVariant.SECONDARY}
|
||||||
|
styletype={EButtonstyletype.TEXT}
|
||||||
|
size={EButtonSize.LG}
|
||||||
|
onClick={handleBackToFolder}
|
||||||
|
disabled={isVerifying}
|
||||||
|
>
|
||||||
|
Retour au dossier
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant={EButtonVariant.PRIMARY}
|
||||||
|
styletype={EButtonstyletype.CONTAINED}
|
||||||
|
size={EButtonSize.LG}
|
||||||
|
onClick={handleVerifyDocuments}
|
||||||
|
rightIcon={<ShieldCheckIcon />}
|
||||||
|
disabled={!bothDocumentsPresent || isVerifying}
|
||||||
|
isLoading={isVerifying}
|
||||||
|
>
|
||||||
|
{isVerifying ? "Vérification en cours..." : "Vérifier les documents"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -12,7 +12,9 @@ import { useCallback } from "react";
|
|||||||
import { AnchorStatus } from "../..";
|
import { AnchorStatus } from "../..";
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import DeleteCustomerModal from "./DeleteCustomerModal";
|
import DeleteCustomerModal from "./DeleteCustomerModal";
|
||||||
import Documents from "@Front/Api/LeCoffreApi/Notary/Documents/Documents";
|
|
||||||
|
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||||
|
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
customer: Customer;
|
customer: Customer;
|
||||||
@ -28,18 +30,33 @@ export default function ClientBox(props: IProps) {
|
|||||||
const { isOpen: isDeleteModalOpen, open: openDeleteModal, close: closeDeleteModal } = useOpenable();
|
const { isOpen: isDeleteModalOpen, open: openDeleteModal, close: closeDeleteModal } = useOpenable();
|
||||||
const { isOpen: isErrorModalOpen, open: openErrorModal, close: closeErrorModal } = useOpenable();
|
const { isOpen: isErrorModalOpen, open: openErrorModal, close: closeErrorModal } = useOpenable();
|
||||||
|
|
||||||
|
// TODO: review
|
||||||
|
const handleOpenConnectionLink = useCallback(() => {
|
||||||
|
const url = `http://localhost:3000/client-dashboard/${folderUid}/profile/${customer.uid}`;
|
||||||
|
alert(url);
|
||||||
|
}, [customer.uid, folderUid]);
|
||||||
|
|
||||||
const handleDelete = useCallback(
|
const handleDelete = useCallback(
|
||||||
async (customerUid: string) => {
|
(customerUid: string) => {
|
||||||
console.log(customer);
|
LoaderService.getInstance().show();
|
||||||
const documents = await Documents.getInstance().get({ where: { depositor_uid: customerUid, folder_uid: folderUid } });
|
DocumentService.getDocuments().then((processes: any[]) => {
|
||||||
console.log(documents);
|
if (processes.length > 0) {
|
||||||
|
let documents: any[] = processes.map((process: any) => process.processData);
|
||||||
|
|
||||||
|
// FilterBy folder.uid & depositor.uid
|
||||||
|
documents = documents.filter((document: any) => document.folder.uid === folderUid && document.depositor && document.depositor.uid === customerUid);
|
||||||
|
|
||||||
if (documents && documents.length > 0) {
|
if (documents && documents.length > 0) {
|
||||||
closeDeleteModal();
|
closeDeleteModal();
|
||||||
openErrorModal();
|
openErrorModal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
props.onDelete(customerUid);
|
props.onDelete(customerUid);
|
||||||
|
} else {
|
||||||
|
props.onDelete(customerUid);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[closeDeleteModal, customer.documents, openErrorModal, props, customer, folderUid],
|
[closeDeleteModal, customer.documents, openErrorModal, props, customer, folderUid],
|
||||||
);
|
);
|
||||||
@ -83,6 +100,15 @@ export default function ClientBox(props: IProps) {
|
|||||||
</Menu>
|
</Menu>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
size={EButtonSize.SM}
|
||||||
|
variant={EButtonVariant.WARNING}
|
||||||
|
styletype={EButtonstyletype.TEXT}
|
||||||
|
onClick={handleOpenConnectionLink}>
|
||||||
|
Lien de connexion
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_NEUTRAL_700}>
|
<Typography typo={ETypo.TEXT_MD_REGULAR} color={ETypoColor.COLOR_NEUTRAL_700}>
|
||||||
Numéro de téléphone
|
Numéro de téléphone
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import Documents from "@Front/Api/LeCoffreApi/Notary/Documents/Documents";
|
|
||||||
import Modal from "@Front/Components/DesignSystem/Modal";
|
import Modal from "@Front/Components/DesignSystem/Modal";
|
||||||
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||||
import React, { useCallback } from "react";
|
import React, { useCallback } from "react";
|
||||||
|
|
||||||
|
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||||
|
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
documentUid: string;
|
documentUid: string;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -15,13 +17,22 @@ export default function DeleteAskedDocumentModal(props: IProps) {
|
|||||||
const { isOpen, onClose, documentUid, onDeleteSuccess } = props;
|
const { isOpen, onClose, documentUid, onDeleteSuccess } = props;
|
||||||
|
|
||||||
const onDelete = useCallback(
|
const onDelete = useCallback(
|
||||||
() =>
|
() => {
|
||||||
Documents.getInstance()
|
LoaderService.getInstance().show();
|
||||||
.delete(documentUid)
|
new Promise<void>(
|
||||||
|
(resolve: () => void) => {
|
||||||
|
DocumentService.getDocumentByUid(documentUid).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
DocumentService.updateDocument(process, { isDeleted: 'true' }).then(() => resolve());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
.then(() => onDeleteSuccess(documentUid))
|
.then(() => onDeleteSuccess(documentUid))
|
||||||
.then(() => ToasterService.getInstance().success({ title: "Succès !", description: "Le document a été supprimé avec succès." }))
|
.then(() => ToasterService.getInstance().success({ title: "Succès !", description: "Le document a été supprimé avec succès." }))
|
||||||
|
.then(() => LoaderService.getInstance().hide())
|
||||||
.then(onClose)
|
.then(onClose)
|
||||||
.catch((error) => console.warn(error)),
|
.catch((error) => console.warn(error));
|
||||||
|
},
|
||||||
[documentUid, onClose, onDeleteSuccess],
|
[documentUid, onClose, onDeleteSuccess],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import DocumentsNotary from "@Front/Api/LeCoffreApi/Notary/DocumentsNotary/DocumentsNotary";
|
|
||||||
import Modal from "@Front/Components/DesignSystem/Modal";
|
import Modal from "@Front/Components/DesignSystem/Modal";
|
||||||
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||||
import React, { useCallback } from "react";
|
import React, { useCallback } from "react";
|
||||||
|
|
||||||
|
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||||
|
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
documentUid: string;
|
documentUid: string;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -15,13 +17,18 @@ export default function DeleteSentDocumentModal(props: IProps) {
|
|||||||
const { isOpen, onClose, documentUid, onDeleteSuccess } = props;
|
const { isOpen, onClose, documentUid, onDeleteSuccess } = props;
|
||||||
|
|
||||||
const onDelete = useCallback(
|
const onDelete = useCallback(
|
||||||
() =>
|
() => {
|
||||||
DocumentsNotary.getInstance()
|
LoaderService.getInstance().show();
|
||||||
.delete(documentUid)
|
DocumentService.getDocumentByUid(documentUid).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
DocumentService.updateDocument(process, { isDeleted: 'true' })
|
||||||
.then(() => onDeleteSuccess(documentUid))
|
.then(() => onDeleteSuccess(documentUid))
|
||||||
.then(() => ToasterService.getInstance().success({ title: "Succès !", description: "Le document a été supprimé avec succès." }))
|
.then(() => ToasterService.getInstance().success({ title: "Succès !", description: "Le document a été supprimé avec succès." }))
|
||||||
.then(onClose)
|
.then(() => LoaderService.getInstance().hide())
|
||||||
.catch((error) => console.warn(error)),
|
.then(onClose);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
[documentUid, onClose, onDeleteSuccess],
|
[documentUid, onClose, onDeleteSuccess],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import Modal from "@Front/Components/DesignSystem/Modal";
|
import Modal from "@Front/Components/DesignSystem/Modal";
|
||||||
import { File } from "le-coffre-resources/dist/Customer";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { FileBlob } from "@Front/Api/Entities/types";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
file: File;
|
file: {
|
||||||
|
uid: string;
|
||||||
|
file_blob: FileBlob;
|
||||||
|
};
|
||||||
url: string;
|
url: string;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
@ -14,7 +17,7 @@ export default function FilePreviewModal(props: IProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal key={file.uid} isOpen={isOpen} onClose={onClose} fullscreen>
|
<Modal key={file.uid} isOpen={isOpen} onClose={onClose} fullscreen>
|
||||||
<object data={url} type={file.mimetype} width="100%" height="800px" />
|
<object data={url} type={file.file_blob.type} width="100%" height="800px" />
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
import Documents from "@Front/Api/LeCoffreApi/Notary/Documents/Documents";
|
|
||||||
import DocumentsNotary from "@Front/Api/LeCoffreApi/Notary/DocumentsNotary/DocumentsNotary";
|
|
||||||
import Files from "@Front/Api/LeCoffreApi/Notary/Files/Files";
|
|
||||||
import FilesNotary from "@Front/Api/LeCoffreApi/Notary/FilesNotary/Files";
|
|
||||||
import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
|
import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
|
||||||
import IconButton from "@Front/Components/DesignSystem/IconButton";
|
import IconButton from "@Front/Components/DesignSystem/IconButton";
|
||||||
import Table from "@Front/Components/DesignSystem/Table";
|
import Table from "@Front/Components/DesignSystem/Table";
|
||||||
@ -10,9 +6,8 @@ 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 { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
|
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
|
||||||
import DocumentNotary, { EDocumentNotaryStatus } from "le-coffre-resources/dist/Notary/DocumentNotary";
|
import DocumentNotary, { EDocumentNotaryStatus } from "le-coffre-resources/dist/Notary/DocumentNotary";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@ -23,6 +18,14 @@ import classes from "./classes.module.scss";
|
|||||||
import DeleteAskedDocumentModal from "./DeleteAskedDocumentModal";
|
import DeleteAskedDocumentModal from "./DeleteAskedDocumentModal";
|
||||||
import DeleteSentDocumentModal from "./DeleteSentDocumentModal";
|
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 LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
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 = {
|
type IProps = {
|
||||||
customerUid: string;
|
customerUid: string;
|
||||||
folderUid: string;
|
folderUid: string;
|
||||||
@ -42,7 +45,7 @@ const tradDocumentsNotaryStatus: Record<EDocumentNotaryStatus, string> = {
|
|||||||
|
|
||||||
export default function DocumentTables(props: IProps) {
|
export default function DocumentTables(props: IProps) {
|
||||||
const { folderUid, customerUid } = props;
|
const { folderUid, customerUid } = props;
|
||||||
const [documents, setDocuments] = useState<Document[]>([]);
|
const [documents, setDocuments] = useState<any[]>([]);
|
||||||
const [documentsNotary, setDocumentsNotary] = useState<DocumentNotary[]>([]);
|
const [documentsNotary, setDocumentsNotary] = useState<DocumentNotary[]>([]);
|
||||||
const [focusedDocumentUid, setFocusedDocumentUid] = useState<string | null>(null);
|
const [focusedDocumentUid, setFocusedDocumentUid] = useState<string | null>(null);
|
||||||
|
|
||||||
@ -52,23 +55,72 @@ export default function DocumentTables(props: IProps) {
|
|||||||
const deleteSentDocumentModal = useOpenable();
|
const deleteSentDocumentModal = useOpenable();
|
||||||
|
|
||||||
const fetchDocuments = useCallback(
|
const fetchDocuments = useCallback(
|
||||||
() =>
|
() => {
|
||||||
Documents.getInstance()
|
LoaderService.getInstance().show();
|
||||||
.get({
|
DocumentService.getDocuments().then(async (processes: any[]) => {
|
||||||
where: { folder: { uid: folderUid }, depositor: { uid: customerUid } },
|
if (processes.length > 0) {
|
||||||
include: { files: true, document_type: true },
|
let documents: any[] = processes.map((process: any) => process.processData);
|
||||||
})
|
|
||||||
.then(setDocuments)
|
// FilterBy folder.uid & depositor.uid
|
||||||
.catch(console.warn),
|
documents = documents.filter((document: any) => document.folder.uid === folderUid && document.depositor && 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) {
|
||||||
|
if (document.document_type) {
|
||||||
|
document.document_type = (await DocumentTypeService.getDocumentTypeByUid(document.document_type.uid)).processData;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.files && document.files.length > 0) {
|
||||||
|
const files: any[] = [];
|
||||||
|
for (const file of document.files) {
|
||||||
|
files.push((await FileService.getFileByUid(file.uid)).processData);
|
||||||
|
}
|
||||||
|
document.files = files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDocuments(documents);
|
||||||
|
} else {
|
||||||
|
setDocuments([]);
|
||||||
|
}
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
[customerUid, folderUid],
|
[customerUid, folderUid],
|
||||||
);
|
);
|
||||||
|
|
||||||
const fetchDocumentsNotary = useCallback(
|
const fetchDocumentsNotary = useCallback(
|
||||||
() =>
|
() => {
|
||||||
DocumentsNotary.getInstance()
|
LoaderService.getInstance().show();
|
||||||
.get({ where: { folder: { uid: folderUid }, customer: { uid: customerUid } }, include: { files: true } })
|
DocumentService.getDocuments().then(async (processes: any[]) => {
|
||||||
.then(setDocumentsNotary)
|
if (processes.length > 0) {
|
||||||
.catch(console.warn),
|
let documents: any[] = processes.map((process: any) => process.processData);
|
||||||
|
|
||||||
|
// FilterBy folder.uid & customer.uid
|
||||||
|
documents = documents.filter((document: any) => document.folder.uid === folderUid && document.customer && document.customer.uid === customerUid);
|
||||||
|
|
||||||
|
for (const document of documents) {
|
||||||
|
if (document.files && document.files.length > 0) {
|
||||||
|
const files: any[] = [];
|
||||||
|
for (const file of document.files) {
|
||||||
|
files.push((await FileService.getFileByUid(file.uid)).processData);
|
||||||
|
}
|
||||||
|
document.files = files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDocumentsNotary(documents);
|
||||||
|
} else {
|
||||||
|
setDocumentsNotary([]);
|
||||||
|
}
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
[customerUid, folderUid],
|
[customerUid, folderUid],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -95,40 +147,109 @@ export default function DocumentTables(props: IProps) {
|
|||||||
[deleteSentDocumentModal],
|
[deleteSentDocumentModal],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDownload = useCallback((doc: Document) => {
|
const onDownload = useCallback((doc: any) => {
|
||||||
const file = doc.files?.[0];
|
const file = doc.files?.[0];
|
||||||
if (!file || !file?.uid) return;
|
if (!file) return;
|
||||||
|
|
||||||
return Files.getInstance()
|
return new Promise<void>((resolve: () => void) => {
|
||||||
.download(file.uid)
|
const blob = new Blob([file.file_blob.data], { type: file.file_blob.type });
|
||||||
.then((blob) => {
|
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = document.createElement("a");
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = file.file_name ?? "file";
|
a.download = file.file_name;
|
||||||
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
})
|
|
||||||
.catch((e) => console.warn(e));
|
resolve();
|
||||||
|
}).catch((e) => console.warn(e));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onDownloadFileNotary = useCallback((doc: DocumentNotary) => {
|
const onDownloadFileNotary = useCallback((doc: any) => {
|
||||||
const file = doc.files?.[0];
|
const file = doc.files?.[0];
|
||||||
if (!file || !file?.uid) return;
|
if (!file) return;
|
||||||
|
|
||||||
return FilesNotary.getInstance()
|
return new Promise<void>((resolve: () => void) => {
|
||||||
.download(file.uid)
|
const blob = new Blob([file.file_blob.data], { type: file.file_blob.type });
|
||||||
.then((blob) => {
|
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = document.createElement("a");
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = file.file_name ?? "file";
|
a.download = file.file_name;
|
||||||
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
})
|
|
||||||
.catch((e) => console.warn(e));
|
resolve();
|
||||||
|
}).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"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the specific document that was clicked
|
||||||
|
const documentProcess = await DocumentService.getDocumentByUid(doc.uid);
|
||||||
|
if (!documentProcess) {
|
||||||
|
throw new Error('Document not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process only the files for this specific document
|
||||||
|
for (const file of documentProcess.processData.files) {
|
||||||
|
const fileProcess = await FileService.getFileByUid(file.uid);
|
||||||
|
console.log('[DocumentTables] onDownloadCertificate: fileProcess', fileProcess);
|
||||||
|
|
||||||
|
const hash = fileProcess.lastUpdatedFileState.pcd_commitment.file_blob;
|
||||||
|
certificateData.documentHash = hash;
|
||||||
|
|
||||||
|
const proof = await MessageBus.getInstance().generateMerkleProof(fileProcess.lastUpdatedFileState, 'file_blob');
|
||||||
|
console.log('[DocumentTables] onDownloadCertificate: proof', proof);
|
||||||
|
|
||||||
|
const metadata: Metadata = {
|
||||||
|
fileName: fileProcess.processData.file_name,
|
||||||
|
isDeleted: false,
|
||||||
|
updatedAt: new Date(fileProcess.processData.updated_at),
|
||||||
|
commitmentId: fileProcess.lastUpdatedFileState.commited_in,
|
||||||
|
createdAt: new Date(fileProcess.processData.created_at),
|
||||||
|
documentUid: doc.uid,
|
||||||
|
documentType: doc.document_type.name,
|
||||||
|
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(
|
const askedDocuments: IRowProps[] = useMemo(
|
||||||
() =>
|
() =>
|
||||||
documents
|
documents
|
||||||
@ -136,21 +257,25 @@ export default function DocumentTables(props: IProps) {
|
|||||||
if (document.document_status !== EDocumentStatus.ASKED) return null;
|
if (document.document_status !== EDocumentStatus.ASKED) return null;
|
||||||
return {
|
return {
|
||||||
key: document.uid,
|
key: document.uid,
|
||||||
document_type: { sx: { width: 400 }, content: document.document_type?.name ?? "_" },
|
document_type: { sx: { width: 300 }, content: document.document_type?.name ?? "_" },
|
||||||
document_status: {
|
document_status: {
|
||||||
sx: { width: 107 },
|
sx: { width: 107 },
|
||||||
content: (
|
content: (
|
||||||
<Tag
|
<Tag
|
||||||
color={ETagColor.INFO}
|
color={ETagColor.INFO}
|
||||||
variant={ETagVariant.SEMI_BOLD}
|
variant={ETagVariant.SEMI_BOLD}
|
||||||
label={tradDocumentStatus[document.document_status].toUpperCase()}
|
label={tradDocumentStatus[document.document_status as EDocumentStatus].toUpperCase()}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
sx: { width: 107 },
|
sx: { width: 120 },
|
||||||
content: document.created_at ? new Date(document.created_at).toLocaleDateString() : "_",
|
content: document.created_at ? new Date(document.created_at).toLocaleDateString() : "_",
|
||||||
},
|
},
|
||||||
|
file: {
|
||||||
|
sx: { width: 120 },
|
||||||
|
content: "_",
|
||||||
|
},
|
||||||
actions: {
|
actions: {
|
||||||
sx: { width: 76 },
|
sx: { width: 76 },
|
||||||
content: (
|
content: (
|
||||||
@ -172,21 +297,25 @@ export default function DocumentTables(props: IProps) {
|
|||||||
if (document.document_status !== EDocumentStatus.DEPOSITED) return null;
|
if (document.document_status !== EDocumentStatus.DEPOSITED) return null;
|
||||||
return {
|
return {
|
||||||
key: document.uid,
|
key: document.uid,
|
||||||
document_type: { sx: { width: 400 }, content: document.document_type?.name ?? "_" },
|
document_type: { sx: { width: 300 }, content: document.document_type?.name ?? "_" },
|
||||||
document_status: {
|
document_status: {
|
||||||
sx: { width: 107 },
|
sx: { width: 107 },
|
||||||
content: (
|
content: (
|
||||||
<Tag
|
<Tag
|
||||||
color={ETagColor.WARNING}
|
color={ETagColor.WARNING}
|
||||||
variant={ETagVariant.SEMI_BOLD}
|
variant={ETagVariant.SEMI_BOLD}
|
||||||
label={tradDocumentStatus[document.document_status].toUpperCase()}
|
label={tradDocumentStatus[document.document_status as EDocumentStatus].toUpperCase()}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
sx: { width: 107 },
|
sx: { width: 120 },
|
||||||
content: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
|
content: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
|
||||||
},
|
},
|
||||||
|
file: {
|
||||||
|
sx: { width: 120 },
|
||||||
|
content: document.files?.[0]?.file_name ?? "_",
|
||||||
|
},
|
||||||
actions: {
|
actions: {
|
||||||
sx: { width: 76 },
|
sx: { width: 76 },
|
||||||
content: (
|
content: (
|
||||||
@ -208,27 +337,31 @@ 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 {
|
||||||
key: document.uid,
|
key: document.uid,
|
||||||
document_type: { sx: { width: 400 }, content: document.document_type?.name ?? "_" },
|
document_type: { sx: { width: 300 }, content: document.document_type?.name ?? "_" },
|
||||||
document_status: {
|
document_status: {
|
||||||
sx: { width: 107 },
|
sx: { width: 107 },
|
||||||
content: (
|
content: (
|
||||||
<Tag
|
<Tag
|
||||||
color={ETagColor.SUCCESS}
|
color={ETagColor.SUCCESS}
|
||||||
variant={ETagVariant.SEMI_BOLD}
|
variant={ETagVariant.SEMI_BOLD}
|
||||||
label={tradDocumentStatus[document.document_status].toUpperCase()}
|
label={tradDocumentStatus[document.document_status as EDocumentStatus].toUpperCase()}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
sx: { width: 107 },
|
sx: { width: 120 },
|
||||||
content: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
|
content: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
|
||||||
},
|
},
|
||||||
|
file: {
|
||||||
|
sx: { width: 120 },
|
||||||
|
content: document.files?.[0]?.file_name ?? "_",
|
||||||
|
},
|
||||||
actions: {
|
actions: {
|
||||||
sx: { width: 76 },
|
sx: { width: 76 },
|
||||||
content: (
|
content: (
|
||||||
@ -241,13 +374,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(
|
||||||
@ -257,21 +394,25 @@ export default function DocumentTables(props: IProps) {
|
|||||||
if (document.document_status !== EDocumentStatus.REFUSED) return null;
|
if (document.document_status !== EDocumentStatus.REFUSED) return null;
|
||||||
return {
|
return {
|
||||||
key: document.uid,
|
key: document.uid,
|
||||||
document_type: { sx: { width: 400 }, content: document.document_type?.name ?? "_" },
|
document_type: { sx: { width: 300 }, content: document.document_type?.name ?? "_" },
|
||||||
document_status: {
|
document_status: {
|
||||||
sx: { width: 107 },
|
sx: { width: 107 },
|
||||||
content: (
|
content: (
|
||||||
<Tag
|
<Tag
|
||||||
color={ETagColor.ERROR}
|
color={ETagColor.ERROR}
|
||||||
variant={ETagVariant.SEMI_BOLD}
|
variant={ETagVariant.SEMI_BOLD}
|
||||||
label={tradDocumentStatus[document.document_status].toUpperCase()}
|
label={tradDocumentStatus[document.document_status as EDocumentStatus].toUpperCase()}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
sx: { width: 107 },
|
sx: { width: 120 },
|
||||||
content: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
|
content: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
|
||||||
},
|
},
|
||||||
|
file: {
|
||||||
|
sx: { width: 120 },
|
||||||
|
content: document.files?.[0]?.file_name ?? "_",
|
||||||
|
},
|
||||||
actions: { sx: { width: 76 }, content: "" },
|
actions: { sx: { width: 76 }, content: "" },
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@ -286,7 +427,7 @@ export default function DocumentTables(props: IProps) {
|
|||||||
return {
|
return {
|
||||||
key: document.uid,
|
key: document.uid,
|
||||||
document_type: {
|
document_type: {
|
||||||
sx: { width: 400 },
|
sx: { width: 300 },
|
||||||
content: formatName(document.files?.[0]?.file_name?.split(".")?.[0] ?? "") || "_",
|
content: formatName(document.files?.[0]?.file_name?.split(".")?.[0] ?? "") || "_",
|
||||||
},
|
},
|
||||||
document_status: {
|
document_status: {
|
||||||
@ -294,9 +435,13 @@ export default function DocumentTables(props: IProps) {
|
|||||||
content: getTagForSentDocument(document.document_status as EDocumentNotaryStatus),
|
content: getTagForSentDocument(document.document_status as EDocumentNotaryStatus),
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
sx: { width: 107 },
|
sx: { width: 120 },
|
||||||
content: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
|
content: document.updated_at ? new Date(document.updated_at).toLocaleDateString() : "_",
|
||||||
},
|
},
|
||||||
|
file: {
|
||||||
|
sx: { width: 120 },
|
||||||
|
content: document.files?.[0]?.file_name ?? "_",
|
||||||
|
},
|
||||||
actions: {
|
actions: {
|
||||||
sx: { width: 76 },
|
sx: { width: 76 },
|
||||||
content: (
|
content: (
|
||||||
@ -383,6 +528,10 @@ function getHeader(dateColumnTitle: string, isMobile: boolean): IHead[] {
|
|||||||
key: "date",
|
key: "date",
|
||||||
title: dateColumnTitle,
|
title: dateColumnTitle,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "file",
|
||||||
|
title: "Fichier",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "actions",
|
key: "actions",
|
||||||
title: "Action",
|
title: "Action",
|
||||||
|
@ -41,7 +41,9 @@ export default function EmailReminder(props: IProps) {
|
|||||||
}, [customer.uid, folderUid]);
|
}, [customer.uid, folderUid]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
/* TODO: review
|
||||||
fetchReminders();
|
fetchReminders();
|
||||||
|
*/
|
||||||
}, [fetchReminders]);
|
}, [fetchReminders]);
|
||||||
|
|
||||||
const remindersLength = useMemo(() => {
|
const remindersLength = useMemo(() => {
|
||||||
|
@ -14,7 +14,9 @@ import classes from "./classes.module.scss";
|
|||||||
import ClientBox from "./ClientBox";
|
import ClientBox from "./ClientBox";
|
||||||
import DocumentTables from "./DocumentTables";
|
import DocumentTables from "./DocumentTables";
|
||||||
import EmailReminder from "./EmailReminder";
|
import EmailReminder from "./EmailReminder";
|
||||||
import DocumentsNotary from "@Front/Api/LeCoffreApi/Notary/DocumentsNotary/DocumentsNotary";
|
|
||||||
|
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||||
|
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
folder: OfficeFolder;
|
folder: OfficeFolder;
|
||||||
@ -27,18 +29,19 @@ export default function ClientView(props: IProps) {
|
|||||||
const { folder, anchorStatus } = props;
|
const { folder, anchorStatus } = props;
|
||||||
|
|
||||||
const customers: ICustomer[] = useMemo(
|
const customers: ICustomer[] = useMemo(
|
||||||
() =>
|
() => {
|
||||||
folder?.customers
|
return folder?.customers
|
||||||
?.map((customer) => ({
|
?.map((customer: any) => ({
|
||||||
id: customer.uid ?? "",
|
id: customer.uid ?? '',
|
||||||
...customer,
|
...customer,
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => {
|
.sort((a: any, b: any) => {
|
||||||
return a.documents &&
|
return a.documents &&
|
||||||
a.documents.filter((document) => document.document_status === EDocumentStatus.DEPOSITED).length > 0
|
a.documents.filter((document: any) => document.document_status === EDocumentStatus.DEPOSITED).length > 0
|
||||||
? -1
|
? -1
|
||||||
: 1;
|
: 1;
|
||||||
}) ?? [],
|
}) ?? []
|
||||||
|
},
|
||||||
[folder],
|
[folder],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -58,27 +61,22 @@ export default function ClientView(props: IProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleClientDelete = useCallback(
|
const handleClientDelete = useCallback(
|
||||||
async (customerUid: string) => {
|
(customerUid: string) => {
|
||||||
if (!folder.uid) return;
|
if (!folder.uid) return;
|
||||||
const documentsNotary = await DocumentsNotary.getInstance().get({
|
LoaderService.getInstance().show();
|
||||||
where: { customer: { uid: customerUid }, folder: { uid: folder.uid } },
|
FolderService.getFolderByUid(folder.uid, false, false).then((process: any) => {
|
||||||
});
|
if (process) {
|
||||||
console.log(documentsNotary);
|
const folder: any = process.processData;
|
||||||
|
|
||||||
if (documentsNotary.length > 0) {
|
// FilterBy customerUid
|
||||||
documentsNotary.forEach(async (doc) => {
|
const customers = folder.customers.filter((uid: string) => uid !== customerUid);
|
||||||
await DocumentsNotary.getInstance().delete(doc.uid!);
|
|
||||||
|
FolderService.updateFolder(process, { customers: customers }).then(() => {
|
||||||
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
|
window.location.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
Folders.getInstance().put(
|
|
||||||
folder.uid,
|
|
||||||
OfficeFolder.hydrate<OfficeFolder>({
|
|
||||||
...folder,
|
|
||||||
customers: folder.customers?.filter((customer) => customer.uid !== customerUid),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
window.location.reload();
|
|
||||||
},
|
},
|
||||||
[folder],
|
[folder],
|
||||||
);
|
);
|
||||||
@ -110,7 +108,7 @@ export default function ClientView(props: IProps) {
|
|||||||
anchorStatus={anchorStatus}
|
anchorStatus={anchorStatus}
|
||||||
folderUid={folder.uid}
|
folderUid={folder.uid}
|
||||||
onDelete={handleClientDelete}
|
onDelete={handleClientDelete}
|
||||||
customerNote={folder.notes!.find((value) => value.customer?.uid === customer.uid) ?? null}
|
customerNote={folder.notes!.find((value: any) => value.customer?.uid === customer.uid) ?? null}
|
||||||
/>
|
/>
|
||||||
<div className={classes["button-container"]}>
|
<div className={classes["button-container"]}>
|
||||||
{anchorStatus === AnchorStatus.NOT_ANCHORED && (
|
{anchorStatus === AnchorStatus.NOT_ANCHORED && (
|
||||||
|
@ -5,11 +5,12 @@ import { IItem } from "@Front/Components/DesignSystem/Menu/MenuItem";
|
|||||||
import Tag, { ETagColor } from "@Front/Components/DesignSystem/Tag";
|
import Tag, { ETagColor } 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 { ArchiveBoxIcon, EllipsisHorizontalIcon, PaperAirplaneIcon, PencilSquareIcon, UsersIcon } from "@heroicons/react/24/outline";
|
import { ArchiveBoxIcon, EllipsisHorizontalIcon, PaperAirplaneIcon, PencilSquareIcon, ShieldCheckIcon, UsersIcon } from "@heroicons/react/24/outline";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
import { AnchorStatus } from "..";
|
import { AnchorStatus } from "..";
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
@ -24,6 +25,7 @@ type IProps = {
|
|||||||
|
|
||||||
export default function InformationSection(props: IProps) {
|
export default function InformationSection(props: IProps) {
|
||||||
const { folder, progress, onArchive, anchorStatus, isArchived } = props;
|
const { folder, progress, onArchive, anchorStatus, isArchived } = props;
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const menuItemsDekstop = useMemo(() => {
|
const menuItemsDekstop = useMemo(() => {
|
||||||
let elements: IItem[] = [];
|
let elements: IItem[] = [];
|
||||||
@ -43,6 +45,17 @@ export default function InformationSection(props: IProps) {
|
|||||||
link: Module.getInstance()
|
link: Module.getInstance()
|
||||||
.get()
|
.get()
|
||||||
.modules.pages.Folder.pages.EditInformations.props.path.replace("[folderUid]", folder?.uid ?? ""),
|
.modules.pages.Folder.pages.EditInformations.props.path.replace("[folderUid]", folder?.uid ?? ""),
|
||||||
|
hasSeparator: true,
|
||||||
|
};
|
||||||
|
const verifyDocumentElement = {
|
||||||
|
icon: <ShieldCheckIcon />,
|
||||||
|
text: "Vérifier le document",
|
||||||
|
onClick: () => {
|
||||||
|
const verifyPath = Module.getInstance()
|
||||||
|
.get()
|
||||||
|
.modules.pages.Folder.pages.VerifyDocuments.props.path.replace("[folderUid]", folder?.uid ?? "");
|
||||||
|
router.push(verifyPath);
|
||||||
|
},
|
||||||
hasSeparator: false,
|
hasSeparator: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,8 +65,11 @@ export default function InformationSection(props: IProps) {
|
|||||||
elements.push(modifyInformationsElement);
|
elements.push(modifyInformationsElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add verify document option
|
||||||
|
elements.push(verifyDocumentElement);
|
||||||
|
|
||||||
return elements;
|
return elements;
|
||||||
}, [anchorStatus, folder?.uid]);
|
}, [anchorStatus, folder?.uid, router]);
|
||||||
|
|
||||||
const menuItemsMobile = useMemo(() => {
|
const menuItemsMobile = useMemo(() => {
|
||||||
let elements: IItem[] = [];
|
let elements: IItem[] = [];
|
||||||
@ -75,6 +91,17 @@ export default function InformationSection(props: IProps) {
|
|||||||
.modules.pages.Folder.pages.EditInformations.props.path.replace("[folderUid]", folder?.uid ?? ""),
|
.modules.pages.Folder.pages.EditInformations.props.path.replace("[folderUid]", folder?.uid ?? ""),
|
||||||
hasSeparator: true,
|
hasSeparator: true,
|
||||||
};
|
};
|
||||||
|
const verifyDocumentElement = {
|
||||||
|
icon: <ShieldCheckIcon />,
|
||||||
|
text: "Vérifier le document",
|
||||||
|
onClick: () => {
|
||||||
|
const verifyPath = Module.getInstance()
|
||||||
|
.get()
|
||||||
|
.modules.pages.Folder.pages.VerifyDocuments.props.path.replace("[folderUid]", folder?.uid ?? "");
|
||||||
|
router.push(verifyPath);
|
||||||
|
},
|
||||||
|
hasSeparator: true,
|
||||||
|
};
|
||||||
|
|
||||||
// If the folder is not anchored, we can modify the collaborators and the informations
|
// If the folder is not anchored, we can modify the collaborators and the informations
|
||||||
if (anchorStatus === AnchorStatus.NOT_ANCHORED) {
|
if (anchorStatus === AnchorStatus.NOT_ANCHORED) {
|
||||||
@ -82,6 +109,9 @@ export default function InformationSection(props: IProps) {
|
|||||||
elements.push(modifyInformationsElement);
|
elements.push(modifyInformationsElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add verify document option
|
||||||
|
elements.push(verifyDocumentElement);
|
||||||
|
|
||||||
elements.push({
|
elements.push({
|
||||||
icon: <PaperAirplaneIcon />,
|
icon: <PaperAirplaneIcon />,
|
||||||
text: "Envoyer des documents",
|
text: "Envoyer des documents",
|
||||||
@ -101,7 +131,7 @@ export default function InformationSection(props: IProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return elements;
|
return elements;
|
||||||
}, [anchorStatus, folder?.uid, isArchived, onArchive]);
|
}, [anchorStatus, folder?.uid, isArchived, onArchive, router]);
|
||||||
return (
|
return (
|
||||||
<section className={classes["root"]}>
|
<section className={classes["root"]}>
|
||||||
<div className={classes["info-box1"]}>
|
<div className={classes["info-box1"]}>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
|
||||||
import Modal from "@Front/Components/DesignSystem/Modal";
|
import Modal from "@Front/Components/DesignSystem/Modal";
|
||||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
@ -6,6 +5,9 @@ import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { useCallback } from "react";
|
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 = {
|
type IProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
@ -21,8 +23,18 @@ export default function DeleteFolderModal(props: IProps) {
|
|||||||
if ((folder?.customers?.length ?? 0) > 0 || (folder?.documents?.length ?? 0) > 0)
|
if ((folder?.customers?.length ?? 0) > 0 || (folder?.documents?.length ?? 0) > 0)
|
||||||
return console.warn("Cannot delete folder with customers or documents");
|
return console.warn("Cannot delete folder with customers or documents");
|
||||||
|
|
||||||
return Folders.getInstance()
|
return new Promise<void>(
|
||||||
.delete(folder.uid)
|
(resolve: () => void) => {
|
||||||
|
LoaderService.getInstance().show();
|
||||||
|
FolderService.getFolderByUid(folder.uid!).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
FolderService.updateFolder(process, { isDeleted: 'true' }).then(() => {
|
||||||
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
.then(() => router.push(Module.getInstance().get().modules.pages.Folder.props.path))
|
.then(() => router.push(Module.getInstance().get().modules.pages.Folder.props.path))
|
||||||
.then(onClose);
|
.then(onClose);
|
||||||
}, [folder, router, onClose]);
|
}, [folder, router, onClose]);
|
||||||
|
@ -7,6 +7,8 @@ import React, { useCallback, useState } from "react";
|
|||||||
|
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
|
|
||||||
|
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
@ -19,11 +21,29 @@ export default function AnchoringModal(props: IProps) {
|
|||||||
const [isAnchoring, setIsAnchoring] = useState(false);
|
const [isAnchoring, setIsAnchoring] = useState(false);
|
||||||
|
|
||||||
const anchor = useCallback(() => {
|
const anchor = useCallback(() => {
|
||||||
|
setIsAnchoring(true);
|
||||||
|
|
||||||
|
// TODO: review
|
||||||
|
FolderService.getFolderByUid(folderUid as string).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
FolderService.updateFolder(process, { status: '' }).then(() => {
|
||||||
|
setIsAnchoring(false);
|
||||||
|
|
||||||
|
onAnchorSuccess();
|
||||||
|
if (onClose) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
const timeoutDelay = 9800;
|
const timeoutDelay = 9800;
|
||||||
const timeoutPromise = new Promise((resolve) => {
|
const timeoutPromise = new Promise((resolve) => {
|
||||||
setTimeout(resolve, timeoutDelay);
|
setTimeout(resolve, timeoutDelay);
|
||||||
});
|
});
|
||||||
setIsAnchoring(true);
|
setIsAnchoring(true);
|
||||||
|
|
||||||
return OfficeFolderAnchors.getInstance()
|
return OfficeFolderAnchors.getInstance()
|
||||||
.post(folderUid)
|
.post(folderUid)
|
||||||
.then(() => timeoutPromise)
|
.then(() => timeoutPromise)
|
||||||
@ -34,6 +54,7 @@ export default function AnchoringModal(props: IProps) {
|
|||||||
console.warn(e);
|
console.warn(e);
|
||||||
setIsAnchoring(false);
|
setIsAnchoring(false);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}, [folderUid, onAnchorSuccess, onClose]);
|
}, [folderUid, onAnchorSuccess, onClose]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -21,6 +21,11 @@ import InformationSection from "./InformationSection";
|
|||||||
import NoClientView from "./NoClientView";
|
import NoClientView from "./NoClientView";
|
||||||
import AnchoringProcessingInfo from "./elements/AnchoringProcessingInfo";
|
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 {
|
export enum AnchorStatus {
|
||||||
"VERIFIED_ON_CHAIN" = "VERIFIED_ON_CHAIN",
|
"VERIFIED_ON_CHAIN" = "VERIFIED_ON_CHAIN",
|
||||||
"ANCHORING" = "ANCHORING",
|
"ANCHORING" = "ANCHORING",
|
||||||
@ -59,6 +64,8 @@ export default function FolderInformation(props: IProps) {
|
|||||||
|
|
||||||
const fetchFolder = useCallback(async () => {
|
const fetchFolder = useCallback(async () => {
|
||||||
if (!folderUid) return;
|
if (!folderUid) return;
|
||||||
|
|
||||||
|
/*
|
||||||
const query = {
|
const query = {
|
||||||
q: {
|
q: {
|
||||||
deed: { include: { deed_type: true, document_types: true } },
|
deed: { include: { deed_type: true, document_types: true } },
|
||||||
@ -99,6 +106,46 @@ export default function FolderInformation(props: IProps) {
|
|||||||
return Folders.getInstance()
|
return Folders.getInstance()
|
||||||
.getByUid(folderUid, query)
|
.getByUid(folderUid, query)
|
||||||
.then((folder) => setFolder(folder));
|
.then((folder) => setFolder(folder));
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: review
|
||||||
|
LoaderService.getInstance().show();
|
||||||
|
return FolderService.getFolderByUid(folderUid).then(async (process: any) => {
|
||||||
|
if (process) {
|
||||||
|
const folder: any = process.processData;
|
||||||
|
|
||||||
|
await new Promise<void>((resolve: () => void) => {
|
||||||
|
NoteService.getNotes().then((processes: any) => {
|
||||||
|
if (processes.length > 0) {
|
||||||
|
let notes: any[] = processes.map((process: any) => process.processData);
|
||||||
|
|
||||||
|
// FilterBy folder.uid
|
||||||
|
notes = notes.filter((note: any) => note.folder.uid === folderUid);
|
||||||
|
|
||||||
|
if (notes.length > 0) {
|
||||||
|
folder.notes = notes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise<void>((resolve: () => void) => {
|
||||||
|
DocumentService.getDocuments().then((processes: any[]) => {
|
||||||
|
if (processes.length > 0) {
|
||||||
|
const documents: any[] = processes.map((process: any) => process.processData);
|
||||||
|
for (const customer of folder.customers) {
|
||||||
|
customer.documents = documents.filter((document: any) => document.depositor && document.depositor.uid === customer.uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setFolder(folder);
|
||||||
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
}, [folderUid]);
|
}, [folderUid]);
|
||||||
|
|
||||||
const fetchAnchorStatus = useCallback(() => {
|
const fetchAnchorStatus = useCallback(() => {
|
||||||
@ -113,7 +160,10 @@ export default function FolderInformation(props: IProps) {
|
|||||||
const fetchData = useCallback(() => {
|
const fetchData = useCallback(() => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
return fetchFolder()
|
return fetchFolder()
|
||||||
.then(() => fetchAnchorStatus())
|
.then(() => {
|
||||||
|
// TODO: review
|
||||||
|
//return fetchAnchorStatus()
|
||||||
|
})
|
||||||
.catch((e) => console.error(e))
|
.catch((e) => console.error(e))
|
||||||
.finally(() => setIsLoading(false));
|
.finally(() => setIsLoading(false));
|
||||||
}, [fetchAnchorStatus, fetchFolder]);
|
}, [fetchAnchorStatus, fetchFolder]);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import backgroundImage from "@Assets/images/background_refonte.svg";
|
import backgroundImage from "@Assets/images/background_refonte.svg";
|
||||||
import DocumentsNotary from "@Front/Api/LeCoffreApi/Notary/DocumentsNotary/DocumentsNotary";
|
import { EDocumentNotaryStatus } from "le-coffre-resources/dist/Notary/DocumentNotary";
|
||||||
import Folders from "@Front/Api/LeCoffreApi/Notary/Folders/Folders";
|
|
||||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
import DragAndDrop from "@Front/Components/DesignSystem/DragAndDrop";
|
import DragAndDrop from "@Front/Components/DesignSystem/DragAndDrop";
|
||||||
import Form from "@Front/Components/DesignSystem/Form";
|
import Form from "@Front/Components/DesignSystem/Form";
|
||||||
@ -19,6 +18,11 @@ import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|||||||
|
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
|
|
||||||
|
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||||
|
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
|
||||||
|
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
|
||||||
|
|
||||||
enum EClientSelection {
|
enum EClientSelection {
|
||||||
ALL_CLIENTS = "all_clients",
|
ALL_CLIENTS = "all_clients",
|
||||||
SELECTED_CLIENTS = "selected_clients",
|
SELECTED_CLIENTS = "selected_clients",
|
||||||
@ -59,6 +63,64 @@ export default function SendDocuments() {
|
|||||||
throw new Error("No clients selected");
|
throw new Error("No clients selected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoaderService.getInstance().show();
|
||||||
|
for (const selectedClient of selectedClients) {
|
||||||
|
for (const file of files) {
|
||||||
|
await new Promise<void>((resolve: () => void) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (event) => {
|
||||||
|
if (event.target?.result) {
|
||||||
|
const arrayBuffer = event.target.result as ArrayBuffer;
|
||||||
|
const uint8Array = new Uint8Array(arrayBuffer);
|
||||||
|
|
||||||
|
const fileBlob: any = {
|
||||||
|
type: file.type,
|
||||||
|
data: uint8Array
|
||||||
|
};
|
||||||
|
|
||||||
|
const fileData: any = {
|
||||||
|
file_blob: fileBlob,
|
||||||
|
file_name: file.name
|
||||||
|
};
|
||||||
|
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
|
||||||
|
|
||||||
|
FileService.createFile(fileData, validatorId).then((processCreated: any) => {
|
||||||
|
const fileUid: string = processCreated.processData.uid;
|
||||||
|
|
||||||
|
const documentData: any = {
|
||||||
|
folder: {
|
||||||
|
uid: folderUid as string
|
||||||
|
},
|
||||||
|
customer: {
|
||||||
|
uid: selectedClient as string
|
||||||
|
},
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
uid: fileUid
|
||||||
|
}
|
||||||
|
],
|
||||||
|
document_status: EDocumentNotaryStatus.SENT
|
||||||
|
};
|
||||||
|
|
||||||
|
DocumentService.createDocument(documentData, validatorId).then(() => resolve());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
|
||||||
|
router.push(
|
||||||
|
Module.getInstance()
|
||||||
|
.get()
|
||||||
|
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", folderUid as string),
|
||||||
|
);
|
||||||
|
setIsSending(false);
|
||||||
|
ToasterService.getInstance().success({ title: "Succès !", description: "Votre document a été envoyée avec succès." });
|
||||||
|
|
||||||
|
/*
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
selectedClients.map(async (customer) => {
|
selectedClients.map(async (customer) => {
|
||||||
const promises = files.map(async (file) => {
|
const promises = files.map(async (file) => {
|
||||||
@ -83,6 +145,7 @@ export default function SendDocuments() {
|
|||||||
);
|
);
|
||||||
setIsSending(false);
|
setIsSending(false);
|
||||||
ToasterService.getInstance().success({ title: "Succès !", description: "Votre document a été envoyée avec succès." });
|
ToasterService.getInstance().success({ title: "Succès !", description: "Votre document a été envoyée avec succès." });
|
||||||
|
*/
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setIsSending(false);
|
setIsSending(false);
|
||||||
console.warn("Error while sending files: ", error);
|
console.warn("Error while sending files: ", error);
|
||||||
@ -92,18 +155,14 @@ export default function SendDocuments() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const fetchFolder = useCallback(async () => {
|
const fetchFolder = useCallback(async () => {
|
||||||
Folders.getInstance()
|
LoaderService.getInstance().show();
|
||||||
.getByUid(folderUid as string, {
|
FolderService.getFolderByUid(folderUid as string).then((process: any) => {
|
||||||
q: {
|
if (process) {
|
||||||
customers: {
|
const folder: any = process.processData;
|
||||||
include: {
|
setFolder(folder);
|
||||||
contact: true,
|
LoaderService.getInstance().hide();
|
||||||
},
|
}
|
||||||
},
|
});
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((folder) => setFolder(folder))
|
|
||||||
.catch((e) => console.warn(e));
|
|
||||||
}, [folderUid]);
|
}, [folderUid]);
|
||||||
|
|
||||||
const onClientSelectionChange = useCallback(
|
const onClientSelectionChange = useCallback(
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import backgroundImage from "@Assets/images/background_refonte.svg";
|
import backgroundImage from "@Assets/images/background_refonte.svg";
|
||||||
import Customers from "@Front/Api/LeCoffreApi/Notary/Customers/Customers";
|
|
||||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
import Form, { IBaseField } from "@Front/Components/DesignSystem/Form";
|
import Form, { IBaseField } from "@Front/Components/DesignSystem/Form";
|
||||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||||
@ -15,9 +14,11 @@ import { Contact, Customer } from "le-coffre-resources/dist/Notary";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import classes from "./classes.module.scss";
|
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() {
|
export default function UpdateClient() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { folderUid, customerUid } = router.query;
|
const { folderUid, customerUid } = router.query;
|
||||||
@ -35,16 +36,14 @@ export default function UpdateClient() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchCustomer = async () => {
|
const fetchCustomer = async () => {
|
||||||
try {
|
try {
|
||||||
const customerData = await Customers.getInstance().getByUid(customerUid as string, {
|
LoaderService.getInstance().show();
|
||||||
contact: {
|
CustomerService.getCustomerByUid(customerUid as string).then((process: any) => {
|
||||||
include: {
|
if (process) {
|
||||||
address: true,
|
const customer: any = process.processData;
|
||||||
},
|
setCustomer(customer);
|
||||||
},
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
});
|
|
||||||
if (customerData) {
|
|
||||||
setCustomer(customerData);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch customer", error);
|
console.error("Failed to fetch customer", error);
|
||||||
}
|
}
|
||||||
@ -72,8 +71,17 @@ export default function UpdateClient() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await contact.validateOrReject?.({ groups: ["createCustomer"], forbidUnknownValues: false });
|
await contact.validateOrReject?.({ groups: ["createCustomer"], forbidUnknownValues: false });
|
||||||
await Customers.getInstance().put(customerUid as string, { contact });
|
|
||||||
|
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);
|
router.push(backwardPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (validationErrors) {
|
} catch (validationErrors) {
|
||||||
if (Array.isArray(validationErrors)) {
|
if (Array.isArray(validationErrors)) {
|
||||||
setValidationError(validationErrors as ValidationError[]);
|
setValidationError(validationErrors as ValidationError[]);
|
||||||
|
@ -11,7 +11,9 @@ import { NextRouter, useRouter } from "next/router";
|
|||||||
import BasePage from "../../Base";
|
import BasePage from "../../Base";
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import Note from "le-coffre-resources/dist/Customer/Note";
|
import Note from "le-coffre-resources/dist/Customer/Note";
|
||||||
import Notes from "@Front/Api/LeCoffreApi/Customer/Notes/Notes";
|
|
||||||
|
import NoteService from "src/common/Api/LeCoffreApi/sdk/NoteService";
|
||||||
|
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
|
||||||
|
|
||||||
type IProps = {};
|
type IProps = {};
|
||||||
|
|
||||||
@ -66,13 +68,11 @@ class UpdateCustomerNoteClass extends BasePage<IPropsClass, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override async componentDidMount() {
|
public override async componentDidMount() {
|
||||||
const query = {
|
LoaderService.getInstance().show();
|
||||||
q: {
|
NoteService.getNoteByUid(this.props.noteUid).then((process: any) => {
|
||||||
customer: "true",
|
if (process) {
|
||||||
folder: "true",
|
const note: any = process.processData;
|
||||||
},
|
|
||||||
};
|
|
||||||
const note = await Notes.getInstance().getByUid(this.props.noteUid, query);
|
|
||||||
// const folder = await Folders.getInstance().getByUid(this.props.folderUid, { note: true });
|
// const folder = await Folders.getInstance().getByUid(this.props.folderUid, { note: true });
|
||||||
//get the note of the folder that has customer_uid = this.props.customer.uid
|
//get the note of the folder that has customer_uid = this.props.customer.uid
|
||||||
// const folderNote = folder.notes?.find((note) => note.customer?.uid === this.props.customerUid);
|
// const folderNote = folder.notes?.find((note) => note.customer?.uid === this.props.customerUid);
|
||||||
@ -82,19 +82,22 @@ class UpdateCustomerNoteClass extends BasePage<IPropsClass, IState> {
|
|||||||
.get()
|
.get()
|
||||||
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", note.folder?.uid!),
|
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", note.folder?.uid!),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
LoaderService.getInstance().hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onFormSubmit(e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) {
|
private async onFormSubmit(e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) {
|
||||||
try {
|
try {
|
||||||
const note = {
|
LoaderService.getInstance().show();
|
||||||
content: values["content"],
|
NoteService.getNoteByUid(this.props.noteUid).then((process: any) => {
|
||||||
};
|
if (process) {
|
||||||
|
NoteService.updateNote(process, { content: values["content"] }).then(() => {
|
||||||
await Notes.getInstance().put(this.props.noteUid, note);
|
|
||||||
this.props.router.push(this.state.backwardPath);
|
this.props.router.push(this.state.backwardPath);
|
||||||
|
});
|
||||||
// await Folders.getInstance().put(this.props.folderUid, values);
|
}
|
||||||
// this.props.router.push(this.backwardPath);
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,9 @@ import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoub
|
|||||||
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||||
import { isArray } from "class-validator";
|
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() {
|
export default function UpdateFolderMetadata() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -46,14 +49,18 @@ export default function UpdateFolderMetadata() {
|
|||||||
setValidationError(validationErrors as ValidationError[]);
|
setValidationError(validationErrors as ValidationError[]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Folders.getInstance().put(folderUid, newValues);
|
LoaderService.getInstance().show();
|
||||||
const url = Module.getInstance()
|
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()
|
.get()
|
||||||
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", folderUid);
|
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", folderUid));
|
||||||
|
});
|
||||||
router.push(url);
|
}
|
||||||
|
});
|
||||||
} catch (backError) {
|
} catch (backError) {
|
||||||
if (!Array.isArray(backError)) return;
|
if (!Array.isArray(backError)) return;
|
||||||
setValidationError(backError);
|
setValidationError(backError);
|
||||||
@ -63,16 +70,14 @@ export default function UpdateFolderMetadata() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!folderUid || isArray(folderUid)) return;
|
if (!folderUid || isArray(folderUid)) return;
|
||||||
const query = {
|
LoaderService.getInstance().show();
|
||||||
q: {
|
FolderService.getFolderByUid(folderUid).then((process: any) => {
|
||||||
deed: { include: { deed_type: true } },
|
if (process) {
|
||||||
office: true,
|
const folder: any = process.processData;
|
||||||
customers: { include: { contact: true } },
|
setSelectedFolder(folder);
|
||||||
},
|
setTimeout(() => LoaderService.getInstance().hide(), 2000);
|
||||||
};
|
}
|
||||||
Folders.getInstance()
|
});
|
||||||
.getByUid(folderUid, query)
|
|
||||||
.then((folder) => setSelectedFolder(folder));
|
|
||||||
}, [folderUid]);
|
}, [folderUid]);
|
||||||
|
|
||||||
const backwardPath = Module.getInstance()
|
const backwardPath = Module.getInstance()
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import LeftArrowIcon from "@Assets/Icons/left-arrow.svg";
|
import LeftArrowIcon from "@Assets/Icons/left-arrow.svg";
|
||||||
import RightArrowIcon from "@Assets/Icons/right-arrow.svg";
|
import RightArrowIcon from "@Assets/Icons/right-arrow.svg";
|
||||||
import Documents from "@Front/Api/LeCoffreApi/Notary/Documents/Documents";
|
|
||||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
import FilePreview from "@Front/Components/DesignSystem/FilePreview";
|
import FilePreview from "@Front/Components/DesignSystem/FilePreview";
|
||||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||||
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
|
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import { Document, File } from "le-coffre-resources/dist/Notary";
|
import { Document } from "le-coffre-resources/dist/Notary";
|
||||||
import { EDocumentStatus } from "le-coffre-resources/dist/Notary/Document";
|
import { EDocumentStatus } from "le-coffre-resources/dist/Notary/Document";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { NextRouter, useRouter } from "next/router";
|
import { NextRouter, useRouter } from "next/router";
|
||||||
@ -15,10 +14,13 @@ import React from "react";
|
|||||||
|
|
||||||
import BasePage from "../../Base";
|
import BasePage from "../../Base";
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import Files from "@Front/Api/LeCoffreApi/Notary/Files/Files";
|
|
||||||
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||||
import MessageBox from "@Front/Components/Elements/MessageBox";
|
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 IProps = {};
|
||||||
type IPropsClass = {
|
type IPropsClass = {
|
||||||
documentUid: string;
|
documentUid: string;
|
||||||
@ -31,7 +33,7 @@ type IState = {
|
|||||||
isValidateModalVisible: boolean;
|
isValidateModalVisible: boolean;
|
||||||
refuseText: string;
|
refuseText: string;
|
||||||
selectedFileIndex: number;
|
selectedFileIndex: number;
|
||||||
selectedFile: File | null;
|
selectedFile: { uid: string; file_name: string; file_blob: FileBlob } | null;
|
||||||
validatedPercentage: number;
|
validatedPercentage: number;
|
||||||
document: Document | null;
|
document: Document | null;
|
||||||
fileBlob: Blob | null;
|
fileBlob: Blob | null;
|
||||||
@ -89,10 +91,10 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={classes["file-container"]}>
|
<div className={classes["file-container"]}>
|
||||||
{this.state.selectedFile.mimetype === "application/pdf" ||
|
{this.state.selectedFile.file_blob.type === "application/pdf" ||
|
||||||
this.state.selectedFile.mimetype === "image/jpeg" ||
|
this.state.selectedFile.file_blob.type === "image/jpeg" ||
|
||||||
this.state.selectedFile.mimetype === "image/png" ||
|
this.state.selectedFile.file_blob.type === "image/png" ||
|
||||||
this.state.selectedFile.mimetype === "image/jpg" ? (
|
this.state.selectedFile.file_blob.type === "image/jpg" ? (
|
||||||
<FilePreview
|
<FilePreview
|
||||||
href={this.state.fileBlob ? URL.createObjectURL(this.state.fileBlob) : ""}
|
href={this.state.fileBlob ? URL.createObjectURL(this.state.fileBlob) : ""}
|
||||||
fileName={this.state.selectedFile.file_name}
|
fileName={this.state.selectedFile.file_name}
|
||||||
@ -197,14 +199,24 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
|||||||
|
|
||||||
override async componentDidMount() {
|
override async componentDidMount() {
|
||||||
try {
|
try {
|
||||||
const document = await Documents.getInstance().getByUid(this.props.documentUid, {
|
const document: any = await new Promise((resolve: (document: any) => void) => {
|
||||||
files: {
|
DocumentService.getDocumentByUid(this.props.documentUid).then(async (process: any) => {
|
||||||
where: { archived_at: null },
|
if (process) {
|
||||||
},
|
const document: any = process.processData;
|
||||||
document_type: true,
|
|
||||||
folder: true,
|
if (document.files && document.files.length > 0) {
|
||||||
depositor: true,
|
const files: any[] = [];
|
||||||
|
for (const file of document.files) {
|
||||||
|
files.push((await FileService.getFileByUid(file.uid)).processData);
|
||||||
|
}
|
||||||
|
document.files = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(document);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
document,
|
document,
|
||||||
@ -226,8 +238,9 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
|||||||
|
|
||||||
private async getFilePreview(): Promise<void> {
|
private async getFilePreview(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const fileBlob: Blob = await Files.getInstance().download(this.state.selectedFile?.uid as string);
|
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({
|
this.setState({
|
||||||
fileBlob,
|
fileBlob,
|
||||||
});
|
});
|
||||||
@ -237,16 +250,16 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private downloadFile() {
|
private downloadFile() {
|
||||||
if (!this.state.fileBlob) return;
|
if (!this.state.fileBlob || !this.state.selectedFile) return;
|
||||||
const url = window.URL.createObjectURL(this.state.fileBlob);
|
|
||||||
const a = document.createElement("a");
|
const url = URL.createObjectURL(this.state.fileBlob);
|
||||||
a.style.display = "none";
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
// the filename you want
|
a.download = this.state.selectedFile.file_name;
|
||||||
a.download = this.state.selectedFile?.file_name as string;
|
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
window.URL.revokeObjectURL(url);
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRandomPercentageForOcr() {
|
private getRandomPercentageForOcr() {
|
||||||
@ -309,8 +322,9 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
|||||||
|
|
||||||
private async refuseDocument() {
|
private async refuseDocument() {
|
||||||
try {
|
try {
|
||||||
await Documents.getInstance().refuse(this.props.documentUid, this.state.refuseText);
|
DocumentService.getDocumentByUid(this.props.documentUid).then((process: any) => {
|
||||||
|
if (process) {
|
||||||
|
DocumentService.updateDocument(process, { document_status: EDocumentStatus.REFUSED, refused_reason: this.state.refuseText }).then(() => {
|
||||||
this.props.router.push(
|
this.props.router.push(
|
||||||
Module.getInstance()
|
Module.getInstance()
|
||||||
.get()
|
.get()
|
||||||
@ -318,6 +332,9 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
|||||||
"?customerUid=" +
|
"?customerUid=" +
|
||||||
this.state.document?.depositor?.uid,
|
this.state.document?.depositor?.uid,
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
@ -325,10 +342,9 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
|||||||
|
|
||||||
private async validateDocument() {
|
private async validateDocument() {
|
||||||
try {
|
try {
|
||||||
await Documents.getInstance().put(this.props.documentUid, {
|
DocumentService.getDocumentByUid(this.props.documentUid).then((process: any) => {
|
||||||
document_status: EDocumentStatus.VALIDATED,
|
if (process) {
|
||||||
});
|
DocumentService.updateDocument(process, { document_status: EDocumentStatus.VALIDATED }).then(() => {
|
||||||
|
|
||||||
this.props.router.push(
|
this.props.router.push(
|
||||||
Module.getInstance()
|
Module.getInstance()
|
||||||
.get()
|
.get()
|
||||||
@ -336,6 +352,9 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
|||||||
"?customerUid=" +
|
"?customerUid=" +
|
||||||
this.state.document?.depositor?.uid,
|
this.state.document?.depositor?.uid,
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,10 @@ import Image from "next/image";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
|
|
||||||
|
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
|
||||||
|
|
||||||
export default function Folder() {
|
export default function Folder() {
|
||||||
const [_isArchivedModalOpen, _setIsArchivedModalOpen] = useState(true);
|
const [_isArchivedModalOpen, _setIsArchivedModalOpen] = useState(true);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -23,6 +24,25 @@ export default function Folder() {
|
|||||||
const { user: activeUser } = useUser();
|
const { user: activeUser } = useUser();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// TODO: review
|
||||||
|
FolderService.getFolders().then((processes: any[]) => {
|
||||||
|
if (processes.length > 0) {
|
||||||
|
let folders: any[] = processes.map((process: any) => process.processData);
|
||||||
|
|
||||||
|
// FilterBy status
|
||||||
|
folders = folders.filter((folder: any) => folder.status === EFolderStatus.LIVE);
|
||||||
|
|
||||||
|
if (folders.length > 0) {
|
||||||
|
router.push(
|
||||||
|
Module.getInstance()
|
||||||
|
.get()
|
||||||
|
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", folders[0].uid)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
Folders.getInstance()
|
Folders.getInstance()
|
||||||
.get({
|
.get({
|
||||||
q: {
|
q: {
|
||||||
@ -38,6 +58,7 @@ export default function Folder() {
|
|||||||
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", folders[0]?.uid ?? ""),
|
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", folders[0]?.uid ?? ""),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}, [router]);
|
}, [router]);
|
||||||
return (
|
return (
|
||||||
<DefaultNotaryDashboard title={"Dossier"} mobileBackText={"Liste des dossiers"}>
|
<DefaultNotaryDashboard title={"Dossier"} mobileBackText={"Liste des dossiers"}>
|
||||||
|
@ -41,12 +41,17 @@ export default function StepEmail(props: IProps) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const error = router.query["error"];
|
const error = router.query["error"];
|
||||||
const redirectUserOnConnection = useCallback(() => {
|
const redirectUserOnConnection = useCallback(() => {
|
||||||
|
/* TODO: review
|
||||||
const variables = FrontendVariables.getInstance();
|
const variables = FrontendVariables.getInstance();
|
||||||
router.push(
|
router.push(
|
||||||
`${variables.IDNOT_BASE_URL + variables.IDNOT_AUTHORIZE_ENDPOINT}?client_id=${variables.IDNOT_CLIENT_ID}&redirect_uri=${
|
`${variables.IDNOT_BASE_URL + variables.IDNOT_AUTHORIZE_ENDPOINT}?client_id=${variables.IDNOT_CLIENT_ID}&redirect_uri=${
|
||||||
variables.FRONT_APP_HOST
|
variables.FRONT_APP_HOST
|
||||||
}/authorized-client&scope=openid,profile&response_type=code`,
|
}/authorized-client&scope=openid,profile&response_type=code`,
|
||||||
);
|
);
|
||||||
|
*/
|
||||||
|
router.push(
|
||||||
|
`https://qual-connexion.idnot.fr/user/IdPOAuth2/authorize/idnot_idp_v1?client_id=B3CE56353EDB15A9&redirect_uri=http://local.lecoffreio.4nkweb:3000/authorized-client&scope=openid,profile&response_type=code`,
|
||||||
|
);
|
||||||
}, [router]);
|
}, [router]);
|
||||||
|
|
||||||
const openErrorModal = useCallback((index: number) => {
|
const openErrorModal = useCallback((index: number) => {
|
||||||
@ -94,12 +99,14 @@ export default function StepEmail(props: IProps) {
|
|||||||
Pour les clients :
|
Pour les clients :
|
||||||
</Typography>
|
</Typography>
|
||||||
<Form className={classes["form"]} onSubmit={onSubmit}>
|
<Form className={classes["form"]} onSubmit={onSubmit}>
|
||||||
|
{/*
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Renseigner votre email"
|
placeholder="Renseigner votre email"
|
||||||
label="E-mail"
|
label="E-mail"
|
||||||
name="email"
|
name="email"
|
||||||
validationError={validationErrors.find((err) => err.property === "email")}
|
validationError={validationErrors.find((err) => err.property === "email")}
|
||||||
/>
|
/>
|
||||||
|
*/}
|
||||||
<Button type="submit">Se connecter</Button>
|
<Button type="submit">Se connecter</Button>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,11 +50,14 @@ export default function Login() {
|
|||||||
|
|
||||||
const onEmailFormSubmit = useCallback(async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
const onEmailFormSubmit = useCallback(async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
||||||
try {
|
try {
|
||||||
|
/* TODO: review
|
||||||
if (!values["email"]) return;
|
if (!values["email"]) return;
|
||||||
setEmail(values["email"]);
|
setEmail(values["email"]);
|
||||||
const res = await Auth.getInstance().mailVerifySms({ email: values["email"] });
|
const res = await Auth.getInstance().mailVerifySms({ email: values["email"] });
|
||||||
setPartialPhoneNumber(res.partialPhoneNumber);
|
setPartialPhoneNumber(res.partialPhoneNumber);
|
||||||
setTotpCodeUid(res.totpCodeUid);
|
setTotpCodeUid(res.totpCodeUid);
|
||||||
|
*/
|
||||||
|
|
||||||
setStep(LoginStep.TOTP);
|
setStep(LoginStep.TOTP);
|
||||||
setValidationErrors([]);
|
setValidationErrors([]);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
@ -11,15 +11,25 @@ import JwtService from "@Front/Services/JwtService/JwtService";
|
|||||||
import UserStore from "@Front/Stores/UserStore";
|
import UserStore from "@Front/Stores/UserStore";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
|
|
||||||
|
import AuthModal from "src/sdk/AuthModal";
|
||||||
|
|
||||||
export default function LoginCallBack() {
|
export default function LoginCallBack() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getUser() {
|
async function getUser() {
|
||||||
|
// TODO: review
|
||||||
|
// HACK: If start with http://local.lecoffreio.4nkweb:3000/authorized-client
|
||||||
|
// Replace with http://localhost:3000/authorized-client
|
||||||
|
if (window.location.href.startsWith('http://local.lecoffreio.4nkweb:3000/authorized-client')) {
|
||||||
|
window.location.href = window.location.href.replace('http://local.lecoffreio.4nkweb:3000/authorized-client', 'http://localhost:3000/authorized-client');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const code = router.query["code"];
|
const code = router.query["code"];
|
||||||
if (code) {
|
if (code) {
|
||||||
try {
|
try {
|
||||||
@ -28,10 +38,12 @@ export default function LoginCallBack() {
|
|||||||
await UserStore.instance.connect(token.accessToken, token.refreshToken);
|
await UserStore.instance.connect(token.accessToken, token.refreshToken);
|
||||||
const jwt = JwtService.getInstance().decodeJwt();
|
const jwt = JwtService.getInstance().decodeJwt();
|
||||||
if (!jwt) return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
|
if (!jwt) return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
|
||||||
if (!jwt.rules.includes("GET folders")) {
|
if (jwt.rules && !jwt.rules.includes("GET folders")) {
|
||||||
return router.push(Module.getInstance().get().modules.pages.Subscription.pages.New.props.path);
|
return router.push(Module.getInstance().get().modules.pages.Subscription.pages.New.props.path);
|
||||||
}
|
}
|
||||||
return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
|
setIsAuthModalOpen(true);
|
||||||
|
//return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
|
||||||
|
return;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e.http_status === 401 && e.message === "Email not found") {
|
if (e.http_status === 401 && e.message === "Email not found") {
|
||||||
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=3");
|
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=3");
|
||||||
@ -42,6 +54,7 @@ export default function LoginCallBack() {
|
|||||||
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
|
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const refreshToken = CookieService.getInstance().getCookie("leCoffreRefreshToken");
|
const refreshToken = CookieService.getInstance().getCookie("leCoffreRefreshToken");
|
||||||
if (!refreshToken) return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
|
if (!refreshToken) return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
|
||||||
const isTokenRefreshed = await JwtService.getInstance().refreshToken(refreshToken);
|
const isTokenRefreshed = await JwtService.getInstance().refreshToken(refreshToken);
|
||||||
@ -51,7 +64,9 @@ export default function LoginCallBack() {
|
|||||||
return router.push(Module.getInstance().get().modules.pages.Subscription.pages.New.props.path);
|
return router.push(Module.getInstance().get().modules.pages.Subscription.pages.New.props.path);
|
||||||
}
|
}
|
||||||
if (isTokenRefreshed) {
|
if (isTokenRefreshed) {
|
||||||
return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
|
setIsAuthModalOpen(true);
|
||||||
|
//return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=2");
|
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=2");
|
||||||
}
|
}
|
||||||
@ -76,6 +91,13 @@ export default function LoginCallBack() {
|
|||||||
description="Notre équipe de support est là pour vous aider."
|
description="Notre équipe de support est là pour vous aider."
|
||||||
button={{ text: "Contacter l'administrateur", link: "mailto:support@lecoffre.io" }}
|
button={{ text: "Contacter l'administrateur", link: "mailto:support@lecoffre.io" }}
|
||||||
/>
|
/>
|
||||||
|
{isAuthModalOpen && <AuthModal
|
||||||
|
isOpen={isAuthModalOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsAuthModalOpen(false);
|
||||||
|
router.push(Module.getInstance().get().modules.pages.Folder.props.path);
|
||||||
|
}}
|
||||||
|
/>}
|
||||||
</div>
|
</div>
|
||||||
</DefaultDoubleSidePage>
|
</DefaultDoubleSidePage>
|
||||||
);
|
);
|
||||||
|
@ -5,6 +5,7 @@ import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
|||||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||||
import DefaultRolesDashboard from "@Front/Components/LayoutTemplates/DefaultRoleDashboard";
|
import DefaultRolesDashboard from "@Front/Components/LayoutTemplates/DefaultRoleDashboard";
|
||||||
|
import { ToasterService } from "@Front/Components/DesignSystem/Toaster";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import { Office, OfficeRole } from "le-coffre-resources/dist/Admin";
|
import { Office, OfficeRole } from "le-coffre-resources/dist/Admin";
|
||||||
import { useRouter } from "next/router";
|
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 { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
|
||||||
import { ValidationError } from "class-validator";
|
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 = {};
|
type IProps = {};
|
||||||
export default function RolesCreate(props: IProps) {
|
export default function RolesCreate(props: IProps) {
|
||||||
const [hasChanged, setHasChanged] = useState<boolean>(false);
|
const [hasChanged, setHasChanged] = useState<boolean>(false);
|
||||||
@ -26,12 +30,17 @@ export default function RolesCreate(props: IProps) {
|
|||||||
const onSubmitHandler = useCallback(
|
const onSubmitHandler = useCallback(
|
||||||
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
||||||
const jwt = JwtService.getInstance().decodeJwt();
|
const jwt = JwtService.getInstance().decodeJwt();
|
||||||
|
|
||||||
|
// TODO: review
|
||||||
|
const officeId = 'demo_notary_office_id'; //jwt?.office_Id;
|
||||||
|
|
||||||
const officeRole = OfficeRole.hydrate<OfficeRole>({
|
const officeRole = OfficeRole.hydrate<OfficeRole>({
|
||||||
name: values["name"],
|
name: values["name"],
|
||||||
office: Office.hydrate<Office>({
|
office: Office.hydrate<Office>({
|
||||||
uid: jwt?.office_Id,
|
uid: officeId,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await officeRole.validateOrReject?.({ groups: ["createOfficeRole"], forbidUnknownValues: true });
|
await officeRole.validateOrReject?.({ groups: ["createOfficeRole"], forbidUnknownValues: true });
|
||||||
} catch (validationErrors: Array<ValidationError> | any) {
|
} catch (validationErrors: Array<ValidationError> | any) {
|
||||||
@ -39,6 +48,29 @@ export default function RolesCreate(props: IProps) {
|
|||||||
setValidationError(validationErrors as ValidationError[]);
|
setValidationError(validationErrors as ValidationError[]);
|
||||||
return;
|
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 {
|
try {
|
||||||
const role = await OfficeRoles.getInstance().post(
|
const role = await OfficeRoles.getInstance().post(
|
||||||
OfficeRole.hydrate<OfficeRole>({
|
OfficeRole.hydrate<OfficeRole>({
|
||||||
@ -55,6 +87,7 @@ export default function RolesCreate(props: IProps) {
|
|||||||
setValidationError(validationErrors as ValidationError[]);
|
setValidationError(validationErrors as ValidationError[]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
},
|
},
|
||||||
[router],
|
[router],
|
||||||
);
|
);
|
||||||
|
@ -13,6 +13,9 @@ import React from "react";
|
|||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import RulesGroups from "@Front/Api/LeCoffreApi/Admin/RulesGroups/RulesGroups";
|
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 & {
|
type RuleGroupsCheckbox = RulesGroup & {
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
};
|
};
|
||||||
@ -39,24 +42,60 @@ export default function RolesInformations() {
|
|||||||
setSelectAll(false);
|
setSelectAll(false);
|
||||||
async function getUser() {
|
async function getUser() {
|
||||||
if (!roleUid) return;
|
if (!roleUid) return;
|
||||||
|
|
||||||
|
/*
|
||||||
const role = await OfficeRoles.getInstance().getByUid(roleUid as string, {
|
const role = await OfficeRoles.getInstance().getByUid(roleUid as string, {
|
||||||
q: {
|
q: {
|
||||||
rules: true,
|
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({
|
const rulesGroups = await RulesGroups.getInstance().get({
|
||||||
include: {
|
include: {
|
||||||
rules: true,
|
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;
|
if (!role) return;
|
||||||
setRoleSelected(role);
|
setRoleSelected(role);
|
||||||
|
|
||||||
|
// TODO: review
|
||||||
if (!role.rules) return;
|
if (!role.rules) return;
|
||||||
const rulesCheckboxes = rulesGroups
|
const rulesCheckboxes = rulesGroups
|
||||||
.map((ruleGroup) => {
|
.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: true };
|
||||||
}
|
}
|
||||||
return { ...ruleGroup, checked: false };
|
return { ...ruleGroup, checked: false };
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
"ClientDashboard": {
|
"ClientDashboard": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"props": {
|
"props": {
|
||||||
"path": "/client-dashboard/[folderUid]",
|
"path": "/client-dashboard/[folderUid]/profile/[profileUid]",
|
||||||
"labelKey": "client-dashboard"
|
"labelKey": "client-dashboard"
|
||||||
},
|
},
|
||||||
"pages": {
|
"pages": {
|
||||||
@ -160,6 +160,13 @@
|
|||||||
"path": "/folders/select",
|
"path": "/folders/select",
|
||||||
"labelKey": "select_folder"
|
"labelKey": "select_folder"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"VerifyDocuments": {
|
||||||
|
"enabled": true,
|
||||||
|
"props": {
|
||||||
|
"path": "/folders/[folderUid]/verify-documents",
|
||||||
|
"labelKey": "verify_documents"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
"ClientDashboard": {
|
"ClientDashboard": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"props": {
|
"props": {
|
||||||
"path": "/client-dashboard/[folderUid]",
|
"path": "/client-dashboard/[folderUid]/profile/[profileUid]",
|
||||||
"labelKey": "client-dashboard"
|
"labelKey": "client-dashboard"
|
||||||
},
|
},
|
||||||
"pages": {
|
"pages": {
|
||||||
@ -160,6 +160,13 @@
|
|||||||
"path": "/folders/select",
|
"path": "/folders/select",
|
||||||
"labelKey": "select_folder"
|
"labelKey": "select_folder"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"VerifyDocuments": {
|
||||||
|
"enabled": true,
|
||||||
|
"props": {
|
||||||
|
"path": "/folders/[folderUid]/verify-documents",
|
||||||
|
"labelKey": "verify_documents"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
"ClientDashboard": {
|
"ClientDashboard": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"props": {
|
"props": {
|
||||||
"path": "/client-dashboard/[folderUid]",
|
"path": "/client-dashboard/[folderUid]/profile/[profileUid]",
|
||||||
"labelKey": "client-dashboard"
|
"labelKey": "client-dashboard"
|
||||||
},
|
},
|
||||||
"pages": {
|
"pages": {
|
||||||
@ -160,6 +160,13 @@
|
|||||||
"path": "/folders/select",
|
"path": "/folders/select",
|
||||||
"labelKey": "select_folder"
|
"labelKey": "select_folder"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"VerifyDocuments": {
|
||||||
|
"enabled": true,
|
||||||
|
"props": {
|
||||||
|
"path": "/folders/[folderUid]/verify-documents",
|
||||||
|
"labelKey": "verify_documents"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
"ClientDashboard": {
|
"ClientDashboard": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"props": {
|
"props": {
|
||||||
"path": "/client-dashboard/[folderUid]",
|
"path": "/client-dashboard/[folderUid]/profile/[profileUid]",
|
||||||
"labelKey": "client-dashboard"
|
"labelKey": "client-dashboard"
|
||||||
},
|
},
|
||||||
"pages": {
|
"pages": {
|
||||||
@ -160,6 +160,13 @@
|
|||||||
"path": "/folders/select",
|
"path": "/folders/select",
|
||||||
"labelKey": "select_folder"
|
"labelKey": "select_folder"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"VerifyDocuments": {
|
||||||
|
"enabled": true,
|
||||||
|
"props": {
|
||||||
|
"path": "/folders/[folderUid]/verify-documents",
|
||||||
|
"labelKey": "verify_documents"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -27,7 +27,9 @@ export class FrontendVariables {
|
|||||||
|
|
||||||
public HOTJAR_SITE_ID!: number;
|
public HOTJAR_SITE_ID!: number;
|
||||||
|
|
||||||
public HOJAR_VERSION!: number;
|
public HOTJAR_VERSION!: number;
|
||||||
|
|
||||||
|
public _4NK_URL!: string;
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ export default function useUser() {
|
|||||||
const [user, setUser] = useState<User | null>();
|
const [user, setUser] = useState<User | null>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
/* TODO: review
|
||||||
const decodedJwt = JwtService.getInstance().decodeJwt();
|
const decodedJwt = JwtService.getInstance().decodeJwt();
|
||||||
if (!decodedJwt) return;
|
if (!decodedJwt) return;
|
||||||
Users.getInstance()
|
Users.getInstance()
|
||||||
@ -18,6 +19,7 @@ export default function useUser() {
|
|||||||
.then((user) => {
|
.then((user) => {
|
||||||
setUser(user);
|
setUser(user);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
347
src/front/Services/PdfService/index.ts
Normal file
347
src/front/Services/PdfService/index.ts
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import { PDFDocument, rgb, StandardFonts } from 'pdf-lib';
|
||||||
|
|
||||||
|
const separator = '\x1f';
|
||||||
|
|
||||||
|
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 pdfDoc = await PDFDocument.create();
|
||||||
|
const page = pdfDoc.addPage([595, 842]); // A4 size
|
||||||
|
const { width, height } = page.getSize();
|
||||||
|
|
||||||
|
const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
||||||
|
const helveticaBoldFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
|
||||||
|
const helveticaObliqueFont = await pdfDoc.embedFont(StandardFonts.HelveticaOblique);
|
||||||
|
|
||||||
|
let y = height - 40; // Start 40pt from the top
|
||||||
|
const leftMargin = 20;
|
||||||
|
const rightSectionX = 320;
|
||||||
|
const lineSpacing = 16;
|
||||||
|
|
||||||
|
// Notary Information Section (Top Left)
|
||||||
|
page.drawText('Notaire Validateur:', {
|
||||||
|
x: leftMargin,
|
||||||
|
y: y,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaBoldFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
y -= lineSpacing;
|
||||||
|
page.drawText(certificateData.notary.name, {
|
||||||
|
x: leftMargin,
|
||||||
|
y: y,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Customer Information Section (Top Right)
|
||||||
|
let yRight = height - 40;
|
||||||
|
page.drawText('Fournisseur de Document:', {
|
||||||
|
x: rightSectionX,
|
||||||
|
y: yRight,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaBoldFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
yRight -= lineSpacing;
|
||||||
|
page.drawText(`${certificateData.customer.firstName} ${certificateData.customer.lastName}`, {
|
||||||
|
x: rightSectionX,
|
||||||
|
y: yRight,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
yRight -= lineSpacing;
|
||||||
|
page.drawText(certificateData.customer.email, {
|
||||||
|
x: rightSectionX,
|
||||||
|
y: yRight,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
yRight -= lineSpacing;
|
||||||
|
page.drawText(certificateData.customer.postalAddress, {
|
||||||
|
x: rightSectionX,
|
||||||
|
y: yRight,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Centered Title
|
||||||
|
y -= 4 * lineSpacing; // Add more space between header and title
|
||||||
|
const titleText = 'Certificat de Validation de Document';
|
||||||
|
const titleWidth = helveticaBoldFont.widthOfTextAtSize(titleText, 20);
|
||||||
|
page.drawText(titleText, {
|
||||||
|
x: (width - titleWidth) / 2,
|
||||||
|
y: y,
|
||||||
|
size: 20,
|
||||||
|
font: helveticaBoldFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
y -= lineSpacing;
|
||||||
|
|
||||||
|
// Add a line separator
|
||||||
|
page.drawLine({
|
||||||
|
start: { x: leftMargin, y: y },
|
||||||
|
end: { x: width - leftMargin, y: y },
|
||||||
|
thickness: 0.5,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
y -= lineSpacing;
|
||||||
|
|
||||||
|
// Document Information Section
|
||||||
|
page.drawText('Informations du Document', {
|
||||||
|
x: leftMargin,
|
||||||
|
y: y,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaBoldFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
y -= lineSpacing;
|
||||||
|
page.drawText(`UID du Dossier: ${certificateData.folderUid}`, {
|
||||||
|
x: leftMargin,
|
||||||
|
y: y,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
y -= lineSpacing;
|
||||||
|
page.drawText(`Type de Document: ${certificateData.metadata.documentType}`, {
|
||||||
|
x: leftMargin,
|
||||||
|
y: y,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
y -= lineSpacing;
|
||||||
|
page.drawText(`Nom du fichier: ${certificateData.metadata.fileName}`, {
|
||||||
|
x: leftMargin,
|
||||||
|
y: y,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
y -= lineSpacing;
|
||||||
|
page.drawText(`Créé le: ${certificateData.metadata.createdAt.toLocaleDateString('fr-FR')}`, {
|
||||||
|
x: leftMargin,
|
||||||
|
y: y,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
y -= lineSpacing;
|
||||||
|
page.drawText(`Dernière modification: ${certificateData.metadata.updatedAt.toLocaleDateString('fr-FR')}`, {
|
||||||
|
x: leftMargin,
|
||||||
|
y: y,
|
||||||
|
size: 12,
|
||||||
|
font: helveticaFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
y -= lineSpacing;
|
||||||
|
|
||||||
|
const keywords = [
|
||||||
|
certificateData.documentHash,
|
||||||
|
certificateData.metadata.commitmentId,
|
||||||
|
JSON.stringify(certificateData.metadata.merkleProof)
|
||||||
|
];
|
||||||
|
pdfDoc.setKeywords([keywords.join(separator)]);
|
||||||
|
|
||||||
|
// Add explanatory text about certificate usage
|
||||||
|
const explanatoryText = "Ce certificat doit être utilisé avec le document qu'il certifie pour être vérifié sur la page dédiée. Le document correspondant à ce certificat doit être téléchargé depuis LeCoffre et peut être conservé avec le certificat tant qu'il n'est pas modifié.";
|
||||||
|
const explanatoryLines = this.splitTextToFit(helveticaObliqueFont, explanatoryText, width - (2 * leftMargin), 11);
|
||||||
|
y -= (explanatoryLines.length * 12) + 40; // Space after verification data
|
||||||
|
explanatoryLines.forEach((line, index) => {
|
||||||
|
page.drawText(line, {
|
||||||
|
x: leftMargin,
|
||||||
|
y: y - (index * 14), // Slightly more spacing for readability
|
||||||
|
size: 11,
|
||||||
|
font: helveticaObliqueFont,
|
||||||
|
color: rgb(0.3, 0.3, 0.3) // Slightly grayed out to distinguish from main content
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
const footerText1 = `Page 1 sur 1`;
|
||||||
|
const footerText2 = `Généré le ${new Date().toLocaleString('fr-FR')}`;
|
||||||
|
const footerWidth1 = helveticaObliqueFont.widthOfTextAtSize(footerText1, 10);
|
||||||
|
const footerWidth2 = helveticaObliqueFont.widthOfTextAtSize(footerText2, 10);
|
||||||
|
page.drawText(footerText1, {
|
||||||
|
x: (width - footerWidth1) / 2,
|
||||||
|
y: 35,
|
||||||
|
size: 10,
|
||||||
|
font: helveticaObliqueFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
page.drawText(footerText2, {
|
||||||
|
x: (width - footerWidth2) / 2,
|
||||||
|
y: 25,
|
||||||
|
size: 10,
|
||||||
|
font: helveticaObliqueFont,
|
||||||
|
color: rgb(0, 0, 0)
|
||||||
|
});
|
||||||
|
|
||||||
|
const pdfBytes = await pdfDoc.save();
|
||||||
|
return new Blob([pdfBytes], { type: 'application/pdf' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating certificate:', error);
|
||||||
|
throw new Error('Failed to generate certificate');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split text to fit within a specified width
|
||||||
|
* @param font - PDF font instance
|
||||||
|
* @param text - Text to split
|
||||||
|
* @param maxWidth - Maximum width in points
|
||||||
|
* @param fontSize - Font size
|
||||||
|
* @returns Array of text lines
|
||||||
|
*/
|
||||||
|
private splitTextToFit(font: any, text: string, maxWidth: number, fontSize: number): string[] {
|
||||||
|
const lines: string[] = [];
|
||||||
|
let currentLine = '';
|
||||||
|
|
||||||
|
// Split by characters (pdf-lib doesn't have word-level text measurement)
|
||||||
|
const chars = text.split('');
|
||||||
|
|
||||||
|
for (const char of chars) {
|
||||||
|
const testLine = currentLine + char;
|
||||||
|
const testWidth = font.widthOfTextAtSize(testLine, fontSize);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a PDF certificate and extract document information
|
||||||
|
* @param pdfBlob - The PDF file as a blob
|
||||||
|
* @returns Promise<{documentHash: string, documentUid: string, commitmentId: string, merkleProof?: string}> - Extracted information
|
||||||
|
*/
|
||||||
|
public async parseCertificate(certificateFile: File): Promise<{documentHash: string, commitmentId: string, merkleProof: any}> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const fileReader = new FileReader();
|
||||||
|
|
||||||
|
console.log("certificateFile", certificateFile);
|
||||||
|
|
||||||
|
fileReader.onload = async () => {
|
||||||
|
try {
|
||||||
|
// Read the metadata and get the validation data from the keywords
|
||||||
|
const pdfDoc = await PDFDocument.load(await certificateFile.arrayBuffer());
|
||||||
|
const keywords = pdfDoc.getKeywords()?.split(separator);
|
||||||
|
|
||||||
|
if (!keywords) {
|
||||||
|
throw new Error("No keywords found in certificate");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(keywords);
|
||||||
|
|
||||||
|
if (keywords.length !== 3) {
|
||||||
|
throw new Error("Invalid keywords found in certificate");
|
||||||
|
}
|
||||||
|
|
||||||
|
const documentHash = keywords[0];
|
||||||
|
const commitmentId = keywords[1];
|
||||||
|
const merkleProof = keywords[2];
|
||||||
|
|
||||||
|
if (!documentHash || !commitmentId || !merkleProof) {
|
||||||
|
throw new Error("Invalid keywords found in certificate");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("documentHash", documentHash);
|
||||||
|
console.log("commitmentId", commitmentId);
|
||||||
|
console.log("merkleProof", merkleProof);
|
||||||
|
|
||||||
|
|
||||||
|
resolve({ documentHash, commitmentId, merkleProof });
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fileReader.onerror = () => {
|
||||||
|
reject(new Error('Failed to read PDF file'));
|
||||||
|
};
|
||||||
|
|
||||||
|
fileReader.readAsArrayBuffer(certificateFile);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
211
src/front/Services/WatermarkService/index.ts
Normal file
211
src/front/Services/WatermarkService/index.ts
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
export default class WatermarkService {
|
||||||
|
private static instance: WatermarkService;
|
||||||
|
|
||||||
|
public static getInstance(): WatermarkService {
|
||||||
|
if (!WatermarkService.instance) {
|
||||||
|
WatermarkService.instance = new WatermarkService();
|
||||||
|
}
|
||||||
|
return WatermarkService.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a watermark to a file based on its type
|
||||||
|
* @param file - The original file
|
||||||
|
* @returns Promise<File> - The file with watermark added
|
||||||
|
*/
|
||||||
|
public async addWatermark(file: File): Promise<File> {
|
||||||
|
const fileType = file.type;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (fileType.startsWith('image/')) {
|
||||||
|
return await this.addWatermarkToImage(file);
|
||||||
|
} else if (fileType === 'application/pdf') {
|
||||||
|
return await this.addWatermarkToPdf(file);
|
||||||
|
} else {
|
||||||
|
// For other file types, return the original file
|
||||||
|
console.log(`Watermark not supported for file type: ${fileType}`);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding watermark:', error);
|
||||||
|
// Return original file if watermarking fails
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add watermark to image files
|
||||||
|
* @param file - Image file
|
||||||
|
* @returns Promise<File> - Image with watermark
|
||||||
|
*/
|
||||||
|
private async addWatermarkToImage(file: File): Promise<File> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (event) => {
|
||||||
|
try {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
try {
|
||||||
|
// Create canvas
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
if (!ctx) {
|
||||||
|
reject(new Error('Could not get canvas context'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set canvas size to image size
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
|
||||||
|
// Draw original image
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
|
||||||
|
// Add watermark
|
||||||
|
this.addImageWatermark(ctx, img.width, img.height);
|
||||||
|
|
||||||
|
// Convert to blob
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
if (blob) {
|
||||||
|
const watermarkedFile = new File([blob], file.name, { type: file.type });
|
||||||
|
resolve(watermarkedFile);
|
||||||
|
} else {
|
||||||
|
reject(new Error('Could not create blob from canvas'));
|
||||||
|
}
|
||||||
|
}, file.type);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
img.onerror = reject;
|
||||||
|
img.src = event.target?.result as string;
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.onerror = reject;
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add watermark to PDF files
|
||||||
|
* @param file - PDF file
|
||||||
|
* @returns Promise<File> - PDF with watermark
|
||||||
|
*/
|
||||||
|
private async addWatermarkToPdf(file: File): Promise<File> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = async (event) => {
|
||||||
|
try {
|
||||||
|
const arrayBuffer = event.target?.result as ArrayBuffer;
|
||||||
|
|
||||||
|
// Import pdf-lib dynamically to avoid SSR issues
|
||||||
|
const { PDFDocument, rgb } = await import('pdf-lib');
|
||||||
|
|
||||||
|
// Load the existing PDF
|
||||||
|
const pdfDoc = await PDFDocument.load(arrayBuffer);
|
||||||
|
|
||||||
|
// Get all pages
|
||||||
|
const pages = pdfDoc.getPages();
|
||||||
|
|
||||||
|
// Add watermark to each page
|
||||||
|
for (const page of pages) {
|
||||||
|
this.addPdfWatermark(page, rgb(0.8, 0.2, 0.2)); // Pale red color
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the modified PDF
|
||||||
|
const pdfBytes = await pdfDoc.save();
|
||||||
|
const watermarkedFile = new File([pdfBytes], file.name, { type: file.type });
|
||||||
|
resolve(watermarkedFile);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding watermark to PDF:', error);
|
||||||
|
// If PDF watermarking fails, return original file
|
||||||
|
resolve(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.onerror = reject;
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add watermark to image using Canvas
|
||||||
|
* @param ctx - Canvas 2D context
|
||||||
|
* @param width - Image width
|
||||||
|
* @param height - Image height
|
||||||
|
*/
|
||||||
|
private addImageWatermark(ctx: CanvasRenderingContext2D, width: number, height: number): void {
|
||||||
|
// Save current state
|
||||||
|
ctx.save();
|
||||||
|
|
||||||
|
// Set watermark properties
|
||||||
|
ctx.fillStyle = 'rgba(128, 128, 128, 0.7)'; // Semi-transparent gray
|
||||||
|
ctx.font = '12px Arial';
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.textBaseline = 'bottom';
|
||||||
|
|
||||||
|
// Position watermark in bottom-right corner
|
||||||
|
const text = 'Processed by LeCoffre';
|
||||||
|
const x = width - 10; // 10 pixels from right edge
|
||||||
|
const y = height - 10; // 10 pixels from bottom
|
||||||
|
|
||||||
|
// Add watermark text
|
||||||
|
ctx.fillText(text, x, y);
|
||||||
|
|
||||||
|
// Restore state
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add watermark to PDF using pdf-lib
|
||||||
|
* @param page - PDF page from pdf-lib
|
||||||
|
* @param color - Color for the watermark
|
||||||
|
*/
|
||||||
|
private addPdfWatermark(page: any, color: any): void {
|
||||||
|
const { width, height } = page.getSize();
|
||||||
|
|
||||||
|
// Calculate watermark position (bottom-right corner)
|
||||||
|
const text = 'Processed by LeCoffre';
|
||||||
|
const dateTime = new Date().toLocaleString('fr-FR', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
});
|
||||||
|
|
||||||
|
const fontSize = 10;
|
||||||
|
const lineHeight = 12; // Space between lines
|
||||||
|
|
||||||
|
// Calculate text widths (approximate - pdf-lib doesn't have a direct method for this)
|
||||||
|
// Using a conservative estimate: ~6 points per character for 10pt font
|
||||||
|
const estimatedTextWidth1 = text.length * 6;
|
||||||
|
const estimatedTextWidth2 = dateTime.length * 6;
|
||||||
|
const maxTextWidth = Math.max(estimatedTextWidth1, estimatedTextWidth2);
|
||||||
|
|
||||||
|
// Position watermark with proper margins (20 points from edges)
|
||||||
|
const x = width - maxTextWidth - 20; // 20 points from right edge
|
||||||
|
const y = 20; // 20 points from bottom
|
||||||
|
|
||||||
|
// Add watermark text with transparency (first line)
|
||||||
|
page.drawText(text, {
|
||||||
|
x: x,
|
||||||
|
y: y + lineHeight, // Second line (top)
|
||||||
|
size: fontSize,
|
||||||
|
color: color,
|
||||||
|
opacity: 0.7
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add date/time with transparency (second line)
|
||||||
|
page.drawText(dateTime, {
|
||||||
|
x: x,
|
||||||
|
y: y, // First line (bottom)
|
||||||
|
size: fontSize,
|
||||||
|
color: color,
|
||||||
|
opacity: 0.7
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -4,13 +4,15 @@ import CookieService from "@Front/Services/CookieService/CookieService";
|
|||||||
import EventEmitter from "@Front/Services/EventEmitter";
|
import EventEmitter from "@Front/Services/EventEmitter";
|
||||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||||
|
|
||||||
|
import User from "src/sdk/User";
|
||||||
|
|
||||||
export default class UserStore {
|
export default class UserStore {
|
||||||
public static readonly instance = new this();
|
public static readonly instance = new this();
|
||||||
protected readonly event = new EventEmitter();
|
protected readonly event = new EventEmitter();
|
||||||
public accessToken: string | null = null;
|
public accessToken: string | null = null;
|
||||||
public refreshToken: string | null = null;
|
public refreshToken: string | null = null;
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() { }
|
||||||
|
|
||||||
public isConnected(): boolean {
|
public isConnected(): boolean {
|
||||||
return !!this.accessToken;
|
return !!this.accessToken;
|
||||||
@ -41,6 +43,8 @@ export default class UserStore {
|
|||||||
CookieService.getInstance().deleteCookie("leCoffreAccessToken");
|
CookieService.getInstance().deleteCookie("leCoffreAccessToken");
|
||||||
CookieService.getInstance().deleteCookie("leCoffreRefreshToken");
|
CookieService.getInstance().deleteCookie("leCoffreRefreshToken");
|
||||||
|
|
||||||
|
User.getInstance().clear();
|
||||||
|
|
||||||
this.event.emit("disconnection", this.accessToken);
|
this.event.emit("disconnection", this.accessToken);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -4,16 +4,16 @@ import CookieService from "@Front/Services/CookieService/CookieService";
|
|||||||
import EventEmitter from "@Front/Services/EventEmitter";
|
import EventEmitter from "@Front/Services/EventEmitter";
|
||||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||||
|
|
||||||
|
import User from "src/sdk/User";
|
||||||
|
|
||||||
export default class UserStore {
|
export default class UserStore {
|
||||||
public static readonly instance = new this();
|
public static readonly instance = new this();
|
||||||
protected readonly event = new EventEmitter();
|
protected readonly event = new EventEmitter();
|
||||||
public accessToken: string | null = null;
|
|
||||||
public refreshToken: string | null = null;
|
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
|
|
||||||
public isConnected(): boolean {
|
public isConnected(): boolean {
|
||||||
return !!this.accessToken;
|
return !!CookieService.getInstance().getCookie("leCoffreAccessToken");
|
||||||
}
|
}
|
||||||
|
|
||||||
public getRole(): string | undefined {
|
public getRole(): string | undefined {
|
||||||
@ -27,7 +27,7 @@ export default class UserStore {
|
|||||||
CookieService.getInstance().setCookie("leCoffreAccessToken", accessToken);
|
CookieService.getInstance().setCookie("leCoffreAccessToken", accessToken);
|
||||||
CookieService.getInstance().setCookie("leCoffreRefreshToken", refreshToken);
|
CookieService.getInstance().setCookie("leCoffreRefreshToken", refreshToken);
|
||||||
|
|
||||||
this.event.emit("connection", this.accessToken);
|
this.event.emit("connection", CookieService.getInstance().getCookie("leCoffreAccessToken"));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return false;
|
return false;
|
||||||
@ -41,7 +41,9 @@ export default class UserStore {
|
|||||||
CookieService.getInstance().deleteCookie("leCoffreAccessToken");
|
CookieService.getInstance().deleteCookie("leCoffreAccessToken");
|
||||||
CookieService.getInstance().deleteCookie("leCoffreRefreshToken");
|
CookieService.getInstance().deleteCookie("leCoffreRefreshToken");
|
||||||
|
|
||||||
this.event.emit("disconnection", this.accessToken);
|
User.getInstance().clear();
|
||||||
|
|
||||||
|
this.event.emit("disconnection", CookieService.getInstance().getCookie("leCoffreAccessToken"));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
@ -56,4 +58,12 @@ export default class UserStore {
|
|||||||
this.event.on("connection", callback);
|
this.event.on("connection", callback);
|
||||||
return () => this.event.off("connection", callback);
|
return () => this.event.off("connection", callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getAccessToken(): string {
|
||||||
|
return CookieService.getInstance().getCookie("leCoffreAccessToken") || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRefreshToken(): string {
|
||||||
|
return CookieService.getInstance().getCookie("leCoffreRefreshToken") || "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,18 @@ import { DefaultLayout } from "@Front/Components/LayoutTemplates/DefaultLayout";
|
|||||||
import { FrontendVariables } from "@Front/Config/VariablesFront";
|
import { FrontendVariables } from "@Front/Config/VariablesFront";
|
||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
import type { AppType, AppProps } from "next/app";
|
import type { AppType, AppProps } from "next/app";
|
||||||
import { useEffect, type ReactElement, type ReactNode } from "react";
|
import { useEffect, useState, type ReactElement, type ReactNode } from "react";
|
||||||
import getConfig from "next/config";
|
import getConfig from "next/config";
|
||||||
import { GoogleTagManager } from "@next/third-parties/google";
|
import { GoogleTagManager } from "@next/third-parties/google";
|
||||||
import { hotjar } from "react-hotjar";
|
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";
|
||||||
|
import User from "src/sdk/User";
|
||||||
|
|
||||||
export type NextPageWithLayout<TProps = Record<string, unknown>, TInitialProps = TProps> = NextPage<TProps, TInitialProps> & {
|
export type NextPageWithLayout<TProps = Record<string, unknown>, TInitialProps = TProps> = NextPage<TProps, TInitialProps> & {
|
||||||
getLayout?: (page: ReactElement) => ReactNode;
|
getLayout?: (page: ReactElement) => ReactNode;
|
||||||
};
|
};
|
||||||
@ -28,6 +35,7 @@ type AppPropsWithLayout = AppProps & {
|
|||||||
docaposteApiUrl: string;
|
docaposteApiUrl: string;
|
||||||
hotjarSiteId: number;
|
hotjarSiteId: number;
|
||||||
hotjarVersion: number;
|
hotjarVersion: number;
|
||||||
|
_4nkUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
const { publicRuntimeConfig } = getConfig();
|
||||||
@ -48,6 +56,7 @@ const MyApp = (({
|
|||||||
docaposteApiUrl,
|
docaposteApiUrl,
|
||||||
hotjarSiteId,
|
hotjarSiteId,
|
||||||
hotjarVersion,
|
hotjarVersion,
|
||||||
|
_4nkUrl,
|
||||||
}: AppPropsWithLayout) => {
|
}: AppPropsWithLayout) => {
|
||||||
const getLayout = Component.getLayout ?? ((page) => <DefaultLayout children={page}></DefaultLayout>);
|
const getLayout = Component.getLayout ?? ((page) => <DefaultLayout children={page}></DefaultLayout>);
|
||||||
|
|
||||||
@ -64,7 +73,30 @@ const MyApp = (({
|
|||||||
instance.FC_CLIENT_ID = fcClientId;
|
instance.FC_CLIENT_ID = fcClientId;
|
||||||
instance.DOCAPOST_API_URL = docaposteApiUrl;
|
instance.DOCAPOST_API_URL = docaposteApiUrl;
|
||||||
instance.HOTJAR_SITE_ID = hotjarSiteId;
|
instance.HOTJAR_SITE_ID = hotjarSiteId;
|
||||||
instance.HOJAR_VERSION = hotjarVersion;
|
instance.HOTJAR_VERSION = hotjarVersion;
|
||||||
|
instance._4NK_URL = _4nkUrl;
|
||||||
|
|
||||||
|
const [isConnected, setIsConnected] = useState(false);
|
||||||
|
const [isReady, setIsReady] = useState(false);
|
||||||
|
|
||||||
|
IframeReference.setTargetOrigin(_4nkUrl);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const isAuthenticated = User.getInstance().isAuthenticated();
|
||||||
|
setIsConnected(isAuthenticated);
|
||||||
|
|
||||||
|
if (isAuthenticated) {
|
||||||
|
MessageBus.getInstance().initMessageListener();
|
||||||
|
MessageBus.getInstance().isReady().then(() => setIsReady(true));
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
MessageBus.getInstance().destroyMessageListener();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => { };
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!hotjarSiteId || !hotjarVersion) {
|
if (!hotjarSiteId || !hotjarVersion) {
|
||||||
console.warn("No hotjar site id or version provided");
|
console.warn("No hotjar site id or version provided");
|
||||||
@ -78,9 +110,15 @@ const MyApp = (({
|
|||||||
}, [hotjarSiteId, hotjarVersion]);
|
}, [hotjarSiteId, hotjarVersion]);
|
||||||
|
|
||||||
return getLayout(
|
return getLayout(
|
||||||
|
<>
|
||||||
|
{((isConnected && isReady) || !isConnected) &&
|
||||||
<Component {...pageProps}>
|
<Component {...pageProps}>
|
||||||
<GoogleTagManager gtmId="GTM-5GLJN86P" />
|
<GoogleTagManager gtmId="GTM-5GLJN86P" />
|
||||||
</Component>,
|
</Component>
|
||||||
|
}
|
||||||
|
{isConnected && <Iframe />}
|
||||||
|
<Loader />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}) as AppType;
|
}) as AppType;
|
||||||
|
|
||||||
@ -100,6 +138,7 @@ MyApp.getInitialProps = async () => {
|
|||||||
docaposteApiUrl: publicRuntimeConfig.NEXT_PUBLIC_DOCAPOST_API_URL,
|
docaposteApiUrl: publicRuntimeConfig.NEXT_PUBLIC_DOCAPOST_API_URL,
|
||||||
hotjarSiteId: publicRuntimeConfig.NEXT_PUBLIC_HOTJAR_SITE_ID,
|
hotjarSiteId: publicRuntimeConfig.NEXT_PUBLIC_HOTJAR_SITE_ID,
|
||||||
hotjarVersion: publicRuntimeConfig.NEXT_PUBLIC_HOTJAR_VERSION,
|
hotjarVersion: publicRuntimeConfig.NEXT_PUBLIC_HOTJAR_VERSION,
|
||||||
|
_4nkUrl: publicRuntimeConfig.NEXT_PUBLIC_4NK_URL,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
import ClientDashboard from "@Front/Components/Layouts/ClientDashboard/index";
|
||||||
|
|
||||||
|
export default function Route() {
|
||||||
|
return <ClientDashboard />;
|
||||||
|
}
|
20
src/pages/folders/[folderUid]/verify-documents.tsx
Normal file
20
src/pages/folders/[folderUid]/verify-documents.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
|
||||||
|
import DocumentVerification from "@Front/Components/Layouts/Folder/DocumentVerification";
|
||||||
|
|
||||||
|
export default function VerifyDocuments() {
|
||||||
|
const router = useRouter();
|
||||||
|
const { folderUid } = router.query;
|
||||||
|
|
||||||
|
if (!folderUid || Array.isArray(folderUid)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DefaultNotaryDashboard>
|
||||||
|
<DocumentVerification folderUid={folderUid} />
|
||||||
|
</DefaultNotaryDashboard>
|
||||||
|
);
|
||||||
|
}
|
150
src/sdk/AuthModal.tsx
Normal file
150
src/sdk/AuthModal.tsx
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import Modal from '@Front/Components/DesignSystem/Modal';
|
||||||
|
import Loader from '@Front/Components/DesignSystem/Loader';
|
||||||
|
import Typography, { ETypo, ETypoColor } from '@Front/Components/DesignSystem/Typography';
|
||||||
|
|
||||||
|
import IframeReference from './IframeReference';
|
||||||
|
import User from './User';
|
||||||
|
|
||||||
|
interface AuthModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
|
||||||
|
const [isIframeReady, setIsIframeReady] = useState(false);
|
||||||
|
const [showIframe, setShowIframe] = useState(false);
|
||||||
|
const [authSuccess, setAuthSuccess] = useState(false);
|
||||||
|
|
||||||
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMessage = (event: MessageEvent) => {
|
||||||
|
if (!event.data || event.data.type === 'PassClientScriptReady') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iframeRef.current) {
|
||||||
|
console.error('[AuthModal] handleMessage: iframeRef.current is null');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.source !== iframeRef.current.contentWindow) {
|
||||||
|
console.error('[AuthModal] handleMessage: source not match');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetOrigin = IframeReference.getTargetOrigin();
|
||||||
|
if (!targetOrigin) {
|
||||||
|
console.error('[AuthModal] handleMessage: targetOrigin not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.origin !== targetOrigin) {
|
||||||
|
console.error('[AuthModal] handleMessage: origin not match');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event.data || typeof event.data !== 'object') {
|
||||||
|
console.error('[AuthModal] handleMessage: data not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = event.data;
|
||||||
|
console.log('[AuthModal] handleMessage:', message);
|
||||||
|
|
||||||
|
switch (message.type) {
|
||||||
|
case 'LISTENING':
|
||||||
|
iframeRef.current.contentWindow!.postMessage({ type: 'REQUEST_LINK' }, targetOrigin);
|
||||||
|
setIsIframeReady(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'LINK_ACCEPTED':
|
||||||
|
setShowIframe(false);
|
||||||
|
|
||||||
|
User.getInstance().setTokens(message.accessToken, message.refreshToken);
|
||||||
|
iframeRef.current.contentWindow!.postMessage({ type: 'GET_PAIRING_ID', accessToken: message.accessToken }, targetOrigin);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'GET_PAIRING_ID':
|
||||||
|
User.getInstance().setPairingId(message.userPairingId);
|
||||||
|
setAuthSuccess(true);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowIframe(false);
|
||||||
|
setIsIframeReady(false);
|
||||||
|
setAuthSuccess(false);
|
||||||
|
onClose();
|
||||||
|
}, 500);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('message', handleMessage);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('message', handleMessage);
|
||||||
|
};
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isIframeReady && !showIframe) {
|
||||||
|
setShowIframe(true);
|
||||||
|
}
|
||||||
|
}, [isIframeReady, showIframe]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
title='Authentification 4nk'
|
||||||
|
>
|
||||||
|
{!isIframeReady && (
|
||||||
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
height: '400px',
|
||||||
|
gap: 'var(--spacing-md, 16px)'
|
||||||
|
}}>
|
||||||
|
<Loader width={40} />
|
||||||
|
<Typography typo={ETypo.TEXT_MD_SEMIBOLD}>Chargement de l'authentification...</Typography>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{authSuccess ? (
|
||||||
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
height: '400px',
|
||||||
|
gap: '20px'
|
||||||
|
}}>
|
||||||
|
<Typography typo={ETypo.TEXT_MD_SEMIBOLD} color={ETypoColor.COLOR_SUCCESS_500}>
|
||||||
|
Authentification réussie ! Redirection en cours...
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div style={{
|
||||||
|
display: showIframe ? 'flex' : 'none',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%'
|
||||||
|
}}>
|
||||||
|
<iframe
|
||||||
|
ref={iframeRef}
|
||||||
|
src={IframeReference.getTargetOrigin()}
|
||||||
|
style={{
|
||||||
|
display: showIframe ? 'block' : 'none',
|
||||||
|
width: '400px',
|
||||||
|
height: '400px',
|
||||||
|
border: 'none',
|
||||||
|
overflow: 'hidden'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
33
src/sdk/EventBus.ts
Normal file
33
src/sdk/EventBus.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
export default class EventBus {
|
||||||
|
private static instance: EventBus;
|
||||||
|
private listeners: Record<string, Array<(...args: any[]) => void>> = {};
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static getInstance(): EventBus {
|
||||||
|
if (!EventBus.instance) {
|
||||||
|
EventBus.instance = new EventBus();
|
||||||
|
}
|
||||||
|
return EventBus.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public on(event: string, callback: (...args: any[]) => void): () => void {
|
||||||
|
if (!this.listeners[event]) {
|
||||||
|
this.listeners[event] = [];
|
||||||
|
}
|
||||||
|
this.listeners[event].push(callback);
|
||||||
|
return () => {
|
||||||
|
if (this.listeners[event]) {
|
||||||
|
this.listeners[event] = this.listeners[event].filter(cb => cb !== callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public emit(event: string, ...args: any[]): void {
|
||||||
|
if (this.listeners[event]) {
|
||||||
|
this.listeners[event].forEach(callback => {
|
||||||
|
callback(...args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
src/sdk/Iframe.tsx
Normal file
36
src/sdk/Iframe.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { useRef, useEffect, memo } from 'react';
|
||||||
|
import IframeReference from './IframeReference';
|
||||||
|
|
||||||
|
interface IframeProps {
|
||||||
|
showIframe?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Iframe({ showIframe = false }: IframeProps) {
|
||||||
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (iframeRef.current) {
|
||||||
|
IframeReference.setIframe(iframeRef.current);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
IframeReference.setIframe(null);
|
||||||
|
};
|
||||||
|
}, [iframeRef.current]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<iframe
|
||||||
|
ref={iframeRef}
|
||||||
|
src={IframeReference.getTargetOrigin()}
|
||||||
|
style={{
|
||||||
|
display: showIframe ? 'block' : 'none',
|
||||||
|
width: '400px',
|
||||||
|
height: '400px',
|
||||||
|
border: 'none',
|
||||||
|
overflow: 'hidden'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iframe.displayName = 'Iframe';
|
||||||
|
export default memo(Iframe);
|
36
src/sdk/IframeReference.ts
Normal file
36
src/sdk/IframeReference.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export default class IframeReference {
|
||||||
|
private static targetOrigin: string | null = null;
|
||||||
|
private static iframe: HTMLIFrameElement | null = null;
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static setTargetOrigin(targetOrigin: string): void {
|
||||||
|
try {
|
||||||
|
new URL(targetOrigin);
|
||||||
|
this.targetOrigin = targetOrigin;
|
||||||
|
} catch {
|
||||||
|
throw new Error(`Invalid targetOrigin: ${targetOrigin}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getTargetOrigin(): string {
|
||||||
|
if (!this.targetOrigin) {
|
||||||
|
throw new Error("targetOrigin is not set");
|
||||||
|
}
|
||||||
|
return this.targetOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static setIframe(iframe: HTMLIFrameElement | null): void {
|
||||||
|
if (iframe !== null && !(iframe instanceof HTMLIFrameElement)) {
|
||||||
|
throw new Error("setIframe expects an HTMLIFrameElement or null");
|
||||||
|
}
|
||||||
|
this.iframe = iframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getIframe(): HTMLIFrameElement {
|
||||||
|
if (!this.iframe) {
|
||||||
|
throw new Error("iframe is not set");
|
||||||
|
}
|
||||||
|
return this.iframe;
|
||||||
|
}
|
||||||
|
}
|
25
src/sdk/MapUtils.ts
Normal file
25
src/sdk/MapUtils.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export default class MapUtils {
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static toJson(input: any): any {
|
||||||
|
if (input instanceof Map) {
|
||||||
|
const obj: Record<string, any> = {};
|
||||||
|
for (const [key, value] of input.entries()) {
|
||||||
|
obj[key] = MapUtils.toJson(value);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
} else if (Array.isArray(input)) {
|
||||||
|
return input.map((item) => MapUtils.toJson(item));
|
||||||
|
} else if (input !== null && typeof input === 'object') {
|
||||||
|
const obj: Record<string, any> = {};
|
||||||
|
for (const key in input) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(input, key)) {
|
||||||
|
obj[key] = MapUtils.toJson(input[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
901
src/sdk/MessageBus.ts
Normal file
901
src/sdk/MessageBus.ts
Normal file
@ -0,0 +1,901 @@
|
|||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import IframeReference from './IframeReference';
|
||||||
|
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;
|
||||||
|
private errors: { [key: string]: string } = {};
|
||||||
|
private isListening: boolean = false;
|
||||||
|
|
||||||
|
public static getInstance(): MessageBus {
|
||||||
|
if (!MessageBus.instance) {
|
||||||
|
MessageBus.instance = new MessageBus();
|
||||||
|
}
|
||||||
|
return MessageBus.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public initMessageListener(): void {
|
||||||
|
window.addEventListener('message', this.handleMessage.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroyMessageListener(): void {
|
||||||
|
window.removeEventListener('message', this.handleMessage.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public isReady(): Promise<void> {
|
||||||
|
if (this.isListening) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return new Promise<void>((resolve: () => void) => {
|
||||||
|
const unsubscribe = EventBus.getInstance().on('IS_READY', () => {
|
||||||
|
unsubscribe();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public requestLink(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
|
||||||
|
const messageId = `REQUEST_LINK_${uuidv4()}`;
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('LINK_ACCEPTED', (responseId: string, message: { accessToken: string, refreshToken: string }) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
User.getInstance().setTokens(message.accessToken, message.refreshToken);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_LINK_ACCEPTED', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'REQUEST_LINK',
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPairingId(): Promise<string> {
|
||||||
|
return new Promise<string>((resolve: (pairingId: string) => void, reject: (error: string) => void) => {
|
||||||
|
this.checkToken().then(() => {
|
||||||
|
const messageId = `GET_PAIRING_ID_${uuidv4()}`;
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('GET_PAIRING_ID', (responseId: string, pairingId: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
resolve(pairingId);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_GET_PAIRING_ID', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const accessToken = user.getAccessToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'GET_PAIRING_ID',
|
||||||
|
accessToken,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
}).catch(console.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFileByUid(uid: string): Promise<any> {
|
||||||
|
return new Promise<any>((resolve: (files: any[]) => void, reject: (error: string) => void) => {
|
||||||
|
this.getProcesses().then(async (processes: any) => {
|
||||||
|
const files: any[] = [];
|
||||||
|
|
||||||
|
for (const processId of Object.keys(processes)) {
|
||||||
|
const process = processes[processId];
|
||||||
|
if (!process.states) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const publicDataDecoded: { [key: string]: any } = {};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
if (!lastCommitedState) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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 publicDataEncoded = lastUpdatedFileState.public_data;
|
||||||
|
if (!publicDataEncoded) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(files[0]);
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getProcessesDecoded(filterPublicValues: (publicValues: { [key: string]: any }) => boolean): Promise<any[]> {
|
||||||
|
return new Promise<any[]>((resolve: (processesDecoded: any[]) => void, reject: (error: string) => void) => {
|
||||||
|
this.getProcesses().then(async (processes: any) => {
|
||||||
|
const processesDecoded: any[] = [];
|
||||||
|
|
||||||
|
for (const processId of Object.keys(processes)) {
|
||||||
|
const process = processes[processId];
|
||||||
|
if (!process.states) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const publicDataDecoded: { [key: string]: any } = {};
|
||||||
|
|
||||||
|
for (let stateId = 0; stateId < process.states.length - 1; stateId++) {
|
||||||
|
const state = process.states[stateId];
|
||||||
|
if (!state) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const publicDataEncoded = state.public_data;
|
||||||
|
if (!publicDataEncoded) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of Object.keys(publicDataEncoded)) {
|
||||||
|
publicDataDecoded[key] = await this.getPublicData(publicDataEncoded[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filterPublicValues(publicDataDecoded)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let processDecoded: any;
|
||||||
|
|
||||||
|
for (let stateId = 0; stateId < process.states.length - 1; stateId++) {
|
||||||
|
const lastState = process.states[stateId];
|
||||||
|
if (!lastState) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastStateId = lastState.state_id;
|
||||||
|
if (!lastStateId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let processData = await this.getData(processId, lastStateId);
|
||||||
|
if (!processData) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isEmpty = Object.keys(processData).length === 0;
|
||||||
|
if (isEmpty) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of Object.keys(publicDataDecoded)) {
|
||||||
|
processData[key] = publicDataDecoded[key];
|
||||||
|
}
|
||||||
|
processData = MapUtils.toJson(processData);
|
||||||
|
|
||||||
|
if (!processDecoded) {
|
||||||
|
processDecoded = {
|
||||||
|
processId,
|
||||||
|
lastStateId,
|
||||||
|
processData,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
for (const key of Object.keys(processData)) {
|
||||||
|
processDecoded.processData[key] = processData[key];
|
||||||
|
}
|
||||||
|
processDecoded.lastStateId = lastStateId;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processesDecoded.push(processDecoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(processesDecoded);
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public createProcess(processData: any, privateFields: string[], roles: {}): Promise<any> {
|
||||||
|
return new Promise<any>((resolve: (processCreated: any) => void, reject: (error: string) => void) => {
|
||||||
|
this.checkToken().then(() => {
|
||||||
|
const messageId = `CREATE_PROCESS_${uuidv4()}`;
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('PROCESS_CREATED', (responseId: string, processCreated: any) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
resolve(processCreated);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_PROCESS_CREATED', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const accessToken = user.getAccessToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'CREATE_PROCESS',
|
||||||
|
processData: processData,
|
||||||
|
privateFields: privateFields,
|
||||||
|
roles: roles,
|
||||||
|
accessToken,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
}).catch(console.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateProcess(processId: string, newData: any, privateFields: string[], roles: {} | null): Promise<any> {
|
||||||
|
return new Promise<any>((resolve: (processUpdated: any) => void, reject: (error: string) => void) => {
|
||||||
|
this.checkToken().then(() => {
|
||||||
|
const messageId = uuidv4();
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('PROCESS_UPDATED', (responseId: string, processUpdated: any) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
resolve(processUpdated);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_PROCESS_UPDATED', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const accessToken = user.getAccessToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'UPDATE_PROCESS',
|
||||||
|
processId,
|
||||||
|
newData,
|
||||||
|
privateFields,
|
||||||
|
roles,
|
||||||
|
accessToken,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
}).catch(console.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getProcesses(): Promise<any> {
|
||||||
|
return new Promise<any>((resolve: (processes: any) => void, reject: (error: string) => void) => {
|
||||||
|
this.checkToken().then(() => {
|
||||||
|
const messageId = `GET_PROCESSES_${uuidv4()}`;
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('PROCESSES_RETRIEVED', (responseId: string, processes: any) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
|
||||||
|
// Filter processes by my processes
|
||||||
|
setTimeout(() => {
|
||||||
|
this.getMyProcesses().then((myProcesses: any) => {
|
||||||
|
const processesFiltered: { [processId: string]: any } = {};
|
||||||
|
for (const processId of myProcesses) {
|
||||||
|
const process = processes[processId];
|
||||||
|
if (process) {
|
||||||
|
processesFiltered[processId] = process;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve(processesFiltered);
|
||||||
|
}).catch(reject);
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_PROCESSES_RETRIEVED', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const accessToken = user.getAccessToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'GET_PROCESSES',
|
||||||
|
accessToken,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
}).catch(console.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMyProcesses(): Promise<any> {
|
||||||
|
return new Promise<any>((resolve: (processes: any) => void, reject: (error: string) => void) => {
|
||||||
|
this.checkToken().then(() => {
|
||||||
|
const messageId = `GET_MY_PROCESSES_${uuidv4()}`;
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('GET_MY_PROCESSES', (responseId: string, processes: any) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
resolve(processes);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_GET_MY_PROCESSES', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const accessToken = user.getAccessToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'GET_MY_PROCESSES',
|
||||||
|
accessToken,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
}).catch(console.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public notifyUpdate(processId: string, stateId: string): Promise<void> {
|
||||||
|
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
|
||||||
|
this.checkToken().then(() => {
|
||||||
|
const messageId = uuidv4();
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('UPDATE_NOTIFIED', (responseId: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_UPDATE_NOTIFIED', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const accessToken = user.getAccessToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'NOTIFY_UPDATE',
|
||||||
|
processId,
|
||||||
|
stateId,
|
||||||
|
accessToken,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
}).catch(console.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public validateState(processId: string, stateId: string): Promise<any> {
|
||||||
|
return new Promise<any>((resolve: (stateValidated: any) => void, reject: (error: string) => void) => {
|
||||||
|
this.checkToken().then(() => {
|
||||||
|
const messageId = `VALIDATE_STATE_${uuidv4()}`;
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('STATE_VALIDATED', (responseId: string, stateValidated: any) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
resolve(stateValidated);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_STATE_VALIDATED', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const accessToken = user.getAccessToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'VALIDATE_STATE',
|
||||||
|
processId,
|
||||||
|
stateId,
|
||||||
|
accessToken,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
}).catch(console.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getData(processId: string, stateId: string): Promise<any> {
|
||||||
|
return new Promise<any>((resolve: (data: any) => void, reject: (error: string) => void) => {
|
||||||
|
this.checkToken().then(() => {
|
||||||
|
const messageId = `DATA_RETRIEVED_${uuidv4()}`;
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('DATA_RETRIEVED', (responseId: string, data: any) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
resolve(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_DATA_RETRIEVED', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const accessToken = user.getAccessToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'RETRIEVE_DATA',
|
||||||
|
processId,
|
||||||
|
stateId,
|
||||||
|
accessToken,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
}).catch(console.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPublicData(encodedData: number[]): Promise<any> {
|
||||||
|
return new Promise<any>((resolve: (data: any) => void, reject: (error: string) => void) => {
|
||||||
|
this.checkToken().then(() => {
|
||||||
|
const messageId = `PUBLIC_DATA_DECODED_${uuidv4()}`
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('PUBLIC_DATA_DECODED', (responseId: string, data: any) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
resolve(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_PUBLIC_DATA_DECODED', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const accessToken = user.getAccessToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'DECODE_PUBLIC_DATA',
|
||||||
|
encodedData,
|
||||||
|
accessToken,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
}).catch(console.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public verifyMerkleProof(merkleProof: string, documentHash: string): Promise<boolean> {
|
||||||
|
return new Promise<boolean>((resolve: (isValid: boolean) => void, reject: (error: string) => void) => {
|
||||||
|
this.checkToken().then(() => {
|
||||||
|
const messageId = `VALIDATE_MERKLE_PROOF_${uuidv4()}`;
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('MERKLE_PROOF_VALIDATED', (responseId: string, isValid: boolean) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
resolve(isValid);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_MERKLE_PROOF_VALIDATED', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const accessToken = user.getAccessToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'VALIDATE_MERKLE_PROOF',
|
||||||
|
accessToken,
|
||||||
|
merkleProof,
|
||||||
|
documentHash,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateToken(): Promise<boolean> {
|
||||||
|
return new Promise<boolean>((resolve: (isValid: boolean) => void, reject: (error: string) => void) => {
|
||||||
|
const messageId = `VALIDATE_TOKEN_${uuidv4()}`;
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('TOKEN_VALIDATED', (responseId: string, isValid: boolean) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
resolve(isValid);
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_TOKEN_VALIDATED', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const accessToken = user.getAccessToken()!;
|
||||||
|
const refreshToken = user.getRefreshToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'VALIDATE_TOKEN',
|
||||||
|
accessToken,
|
||||||
|
refreshToken,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private renewToken(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
|
||||||
|
const messageId = `RENEW_TOKEN_${uuidv4()}`;
|
||||||
|
|
||||||
|
const unsubscribe = EventBus.getInstance().on('TOKEN_RENEWED', (responseId: string, message: { accessToken: string, refreshToken: string }) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribe();
|
||||||
|
User.getInstance().setTokens(message.accessToken, message.refreshToken);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
const unsubscribeError = EventBus.getInstance().on('ERROR_TOKEN_RENEWED', (responseId: string, error: string) => {
|
||||||
|
if (responseId !== messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsubscribeError();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = User.getInstance();
|
||||||
|
const refreshToken = user.getRefreshToken()!;
|
||||||
|
|
||||||
|
this.sendMessage({
|
||||||
|
type: 'RENEW_TOKEN',
|
||||||
|
refreshToken,
|
||||||
|
messageId
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkToken(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
|
||||||
|
this.isReady().then(() => {
|
||||||
|
this.validateToken().then((isValid: boolean) => {
|
||||||
|
if (!isValid) {
|
||||||
|
this.renewToken().then(resolve).catch(reject);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendMessage(message: any): void {
|
||||||
|
const targetOrigin = IframeReference.getTargetOrigin();
|
||||||
|
if (!targetOrigin) {
|
||||||
|
console.error('[MessageBus] sendMessage: targetOrigin not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const iframe = IframeReference.getIframe();
|
||||||
|
if (!iframe) {
|
||||||
|
console.error('[MessageBus] sendMessage: iframe not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message.type === 'VALIDATE_MERKLE_PROOF') {
|
||||||
|
console.log('[MessageBus] sendMessage:', message);
|
||||||
|
}
|
||||||
|
iframe.contentWindow?.postMessage(message, targetOrigin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleMessage(event: MessageEvent): void {
|
||||||
|
if (!event.data || event.data.type === 'PassClientScriptReady') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iframe = IframeReference.getIframe();
|
||||||
|
if (!iframe) {
|
||||||
|
console.error('[MessageBus] handleMessage: iframe not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.source !== iframe.contentWindow) {
|
||||||
|
console.error('[MessageBus] handleMessage: source not match');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetOrigin = IframeReference.getTargetOrigin();
|
||||||
|
if (!targetOrigin) {
|
||||||
|
console.error('[MessageBus] handleMessage: targetOrigin not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.origin !== targetOrigin) {
|
||||||
|
console.error('[MessageBus] handleMessage: origin not match');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event.data || typeof event.data !== 'object') {
|
||||||
|
console.error('[MessageBus] handleMessage: data not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = event.data;
|
||||||
|
// console.log('[MessageBus] handleMessage:', message);
|
||||||
|
|
||||||
|
switch (message.type) {
|
||||||
|
case 'LISTENING':
|
||||||
|
this.isListening = true;
|
||||||
|
EventBus.getInstance().emit('IS_READY');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'LINK_ACCEPTED':
|
||||||
|
this.doHandleMessage(message.messageId, 'LINK_ACCEPTED', message, (message: any) => ({
|
||||||
|
accessToken: message.accessToken,
|
||||||
|
refreshToken: message.refreshToken
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'VALIDATE_TOKEN':
|
||||||
|
this.doHandleMessage(message.messageId, 'TOKEN_VALIDATED', message, (message: any) => message.isValid);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'RENEW_TOKEN':
|
||||||
|
this.doHandleMessage(message.messageId, 'TOKEN_RENEWED', message, (message: any) => ({
|
||||||
|
accessToken: message.accessToken,
|
||||||
|
refreshToken: message.refreshToken
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'GET_PAIRING_ID':
|
||||||
|
this.doHandleMessage(message.messageId, 'GET_PAIRING_ID', message, (message: any) => message.userPairingId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'PROCESS_CREATED': // CREATE_PROCESS
|
||||||
|
this.doHandleMessage(message.messageId, 'PROCESS_CREATED', message, (message: any) => message.processCreated);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'PROCESS_UPDATED': // UPDATE_PROCESS
|
||||||
|
this.doHandleMessage(message.messageId, 'PROCESS_UPDATED', message, (message: any) => message.updatedProcess);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'PROCESSES_RETRIEVED': // GET_PROCESSES
|
||||||
|
this.doHandleMessage(message.messageId, 'PROCESSES_RETRIEVED', message, (message: any) => message.processes);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'GET_MY_PROCESSES': // GET_MY_PROCESSES
|
||||||
|
this.doHandleMessage(message.messageId, 'GET_MY_PROCESSES', message, (message: any) => message.myProcesses);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'DATA_RETRIEVED': // RETRIEVE_DATA
|
||||||
|
this.doHandleMessage(message.messageId, 'DATA_RETRIEVED', message, (message: any) => message.data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'PUBLIC_DATA_DECODED': // DECODE_PUBLIC_DATA
|
||||||
|
this.doHandleMessage(message.messageId, 'PUBLIC_DATA_DECODED', message, (message: any) => message.decodedData);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'UPDATE_NOTIFIED': // NOTIFY_UPDATE
|
||||||
|
this.doHandleMessage(message.messageId, 'UPDATE_NOTIFIED', message, () => { });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'STATE_VALIDATED': // VALIDATE_STATE
|
||||||
|
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': // GET_MERKLE_PROOF
|
||||||
|
this.doHandleMessage(message.messageId, 'MERKLE_PROOF_RETRIEVED', message, (message: any) => message.proof);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'MERKLE_PROOF_VALIDATED': // VALIDATE_MERKLE_PROOF
|
||||||
|
this.doHandleMessage(message.messageId, 'MERKLE_PROOF_VALIDATED', message, (message: any) => message.isValid);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ERROR':
|
||||||
|
console.error('Error:', message);
|
||||||
|
this.errors[message.messageId] = message.error;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private doHandleMessage(messageId: string, messageType: string, message: any, callback: (message: any) => any) {
|
||||||
|
if (this.errors[messageId]) {
|
||||||
|
const error = this.errors[messageId];
|
||||||
|
delete this.errors[messageId];
|
||||||
|
EventBus.getInstance().emit(`ERROR_${messageType}`, messageId, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EventBus.getInstance().emit('MESSAGE_RECEIVED', message);
|
||||||
|
EventBus.getInstance().emit(messageType, messageId, callback(message));
|
||||||
|
}
|
||||||
|
}
|
125
src/sdk/RolesBuilder.ts
Normal file
125
src/sdk/RolesBuilder.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
type Member = string;
|
||||||
|
|
||||||
|
interface ValidationRule {
|
||||||
|
quorum: number;
|
||||||
|
fields: string[];
|
||||||
|
min_sig_member: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Storage {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
params?: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RoleDefinition {
|
||||||
|
members: Member[];
|
||||||
|
validation_rules: ValidationRule[];
|
||||||
|
storages: Storage[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RolesStructure {
|
||||||
|
demiurge: RoleDefinition;
|
||||||
|
owner: RoleDefinition;
|
||||||
|
validator: RoleDefinition;
|
||||||
|
collaborator: RoleDefinition;
|
||||||
|
client: RoleDefinition;
|
||||||
|
[key: string]: RoleDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class RolesBuilder {
|
||||||
|
private static instance: RolesBuilder;
|
||||||
|
private roles: RolesStructure;
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
this.roles = {
|
||||||
|
demiurge: {
|
||||||
|
members: [],
|
||||||
|
validation_rules: [],
|
||||||
|
storages: []
|
||||||
|
},
|
||||||
|
owner: {
|
||||||
|
members: [],
|
||||||
|
validation_rules: [
|
||||||
|
{
|
||||||
|
quorum: 0.5,
|
||||||
|
fields: ['roles'],
|
||||||
|
min_sig_member: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
storages: []
|
||||||
|
},
|
||||||
|
validator: {
|
||||||
|
members: [],
|
||||||
|
validation_rules: [
|
||||||
|
{
|
||||||
|
quorum: 0.5,
|
||||||
|
fields: ['idCertified', 'roles'],
|
||||||
|
min_sig_member: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
storages: []
|
||||||
|
},
|
||||||
|
collaborator: {
|
||||||
|
members: [],
|
||||||
|
validation_rules: [],
|
||||||
|
storages: []
|
||||||
|
},
|
||||||
|
client: {
|
||||||
|
members: [],
|
||||||
|
validation_rules: [],
|
||||||
|
storages: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(): RolesBuilder {
|
||||||
|
if (!RolesBuilder.instance) {
|
||||||
|
RolesBuilder.instance = new RolesBuilder();
|
||||||
|
}
|
||||||
|
return RolesBuilder.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addMember(role: string, memberId: string): RolesBuilder {
|
||||||
|
if (this.roles[role]) {
|
||||||
|
if (!this.roles[role].members.includes(memberId)) {
|
||||||
|
this.roles[role].members.push(memberId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addMembers(role: string, memberIds: string[]): RolesBuilder {
|
||||||
|
memberIds.forEach(id => this.addMember(role, id));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addValidationRule(role: string, rule: ValidationRule): RolesBuilder {
|
||||||
|
if (this.roles[role]) {
|
||||||
|
this.roles[role].validation_rules.push(rule);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addStorage(role: string, storage: Storage): RolesBuilder {
|
||||||
|
if (this.roles[role]) {
|
||||||
|
this.roles[role].storages.push(storage);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createRole(roleId: string): RolesBuilder {
|
||||||
|
if (!this.roles[roleId]) {
|
||||||
|
this.roles[roleId] = {
|
||||||
|
members: [],
|
||||||
|
validation_rules: [],
|
||||||
|
storages: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public build(): RolesStructure {
|
||||||
|
return { ...this.roles };
|
||||||
|
}
|
||||||
|
}
|
43
src/sdk/User.ts
Normal file
43
src/sdk/User.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
export default class User {
|
||||||
|
private static instance: User;
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public static getInstance(): User {
|
||||||
|
if (!User.instance) {
|
||||||
|
User.instance = new User();
|
||||||
|
}
|
||||||
|
return User.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setTokens(access: string, refresh: string): void {
|
||||||
|
sessionStorage.setItem('accessToken', access);
|
||||||
|
sessionStorage.setItem('refreshToken', refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAccessToken(): string | null {
|
||||||
|
return sessionStorage.getItem('accessToken');
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRefreshToken(): string | null {
|
||||||
|
return sessionStorage.getItem('refreshToken');
|
||||||
|
}
|
||||||
|
|
||||||
|
public setPairingId(pairingId: string): void {
|
||||||
|
sessionStorage.setItem('pairingId', pairingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPairingId(): string | null {
|
||||||
|
return sessionStorage.getItem('pairingId');
|
||||||
|
}
|
||||||
|
|
||||||
|
public isAuthenticated(): boolean {
|
||||||
|
return this.getAccessToken() !== null && this.getRefreshToken() !== null && this.getPairingId() !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear(): void {
|
||||||
|
sessionStorage.removeItem('accessToken');
|
||||||
|
sessionStorage.removeItem('refreshToken');
|
||||||
|
sessionStorage.removeItem('pairingId');
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user