Compare commits

...

18 Commits

Author SHA1 Message Date
Sosthene
a6a67d4b7c Fix DeedType and DocumentType 2025-09-10 15:56:30 +02:00
Sosthene
caff36edcc Refactor DeedTypeService
* async/await pattern where possible
* remove unused getDeedTypeByUid()
2025-09-10 15:56:30 +02:00
Sosthene
e2cd9a46b0 Update contracts for DeedType 2025-09-10 15:56:30 +02:00
Sosthene
297568d020 Catch empty attributes in completeCollaborators 2025-09-10 15:56:30 +02:00
Sosthene
c511aebbe4 Use the VALIDATOR_ID const 2025-09-10 15:56:30 +02:00
Sosthene
830524a2e9 Add getIdNotUserForOffice 2025-09-10 15:56:30 +02:00
Sosthene
f13eeedf73 LoginCallback heavy refactoring (wip?) 2025-09-10 15:56:30 +02:00
Sosthene
993e26c30b Add addCollaborators method to OfficeService 2025-09-10 15:56:30 +02:00
Sosthene
ceca20c9c7 Remove broken completeOfficeRoles 2025-09-10 15:56:30 +02:00
Sosthene
de2ff30960 MessageBus refactoring
* better error management
* Keep tracks of when messages sent don't have answers
* New convenient methods
2025-09-10 15:56:30 +02:00
Sosthene
68ecdf181f Make AuthModal works with decoupled Pairing creation 2025-09-10 15:56:30 +02:00
Sosthene
3c1196fb44 Heavy refactoring of importData 2025-09-10 15:56:30 +02:00
Sosthene
288f9b97e6 Update of process definition for most process creations 2025-09-10 15:56:30 +02:00
Sosthene
5b9184dfea Proper url for database calls in DatabaseService 2025-09-10 15:56:30 +02:00
Sosthene
1730c1d56f Add AppConstants 2025-09-10 15:56:30 +02:00
Sosthene
8a820d4cd3 Add NEXT_PUBLIC_BACK_API_PORT env variable 2025-09-10 15:56:30 +02:00
Sosthene
e3aadd67d8 Log as client (no verification) 2025-09-10 15:56:30 +02:00
Sosthene
a5ff28539c Set callbackurl to 127.0.0.1:3000 2025-09-10 15:56:30 +02:00
34 changed files with 1031 additions and 494 deletions

View File

@ -9,6 +9,7 @@ const nextConfig = {
// Will be available on both server and client // Will be available on both server and client
NEXT_PUBLIC_BACK_API_PROTOCOL: process.env.NEXT_PUBLIC_BACK_API_PROTOCOL, NEXT_PUBLIC_BACK_API_PROTOCOL: process.env.NEXT_PUBLIC_BACK_API_PROTOCOL,
NEXT_PUBLIC_BACK_API_HOST: process.env.NEXT_PUBLIC_BACK_API_HOST, NEXT_PUBLIC_BACK_API_HOST: process.env.NEXT_PUBLIC_BACK_API_HOST,
NEXT_PUBLIC_BACK_API_PORT: process.env.NEXT_PUBLIC_BACK_API_PORT,
NEXT_PUBLIC_BACK_API_ROOT_URL: process.env.NEXT_PUBLIC_BACK_API_ROOT_URL, NEXT_PUBLIC_BACK_API_ROOT_URL: process.env.NEXT_PUBLIC_BACK_API_ROOT_URL,
NEXT_PUBLIC_BACK_API_VERSION: process.env.NEXT_PUBLIC_BACK_API_VERSION, NEXT_PUBLIC_BACK_API_VERSION: process.env.NEXT_PUBLIC_BACK_API_VERSION,
NEXT_PUBLIC_FRONT_APP_HOST: process.env.NEXT_PUBLIC_FRONT_APP_HOST, NEXT_PUBLIC_FRONT_APP_HOST: process.env.NEXT_PUBLIC_FRONT_APP_HOST,
@ -26,6 +27,7 @@ const nextConfig = {
serverRuntimeConfig: { serverRuntimeConfig: {
NEXT_PUBLIC_BACK_API_PROTOCOL: process.env.NEXT_PUBLIC_BACK_API_PROTOCOL, NEXT_PUBLIC_BACK_API_PROTOCOL: process.env.NEXT_PUBLIC_BACK_API_PROTOCOL,
NEXT_PUBLIC_BACK_API_HOST: process.env.NEXT_PUBLIC_BACK_API_HOST, NEXT_PUBLIC_BACK_API_HOST: process.env.NEXT_PUBLIC_BACK_API_HOST,
NEXT_PUBLIC_BACK_API_PORT: process.env.NEXT_PUBLIC_BACK_API_PORT,
NEXT_PUBLIC_BACK_API_ROOT_URL: process.env.NEXT_PUBLIC_BACK_API_ROOT_URL, NEXT_PUBLIC_BACK_API_ROOT_URL: process.env.NEXT_PUBLIC_BACK_API_ROOT_URL,
NEXT_PUBLIC_BACK_API_VERSION: process.env.NEXT_PUBLIC_BACK_API_VERSION, NEXT_PUBLIC_BACK_API_VERSION: process.env.NEXT_PUBLIC_BACK_API_VERSION,
NEXT_PUBLIC_FRONT_APP_HOST: process.env.NEXT_PUBLIC_FRONT_APP_HOST, NEXT_PUBLIC_FRONT_APP_HOST: process.env.NEXT_PUBLIC_FRONT_APP_HOST,
@ -43,6 +45,7 @@ const nextConfig = {
env: { env: {
NEXT_PUBLIC_BACK_API_PROTOCOL: process.env.NEXT_PUBLIC_BACK_API_PROTOCOL, NEXT_PUBLIC_BACK_API_PROTOCOL: process.env.NEXT_PUBLIC_BACK_API_PROTOCOL,
NEXT_PUBLIC_BACK_API_HOST: process.env.NEXT_PUBLIC_BACK_API_HOST, NEXT_PUBLIC_BACK_API_HOST: process.env.NEXT_PUBLIC_BACK_API_HOST,
NEXT_PUBLIC_BACK_API_PORT: process.env.NEXT_PUBLIC_BACK_API_PORT,
NEXT_PUBLIC_BACK_API_ROOT_URL: process.env.NEXT_PUBLIC_BACK_API_ROOT_URL, NEXT_PUBLIC_BACK_API_ROOT_URL: process.env.NEXT_PUBLIC_BACK_API_ROOT_URL,
NEXT_PUBLIC_BACK_API_VERSION: process.env.NEXT_PUBLIC_BACK_API_VERSION, NEXT_PUBLIC_BACK_API_VERSION: process.env.NEXT_PUBLIC_BACK_API_VERSION,
NEXT_PUBLIC_FRONT_APP_HOST: process.env.NEXT_PUBLIC_FRONT_APP_HOST, NEXT_PUBLIC_FRONT_APP_HOST: process.env.NEXT_PUBLIC_FRONT_APP_HOST,

View File

@ -7,6 +7,7 @@ import AbstractService from './AbstractService';
import OfficeService from './OfficeService'; import OfficeService from './OfficeService';
import RoleService from './RoleService'; import RoleService from './RoleService';
import OfficeRoleService from './OfficeRoleService'; import OfficeRoleService from './OfficeRoleService';
import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants';
export default class CollaboratorService extends AbstractService { export default class CollaboratorService extends AbstractService {
@ -33,7 +34,7 @@ export default class CollaboratorService extends AbstractService {
const roles: any = { const roles: any = {
demiurge: { demiurge: {
members: [...[ownerId], validatorId], members: [ownerId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
}, },
@ -41,28 +42,23 @@ export default class CollaboratorService extends AbstractService {
members: [ownerId], members: [ownerId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 1,
fields: [...privateFields, 'roles', 'uid', 'utype'], fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'],
min_sig_member: 1, min_sig_member: 1,
}, },
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
}, },
validator: { validator: {
members: [validatorId], members: [validatorId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 1,
fields: ['idCertified', 'roles'], fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'],
min_sig_member: 1, min_sig_member: 1,
}, },
{
quorum: 0.0,
fields: [...privateFields],
min_sig_member: 0,
},
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
}, },
apophis: { apophis: {
members: [ownerId], members: [ownerId],
@ -265,26 +261,34 @@ export default class CollaboratorService extends AbstractService {
if (process.processData.office) { if (process.processData.office) {
const office: any = (await OfficeService.getOfficeByUid(process.processData.office.uid)).processData; const office: any = (await OfficeService.getOfficeByUid(process.processData.office.uid)).processData;
process.processData.office = { if (office) {
uid: office.uid, process.processData.office = {
idNot: office.idNot, uid: office.uid,
crpcen: office.crpcen, idNot: office.idNot,
name: office.name, crpcen: office.crpcen,
office_status: office.office_status name: office.name,
}; office_status: office.office_status
};
if (progressCallback) { if (progressCallback) {
progressiveProcess.processData.office = process.processData.office; progressiveProcess.processData.office = process.processData.office;
progressCallback(JSON.parse(JSON.stringify(progressiveProcess))); progressCallback(JSON.parse(JSON.stringify(progressiveProcess)));
}
} else {
console.error('Office not found');
} }
} }
if (process.processData.role) { if (process.processData.role) {
const role: any = (await RoleService.getRoleByUid(process.processData.role.uid)).processData; const role: any = (await RoleService.getRoleByUid(process.processData.role.uid)).processData;
process.processData.role = { if (!role) {
uid: role.uid, console.error('Role not found');
name: role.name } else {
}; process.processData.role = {
uid: role.uid,
name: role.name
};
}
if (progressCallback) { if (progressCallback) {
progressiveProcess.processData.role = process.processData.role; progressiveProcess.processData.role = process.processData.role;
@ -294,16 +298,20 @@ export default class CollaboratorService extends AbstractService {
if (process.processData.office_role) { if (process.processData.office_role) {
const officeRole: any = (await OfficeRoleService.getOfficeRoleByUid(process.processData.office_role.uid)).processData; const officeRole: any = (await OfficeRoleService.getOfficeRoleByUid(process.processData.office_role.uid)).processData;
process.processData.office_role = { if (!officeRole) {
uid: officeRole.uid, console.error('Office role not found');
name: officeRole.name, } else {
rules: officeRole.rules?.map((rule: any) => { process.processData.office_role = {
return { uid: officeRole.uid,
uid: rule.uid, name: officeRole.name,
name: rule.name rules: officeRole.rules?.map((rule: any) => {
}; return {
}) uid: rule.uid,
}; name: rule.name
};
})
};
}
if (progressCallback) { if (progressCallback) {
progressiveProcess.processData.office_role = process.processData.office_role; progressiveProcess.processData.office_role = process.processData.office_role;

View File

@ -27,7 +27,8 @@ export default class DatabaseService {
try { try {
// Construction de l'URL avec paramètres de pagination // Construction de l'URL avec paramètres de pagination
const url = `${publicRuntimeConfig.NEXT_PUBLIC_API_URL}/db/${tableName}?page=${page}&limit=${limit}`; const baseUrl = DatabaseService.buildBaseUrl();
const url = `${baseUrl}/db/${tableName}?page=${page}&limit=${limit}`;
// Appel à l'API REST // Appel à l'API REST
const response = await fetch(url, { const response = await fetch(url, {
@ -49,4 +50,28 @@ export default class DatabaseService {
throw error; throw error;
} }
} }
/**
* Construit l'URL de base en utilisant les variables d'environnement
* @returns URL de base de l'API
*/
private static buildBaseUrl(): string {
// Vérification des variables d'environnement requises
const requiredVars = [
'NEXT_PUBLIC_BACK_API_PROTOCOL',
'NEXT_PUBLIC_BACK_API_HOST',
'NEXT_PUBLIC_BACK_API_PORT',
'NEXT_PUBLIC_BACK_API_ROOT_URL',
'NEXT_PUBLIC_BACK_API_VERSION'
];
for (const varName of requiredVars) {
if (!publicRuntimeConfig[varName]) {
throw new Error(`${varName} is not defined in environment variables`);
}
}
// Construction de l'URL de base
return `${publicRuntimeConfig.NEXT_PUBLIC_BACK_API_PROTOCOL}${publicRuntimeConfig.NEXT_PUBLIC_BACK_API_HOST}:${publicRuntimeConfig.NEXT_PUBLIC_BACK_API_PORT}${publicRuntimeConfig.NEXT_PUBLIC_BACK_API_ROOT_URL}${publicRuntimeConfig.NEXT_PUBLIC_BACK_API_VERSION}`;
}
} }

View File

@ -4,7 +4,7 @@ import User from 'src/sdk/User';
import AbstractService from './AbstractService'; import AbstractService from './AbstractService';
import DocumentTypeService from './DocumentTypeService'; import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants';
export default class DeedTypeService extends AbstractService { export default class DeedTypeService extends AbstractService {
@ -12,7 +12,7 @@ export default class DeedTypeService extends AbstractService {
super(); super();
} }
public static createDeedType(deedTypeData: any, validatorId: string): Promise<any> { public static async createDeedType(deedTypeData: any, validatorId: string): Promise<{ processId: string, processData: any }> {
const ownerId: string = User.getInstance().getPairingId()!; const ownerId: string = User.getInstance().getPairingId()!;
const processData: any = { const processData: any = {
@ -25,105 +25,65 @@ export default class DeedTypeService extends AbstractService {
}; };
const privateFields: string[] = Object.keys(processData); const privateFields: string[] = Object.keys(processData);
privateFields.splice(privateFields.indexOf('uid'), 1); const allFields: string[] = [...privateFields, 'roles'];
privateFields.splice(privateFields.indexOf('utype'), 1);
privateFields.splice(privateFields.indexOf('isDeleted'), 1);
const roles: any = { const roles: any = {
demiurge: { demiurge: {
members: [...[ownerId], validatorId], members: [ownerId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
}, },
owner: { owner: {
members: [ownerId], members: [ownerId, validatorId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 0.01,
fields: [...privateFields, 'roles', 'uid', 'utype'], fields: allFields,
min_sig_member: 1, min_sig_member: 0.01,
}, },
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
},
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: { apophis: {
members: [ownerId], members: [ownerId, validatorId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
} }
}; };
return new Promise<any>((resolve: (processCreated: any) => void, reject: (error: string) => void) => { try {
this.messageBus.createProcess(processData, privateFields, roles).then((processCreated: any) => { const processCreated = await this.messageBus.createProcess(processData, privateFields, roles);
this.messageBus.notifyUpdate(processCreated.processId, processCreated.process.states[0].state_id).then(() => { await this.messageBus.notifyUpdate(processCreated.processId, processCreated.process.states[0].state_id);
this.messageBus.validateState(processCreated.processId, processCreated.process.states[0].state_id).then((_stateValidated: any) => { await this.messageBus.validateState(processCreated.processId, processCreated.process.states[0].state_id);
this.getDeedTypeByUid(processCreated.processData.uid).then(resolve).catch(reject); const finalProcessData = await this.messageBus.getProcessData(processCreated.processId);
}).catch(reject);
}).catch(reject); return { processId: processCreated.processId, processData: finalProcessData };
}).catch(reject); } catch (error) {
}); throw error;
}
} }
public static getDeedTypes(callback: (processes: any[]) => void, waitForAll: boolean = false): void { public static getDeedTypes(callback: (processes: any[]) => void): void {
// Check if we have valid cache // Check if we have valid cache
const items: any[] = this.getItems('_deed_types_'); const items: any[] = this.getItems('_deed_types_');
if (items.length > 0 && !waitForAll) { if (items.length > 0) {
setTimeout(() => callback([...items]), 0); setTimeout(() => callback([...items]), 0);
} }
this.messageBus.getProcessesDecoded((publicValues: any) => this.messageBus.getProcessesDecoded((values: any) => {
publicValues['uid'] && return values['utype'] === 'deedType'
publicValues['utype'] && && values['isDeleted'] === 'false';
publicValues['utype'] === 'deedType' && }).then(async (processes: any) => {
publicValues['isDeleted'] &&
publicValues['isDeleted'] === 'false' &&
!items.map((item: any) => item.processData.uid).includes(publicValues['uid'])
).then(async (processes: any[]) => {
if (processes.length === 0) { if (processes.length === 0) {
if (waitForAll) { callback([...items]);
callback([...items]);
}
return; return;
} }
const updatedItems: any[] = [...items]; const updatedItems: any[] = [...items];
for (let processIndex = 0; processIndex < processes.length; processIndex++) { for (let processIndex = 0; processIndex < processes.length; processIndex++) {
let process = processes[processIndex]; const process = processes[processIndex];
if (!waitForAll) {
process = await this.completeDeedType(process, (processInProgress: any) => {
const currentItems: any[] = [...updatedItems];
const existingIndex: number = currentItems.findIndex(item => item.processData?.uid === processInProgress.processData?.uid);
if (existingIndex >= 0) {
currentItems[existingIndex] = processInProgress;
} else {
currentItems.push(processInProgress);
}
callback(currentItems);
});
} else {
process = await this.completeDeedType(process);
}
// Update cache // Update cache
this.setItem('_deed_types_', process); this.setItem('_deed_types_', process);
@ -133,90 +93,33 @@ export default class DeedTypeService extends AbstractService {
} else { } else {
updatedItems.push(process); updatedItems.push(process);
} }
if (!waitForAll) {
callback([...updatedItems]);
}
} }
if (waitForAll) { callback([...updatedItems]);
callback([...updatedItems]);
}
}); });
} }
public static getDeedTypeByUid(uid: string): Promise<any> { public static async updateDeedType(processId: string, newData: any): Promise<void> {
// Check if we have valid cache try {
const item: any = this.getItem('_deed_types_', uid); const processUpdated = await this.messageBus.updateProcess(
if (item) { processId,
return Promise.resolve(item); { updated_at: new Date().toISOString(), ...newData },
[],
null
);
const newStateId: string = processUpdated.diffs[0]?.state_id;
await this.messageBus.notifyUpdate(processId, newStateId);
await this.messageBus.validateState(processId, newStateId);
const processData = await this.messageBus.getProcessData(processId);
// Update cache
this.setItem('_deed_types_', processData);
} catch (error) {
console.error('Failed to update deed type:', error);
throw error; // Re-throw to allow caller to handle
} }
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 {
let process: any = processes[0];
process = await this.completeDeedType(process);
// Update cache
this.setItem('_deed_types_', process);
resolve(process);
}
}).catch(reject);
});
} }
public static updateDeedType(process: any, newData: any): Promise<void> {
return new Promise<void>((resolve: () => 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(() => {
// Update cache
this.setItem('_deed_types_', process);
resolve();
}).catch((error) => console.error('Failed to validate state', error));
}).catch((error) => console.error('Failed to notify update', error));
}).catch((error) => console.error('Failed to update', error));
});
}
private static async completeDeedType(process: any, progressCallback?: (processInProgress: any) => void): Promise<any> {
const progressiveProcess: any = JSON.parse(JSON.stringify(process));
if (process.processData.document_types && process.processData.document_types.length > 0) {
progressiveProcess.processData.document_types = [];
if (progressCallback) {
progressCallback(progressiveProcess);
}
for (const document_type of process.processData.document_types) {
const documentTypeData = (await DocumentTypeService.getDocumentTypeByUid(document_type.uid)).processData;
progressiveProcess.processData.document_types.push(documentTypeData);
// Remove duplicates
progressiveProcess.processData.document_types = progressiveProcess.processData.document_types
.filter((item: any, index: number) => progressiveProcess.processData.document_types.findIndex((t: any) => t.uid === item.uid) === index);
if (progressCallback) {
progressCallback(JSON.parse(JSON.stringify(progressiveProcess)));
}
}
process.processData.document_types = progressiveProcess.processData.document_types;
}
return process;
}
} }

View File

@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import User from 'src/sdk/User'; import User from 'src/sdk/User';
import AbstractService from './AbstractService'; import AbstractService from './AbstractService';
import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants';
export default class DocumentTypeService extends AbstractService { export default class DocumentTypeService extends AbstractService {
@ -10,7 +11,7 @@ export default class DocumentTypeService extends AbstractService {
super(); super();
} }
public static createDocumentType(documentTypeData: any, validatorId: string): Promise<any> { public static createDocumentType(documentTypeData: any, validatorId: string): Promise<{ processId: string, processData: any }> {
const ownerId: string = User.getInstance().getPairingId()!; const ownerId: string = User.getInstance().getPairingId()!;
const processData: any = { const processData: any = {
@ -23,45 +24,27 @@ export default class DocumentTypeService extends AbstractService {
}; };
const privateFields: string[] = Object.keys(processData); const privateFields: string[] = Object.keys(processData);
privateFields.splice(privateFields.indexOf('uid'), 1); const allFields: string[] = [...privateFields, 'roles'];
privateFields.splice(privateFields.indexOf('utype'), 1);
privateFields.splice(privateFields.indexOf('isDeleted'), 1);
const roles: any = { const roles: any = {
demiurge: { demiurge: {
members: [...[ownerId], validatorId], members: [ownerId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
}, },
owner: { owner: {
members: [ownerId], members: [ownerId, validatorId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 0.01,
fields: [...privateFields, 'roles', 'uid', 'utype'], fields: allFields,
min_sig_member: 1, min_sig_member: 0.01,
}, },
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
},
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: { apophis: {
members: [ownerId], members: [ownerId, validatorId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
} }
@ -71,39 +54,80 @@ export default class DocumentTypeService extends AbstractService {
this.messageBus.createProcess(processData, privateFields, roles).then((processCreated: any) => { this.messageBus.createProcess(processData, privateFields, roles).then((processCreated: any) => {
this.messageBus.notifyUpdate(processCreated.processId, processCreated.process.states[0].state_id).then(() => { 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) => { this.messageBus.validateState(processCreated.processId, processCreated.process.states[0].state_id).then((_stateValidated: any) => {
this.getDocumentTypeByUid(processCreated.processData.uid).then(resolve).catch(reject); this.messageBus.getProcessData(processCreated.processId).then((processData: any) => {
resolve({ processId: processCreated.processId, processData: processData });
}).catch(reject);
}).catch(reject); }).catch(reject);
}).catch(reject); }).catch(reject);
}).catch(reject); }).catch(reject);
}); });
} }
public static getDocumentTypes(): Promise<any[]> { public static getDocumentTypes(callback: (processes: any[]) => void): void {
// Check if we have valid cache // Check if we have valid cache
const items: any[] = this.getItems('_document_types_'); const items: any[] = this.getItems('_document_types_');
if (items.length > 0) {
return this.messageBus.getProcessesDecoded((publicValues: any) => setTimeout(() => callback([...items]), 0);
publicValues['uid'] && }
publicValues['utype'] &&
publicValues['utype'] === 'documentType' && this.messageBus.getProcessesDecoded((values: any) => {
publicValues['isDeleted'] && return values['utype'] === 'documentType'
publicValues['isDeleted'] === 'false' && && values['isDeleted'] === 'false';
!items.map((item: any) => item.processData.uid).includes(publicValues['uid']) }).then(async (processes: any) => {
).then(async (processes: any[]) => {
if (processes.length === 0) { if (processes.length === 0) {
return items; callback([...items]);
} else { return;
for (const process of processes) {
// Update cache
this.setItem('_document_types_', process);
items.push(process);
}
return items;
} }
const updatedItems: any[] = [...items];
for (let processIndex = 0; processIndex < processes.length; processIndex++) {
const process = processes[processIndex];
// Update cache
this.setItem('_document_types_', process);
const existingIndex: number = updatedItems.findIndex(item => item.processId === process.processId);
if (existingIndex >= 0) {
updatedItems[existingIndex] = process;
} else {
updatedItems.push(process);
}
}
callback([...updatedItems]);
}); });
} }
public static getDocumentTypeByProcessId(processId: string): Promise<any> {
// Check if we have valid cache
const item: any = this.getItem('_document_types_', processId);
if (item) {
return Promise.resolve(item);
}
return new Promise<any>((resolve: (process: any) => void, reject: (error: string) => void) => {
this.messageBus.getProcessesDecoded((publicValues: any) => {
return publicValues['utype'] === 'documentType' && publicValues['isDeleted'] === 'false';
}).then((processes: any[]) => {
// Find the process with matching processId
const process = processes.find((p: any) => p.processId === processId);
if (!process) {
resolve(null);
return;
}
// Update cache
this.setItem('_document_types_', process);
resolve(process);
}).catch(reject);
});
}
// Keep the old method for backward compatibility but mark as deprecated
/** @deprecated Use getDocumentTypeByProcessId instead */
public static getDocumentTypeByUid(uid: string): Promise<any> { public static getDocumentTypeByUid(uid: string): Promise<any> {
// Check if we have valid cache // Check if we have valid cache
const item: any = this.getItem('_document_types_', uid); const item: any = this.getItem('_document_types_', uid);
@ -133,10 +157,9 @@ export default class DocumentTypeService extends AbstractService {
const newStateId: string = processUpdated.diffs[0]?.state_id; const newStateId: string = processUpdated.diffs[0]?.state_id;
this.messageBus.notifyUpdate(process.processId, newStateId).then(() => { this.messageBus.notifyUpdate(process.processId, newStateId).then(() => {
this.messageBus.validateState(process.processId, newStateId).then((_stateValidated) => { this.messageBus.validateState(process.processId, newStateId).then((_stateValidated) => {
const documentTypeUid: string = process.processData.uid; this.removeItem('_document_types_', process.processId);
this.removeItem('_document_types_', documentTypeUid);
this.getDocumentTypeByUid(documentTypeUid).then(resolve).catch(reject); this.getDocumentTypeByProcessId(process.processId).then(resolve).catch(reject);
}).catch(reject); }).catch(reject);
}).catch(reject); }).catch(reject);
}).catch(reject); }).catch(reject);

View File

@ -8,6 +8,9 @@ import RuleService from './RuleService';
import RuleGroupService from './RuleGroupService'; import RuleGroupService from './RuleGroupService';
import RoleService from './RoleService'; import RoleService from './RoleService';
import OfficeRoleService from './OfficeRoleService'; import OfficeRoleService from './OfficeRoleService';
import { DEFAULT_VALIDATOR_ID } from '@Front/Config/AppConstants';
const mandatoryRoles = ['Notaire', 'Collaborateur'];
/** /**
* Type pour le callback de progression * Type pour le callback de progression
@ -85,9 +88,13 @@ export default class ImportData {
// Exécuter l'étape et stocker le résultat si nécessaire // Exécuter l'étape et stocker le résultat si nécessaire
const result = await step.function(stepProgressCallback, results); const result = await step.function(stepProgressCallback, results);
if (result) { if (result !== undefined) {
results.push(result); results.push(result);
} else {
// Push empty array to maintain consistent indexing
results.push([]);
} }
console.log(`Step ${i} (${step.name}): ${result?.length || 0} items`);
} }
if (!await this.isDone()) { if (!await this.isDone()) {
@ -122,7 +129,7 @@ export default class ImportData {
const roles: any = { const roles: any = {
demiurge: { demiurge: {
members: [...[ownerId], validatorId], members: [ownerId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
}, },
@ -172,6 +179,7 @@ export default class ImportData {
} }
private static async importRules(onProgress?: (progress: number, description?: string) => void): Promise<any[]> { private static async importRules(onProgress?: (progress: number, description?: string) => void): Promise<any[]> {
console.log('Importing rules');
const rules: any[] = []; const rules: any[] = [];
const INIT_PROGRESS = 0; const INIT_PROGRESS = 0;
@ -201,10 +209,14 @@ export default class ImportData {
const existingRules: any[] = (await RuleService.getRules()).map((process: any) => process.processData); const existingRules: any[] = (await RuleService.getRules()).map((process: any) => process.processData);
const filteredRules: any[] = result.data.filter((rule: any) => !existingRules.some((existingRule: any) => existingRule.uid === rule.uid)); const filteredRules: any[] = result.data.filter((rule: any) => !existingRules.some((existingRule: any) => existingRule.uid === rule.uid));
if (filteredRules.length === 0) {
console.debug('All rules already imported');
}
const totalFilteredRules = filteredRules.length; const totalFilteredRules = filteredRules.length;
for (let i = 0; i < totalFilteredRules; i++) { for (let i = 0; i < totalFilteredRules; i++) {
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; console.log(`Adding rule ${filteredRules[i].name}`);
rules.push((await RuleService.createRule(filteredRules[i], validatorId)).processData); rules.push((await RuleService.createRule(filteredRules[i], DEFAULT_VALIDATOR_ID)).processData);
const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS; const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS;
const ruleProgressIncrement = progressRange / (totalFilteredRules * totalPages); const ruleProgressIncrement = progressRange / (totalFilteredRules * totalPages);
@ -225,6 +237,7 @@ export default class ImportData {
} }
private static async importRuleGroups(onProgress?: (progress: number, description?: string) => void): Promise<any[]> { private static async importRuleGroups(onProgress?: (progress: number, description?: string) => void): Promise<any[]> {
console.log('Importing rule groups');
const ruleGroups: any[] = []; const ruleGroups: any[] = [];
const INIT_PROGRESS = 0; const INIT_PROGRESS = 0;
@ -256,8 +269,8 @@ export default class ImportData {
const totalFilteredRuleGroups = filteredRuleGroups.length; const totalFilteredRuleGroups = filteredRuleGroups.length;
for (let i = 0; i < totalFilteredRuleGroups; i++) { for (let i = 0; i < totalFilteredRuleGroups; i++) {
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; console.log(`Adding rule group ${filteredRuleGroups[i].name}`);
ruleGroups.push((await RuleGroupService.createRuleGroup(filteredRuleGroups[i], validatorId)).processData); ruleGroups.push((await RuleGroupService.createRuleGroup(filteredRuleGroups[i], DEFAULT_VALIDATOR_ID)).processData);
const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS; const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS;
const ruleProgressIncrement = progressRange / (totalFilteredRuleGroups * totalPages); const ruleProgressIncrement = progressRange / (totalFilteredRuleGroups * totalPages);
@ -278,6 +291,7 @@ export default class ImportData {
} }
private static async importRoles(onProgress?: (progress: number, description?: string) => void): Promise<any[]> { private static async importRoles(onProgress?: (progress: number, description?: string) => void): Promise<any[]> {
console.log('Importing roles');
// Constantes de progression - pourraient être paramétrées // Constantes de progression - pourraient être paramétrées
const INIT_PROGRESS = 0; const INIT_PROGRESS = 0;
const FETCH_PROGRESS = 30; const FETCH_PROGRESS = 30;
@ -309,10 +323,14 @@ export default class ImportData {
onProgress?.(FETCH_PROGRESS, 'Récupération des rôles existants'); onProgress?.(FETCH_PROGRESS, 'Récupération des rôles existants');
const roles: any[] = processes.map((process: any) => process.processData); const roles: any[] = processes.map((process: any) => process.processData);
if (roles.length === 0) { if (roles.length === 0) {
const defaultRolesNames: string[] = defaultRoles.map((role: any) => role.name);
const totalRoles = defaultRoles.length; const totalRoles = defaultRoles.length;
for (let i = 0; i < totalRoles; i++) { for (let i = 0; i < totalRoles; i++) {
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; if (!defaultRolesNames.includes(roles[i].name)) {
roles.push((await RoleService.createRole(defaultRoles[i], validatorId)).processData); roles.push((await RoleService.createRole(defaultRoles[i], DEFAULT_VALIDATOR_ID)).processData);
} else {
console.log(`Role ${defaultRoles[i].name} already exists`);
}
// Progression dynamique pendant la création des rôles // Progression dynamique pendant la création des rôles
const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS; const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS;
@ -326,54 +344,166 @@ export default class ImportData {
}); });
} }
private static async importDefaultRoles(officeUid: string, ruleGroups: any[], onProgress?: (progress: number, description?: string) => void): Promise<any[]> {
console.log('Importing default roles');
let officeRoles: any[] = [];
const CREATE_START_PROGRESS = 60;
const CREATE_END_PROGRESS = 90;
onProgress?.(CREATE_START_PROGRESS, 'Création des rôles par défaut');
// Prepare the collaborator rules from rule groups
const collaboratorRules: any[] = ruleGroups
.map((ruleGroup: any) => ruleGroup.rules || [])
.reduce((acc: any, curr: any) => [...acc, ...curr], [])
.map((rule: any) => ({ uid: rule.uid }));
console.log(`Found ${collaboratorRules.length} collaborator rules from ${ruleGroups.length} rule groups`);
// Get fresh list of existing roles (including ones we just created)
const updatedExistingRoles = await OfficeRoleService.getOfficeRoles();
const existingRoles = updatedExistingRoles
.map((role: any) => role.processData)
.filter((roleData: any) => roleData.office?.uid === officeUid);
const existingRoleNames = existingRoles.map((role: any) => role.name);
const missingMandatoryRoles = mandatoryRoles.filter(roleName =>
!existingRoleNames.includes(roleName)
);
console.log(`Found ${existingRoleNames.length} existing roles, ${missingMandatoryRoles.length} mandatory roles missing`);
if (missingMandatoryRoles.length === 0) {
onProgress?.(CREATE_END_PROGRESS, 'Tous les rôles obligatoires existent déjà');
return officeRoles;
}
for (let i = 0; i < missingMandatoryRoles.length; i++) {
const roleName = missingMandatoryRoles[i];
const fallbackRole = {
name: roleName,
office: { uid: officeUid },
// Only Notaire gets rules, Collaborateur gets none
...(roleName === 'Notaire' && { rules: collaboratorRules })
};
console.log(`Creating missing mandatory role: ${roleName}`);
officeRoles.push((await OfficeRoleService.createOfficeRole(fallbackRole, DEFAULT_VALIDATOR_ID)).processData);
const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS;
const progress = CREATE_START_PROGRESS + ((i + 1) / missingMandatoryRoles.length) * progressRange;
onProgress?.(progress, `Création rôle obligatoire ${i + 1}/${missingMandatoryRoles.length} - ${roleName}`);
}
return officeRoles;
}
private static async importOfficeRoles(office: any, ruleGroups: any[], onProgress?: (progress: number, description?: string) => void): Promise<any[]> { private static async importOfficeRoles(office: any, ruleGroups: any[], onProgress?: (progress: number, description?: string) => void): Promise<any[]> {
// Constantes de progression - pourraient être paramétrées console.log('Importing office roles');
let officeRoles: any[] = [];
const officeUid = office.processData?.uid;
if (!officeUid) {
console.error('Office UID is not set');
return officeRoles;
}
console.log(`Processing ${ruleGroups.length} rule groups for office ${officeUid}`);
const INIT_PROGRESS = 0; const INIT_PROGRESS = 0;
const FETCH_PROGRESS = 30; const FETCH_PROGRESS = 30;
const CREATE_START_PROGRESS = FETCH_PROGRESS;
const CREATE_END_PROGRESS = 90; const CREATE_END_PROGRESS = 90;
const FINAL_PROGRESS = 100; const FINAL_PROGRESS = 100;
onProgress?.(INIT_PROGRESS, 'Initialisation'); onProgress?.(INIT_PROGRESS, 'Initialisation');
return await new Promise<any[]>((resolve: (roles: any[]) => void) => {
const collaboratorRules: any[] = ruleGroups
.map((ruleGroup: any) => ruleGroup.rules)
.reduce((acc: any, curr: any) => [...acc, ...curr], [])
.map((rule: any) => ({ uid: rule.uid }));
const defaultOfficeRoles: any[] = [ let page = 1;
{ let limit = 10;
name: 'Notaire', let totalPages = 1;
office: {
uid: office.uid
},
rules: collaboratorRules
},
{
name: 'Collaborateur',
office: {
uid: office.uid
}
}
];
OfficeRoleService.getOfficeRoles().then(async (processes: any[]) => {
onProgress?.(FETCH_PROGRESS, 'Récupération des rôles d\'office existants');
const officeRoles: any[] = processes.map((process: any) => process.processData);
if (officeRoles.length === 0) {
const totalOfficeRoles = defaultOfficeRoles.length;
for (let i = 0; i < totalOfficeRoles; i++) {
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
officeRoles.push((await OfficeRoleService.createOfficeRole(defaultOfficeRoles[i], validatorId)).processData);
// Progression dynamique pendant la création des rôles d'office onProgress?.(FETCH_PROGRESS, 'Récupération des rôles d\'office existants');
const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS; let result = await DatabaseService.getTableData('office_roles', page, limit);
const progress = CREATE_START_PROGRESS + ((i + 1) / totalOfficeRoles) * progressRange; if (result && result.success && result.pagination) {
onProgress?.(progress, `Création du rôle d'office ${i + 1}/${totalOfficeRoles} : ${defaultOfficeRoles[i].name}`); totalPages = result.pagination.totalPages || 1;
} }
}
onProgress?.(FINAL_PROGRESS, 'Importation des rôles d\'office terminée'); const FETCH_PAGE_PROGRESS_START = FETCH_PROGRESS;
resolve(officeRoles); const FETCH_PAGE_PROGRESS_END = 60;
}); const CREATE_START_PROGRESS = 60;
// Collect all office roles for this office from all pages
const allOfficeRolesForThisOffice: any[] = [];
while (result && result.success) {
const fetchPageProgress = FETCH_PAGE_PROGRESS_START + ((page / totalPages) * (FETCH_PAGE_PROGRESS_END - FETCH_PAGE_PROGRESS_START));
onProgress?.(fetchPageProgress, `Page ${page}/${totalPages} : Récupération des rôles d'office`);
// Collect office roles for this office from current page
const officeRolesFromPage = result.data.filter((officeRole: any) =>
officeRole.office?.uid === officeUid
);
allOfficeRolesForThisOffice.push(...officeRolesFromPage);
if (!result.pagination.hasNextPage) {
break;
}
page++;
result = await DatabaseService.getTableData('office_roles', page, limit);
}
console.log(`Found ${allOfficeRolesForThisOffice.length} office roles in database for this office`);
if (allOfficeRolesForThisOffice.length === 0) {
console.log('No office roles found in database, creating defaults');
return await this.importDefaultRoles(officeUid, ruleGroups, onProgress);
}
// Get all existing office role processes (to avoid duplicates)
const existingOfficeRoles: any[] = await OfficeRoleService.getOfficeRoles();
const existingOfficeRoleUids = existingOfficeRoles.map((existingRole: any) =>
existingRole.processData.uid
);
console.log(`Found ${existingOfficeRoles.length} existing office role processes`);
// Import all office roles found in database (if not already imported)
const dbRolesToImport = allOfficeRolesForThisOffice.filter((dbRole: any) =>
!existingOfficeRoleUids.includes(dbRole.uid)
);
console.log(`Importing ${dbRolesToImport.length} new office roles from database`);
for (let i = 0; i < dbRolesToImport.length; i++) {
const roleData = dbRolesToImport[i];
// Ensure office UID is set correctly
if (!roleData.office) {
roleData.office = { uid: officeUid };
} else if (!roleData.office.uid) {
roleData.office.uid = officeUid;
}
console.log(`Importing office role: ${roleData.name}`);
officeRoles.push((await OfficeRoleService.createOfficeRole(roleData, DEFAULT_VALIDATOR_ID)).processData);
const progressRange = (CREATE_END_PROGRESS - CREATE_START_PROGRESS) * 0.7; // 70% for db imports
const progress = CREATE_START_PROGRESS + ((i + 1) / dbRolesToImport.length) * progressRange;
onProgress?.(progress, `Import base de données ${i + 1}/${dbRolesToImport.length} - ${roleData.name}`);
}
// Now check for mandatory roles and create any missing ones
const mandatoryRolesProgress = CREATE_START_PROGRESS + (CREATE_END_PROGRESS - CREATE_START_PROGRESS) * 0.7;
onProgress?.(mandatoryRolesProgress, 'Vérification des rôles obligatoires');
const defaultRolesCreated = await this.importDefaultRoles(officeUid, ruleGroups, (subProgress, description) => {
// Map sub-progress to remaining 20% of total progress
const mappedProgress = mandatoryRolesProgress + (subProgress - 60) * 0.3 / 30; // Scale 60-90 to remaining 30%
onProgress?.(mappedProgress, description);
}); });
officeRoles.push(...defaultRolesCreated);
onProgress?.(FINAL_PROGRESS, 'Importation des rôles d\'office terminée');
return officeRoles;
} }
} }

View File

@ -6,6 +6,7 @@ import AbstractService from './AbstractService';
import OfficeService from './OfficeService'; import OfficeService from './OfficeService';
import RuleService from './RuleService'; import RuleService from './RuleService';
import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants';
export default class OfficeRoleService extends AbstractService { export default class OfficeRoleService extends AbstractService {
@ -32,7 +33,7 @@ export default class OfficeRoleService extends AbstractService {
const roles: any = { const roles: any = {
demiurge: { demiurge: {
members: [...[ownerId], validatorId], members: [ownerId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
}, },
@ -40,31 +41,26 @@ export default class OfficeRoleService extends AbstractService {
members: [ownerId], members: [ownerId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 0.01,
fields: [...privateFields, 'roles', 'uid', 'utype'], fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'],
min_sig_member: 1, min_sig_member: 0.01,
}, },
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
}, },
validator: { validator: {
members: [validatorId], members: [validatorId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 1,
fields: ['idCertified', 'roles'], fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'],
min_sig_member: 1, min_sig_member: 1,
}, },
{
quorum: 0.0,
fields: [...privateFields],
min_sig_member: 0,
},
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
}, },
apophis: { apophis: {
members: [ownerId], members: [ownerId, validatorId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
} }
@ -97,8 +93,6 @@ export default class OfficeRoleService extends AbstractService {
return items; return items;
} else { } else {
for (let process of processes) { for (let process of processes) {
process = await this.completeOfficeRole(process);
// Update cache // Update cache
this.setItem('_office_roles_', process); this.setItem('_office_roles_', process);
@ -125,14 +119,11 @@ export default class OfficeRoleService extends AbstractService {
publicValues['isDeleted'] && publicValues['isDeleted'] &&
publicValues['isDeleted'] === 'false' publicValues['isDeleted'] === 'false'
).then(async (processes: any[]) => { ).then(async (processes: any[]) => {
if (processes.length === 0) { if (processes.length !== 1) {
resolve(null); resolve(null);
} else { } else {
let process: any = processes[0];
process = await this.completeOfficeRole(process);
// Update cache // Update cache
this.setItem('_office_roles_', process); this.setItem('_office_roles_', processes[0]);
resolve(process); resolve(process);
} }
@ -156,24 +147,4 @@ export default class OfficeRoleService extends AbstractService {
}); });
} }
private static async completeOfficeRole(process: any): Promise<any> {
if (process.processData.office) {
process.processData.office = await new Promise<any>(async (resolve: (office: any) => void) => {
const office: any = (await OfficeService.getOfficeByUid(process.processData.office.uid)).processData;
resolve(office);
});
}
if (process.processData.rules && process.processData.rules.length > 0) {
process.processData.rules = await new Promise<any[]>(async (resolve: (rules: any[]) => void) => {
const rules: any[] = [];
for (const rule of process.processData.rules) {
rules.push((await RuleService.getRuleByUid(rule.uid)).processData);
}
resolve(rules);
});
}
return process;
}
} }

View File

@ -10,9 +10,12 @@ export default class OfficeService extends AbstractService {
super(); super();
} }
public static createOffice(officeData: any, validatorId: string): Promise<any> { public static createOffice(officeData: any, owners: string[], validatorId: string, storageUrls: string[]): Promise<any> {
const ownerId: string = User.getInstance().getPairingId()!; const ownerId: string = User.getInstance().getPairingId()!;
// Create a set for all owners to avoid duplicates
const ownersSet: Set<string> = new Set([...owners, ownerId]);
const processData: any = { const processData: any = {
uid: uuidv4(), uid: uuidv4(),
utype: 'office', utype: 'office',
@ -29,39 +32,34 @@ export default class OfficeService extends AbstractService {
const roles: any = { const roles: any = {
demiurge: { demiurge: {
members: [...[ownerId], validatorId], members: Array.from(ownersSet),
validation_rules: [], validation_rules: [],
storages: [] storages: []
}, },
owner: { owner: {
members: [ownerId], members: Array.from(ownersSet),
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 0.01, // effectively any owner can make any change
fields: [...privateFields, 'roles', 'uid', 'utype'], fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'],
min_sig_member: 1, min_sig_member: 0.01, // need to sign with at least one device
}, },
], ],
storages: [] storages: storageUrls
}, },
validator: { validator: {
members: [validatorId], members: [validatorId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 1, // validator can do anything alone
fields: ['idCertified', 'roles'], fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'],
min_sig_member: 1, min_sig_member: 1,
}, },
{
quorum: 0.0,
fields: [...privateFields],
min_sig_member: 0,
},
], ],
storages: [] storages: storageUrls
}, },
apophis: { apophis: {
members: [ownerId], members: Array.from(ownersSet), // any owner can terminate the office
validation_rules: [], validation_rules: [],
storages: [] storages: []
} }
@ -149,4 +147,43 @@ export default class OfficeService extends AbstractService {
}).catch(reject); }).catch(reject);
}); });
} }
public static addCollaborators(process: any, existingRoles: any, collaborators: any[]): Promise<void> {
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
const newRoles: any = existingRoles;
const owners: string[] = newRoles['owner'].members;
if (!owners) {
console.error('[addCollaborators] owner role not found');
return;
}
const previousOwnersLength: number = owners.length;
for (const collaborator of collaborators) {
if (owners.includes(collaborator)) {
console.debug('[addCollaborators] collaborator already in owner role');
continue;
} else {
owners.push(collaborator);
}
}
if (previousOwnersLength === owners.length) {
console.error('[addCollaborators] no new collaborators added');
return;
}
console.log('newRoles : ', newRoles);
this.messageBus.updateProcess(process.processId, { updated_at: new Date().toISOString() }, [], newRoles).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) => {
const officeUid: string = process.processData.uid;
this.removeItem('_offices_', officeUid);
this.getOfficeByUid(officeUid).then(resolve).catch(reject);
}).catch(reject);
}).catch(reject);
}).catch(reject);
});
}
} }

View File

@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import User from 'src/sdk/User'; import User from 'src/sdk/User';
import AbstractService from './AbstractService'; import AbstractService from './AbstractService';
import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants';
export default class RoleService extends AbstractService { export default class RoleService extends AbstractService {
@ -29,7 +30,7 @@ export default class RoleService extends AbstractService {
const roles: any = { const roles: any = {
demiurge: { demiurge: {
members: [...[ownerId], validatorId], members: [ownerId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
}, },
@ -37,31 +38,26 @@ export default class RoleService extends AbstractService {
members: [ownerId], members: [ownerId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 0.01,
fields: [...privateFields, 'roles', 'uid', 'utype'], fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'],
min_sig_member: 1, min_sig_member: 0.01,
}, },
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
}, },
validator: { validator: {
members: [validatorId], members: [validatorId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 1,
fields: ['idCertified', 'roles'], fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'],
min_sig_member: 1, min_sig_member: 1,
}, },
{
quorum: 0.0,
fields: [...privateFields],
min_sig_member: 0,
},
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
}, },
apophis: { apophis: {
members: [ownerId], members: [ownerId, validatorId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
} }

View File

@ -5,6 +5,7 @@ import User from 'src/sdk/User';
import AbstractService from './AbstractService'; import AbstractService from './AbstractService';
import RuleService from './RuleService'; import RuleService from './RuleService';
import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants';
export default class RuleGroupService extends AbstractService { export default class RuleGroupService extends AbstractService {
@ -31,7 +32,7 @@ export default class RuleGroupService extends AbstractService {
const roles: any = { const roles: any = {
demiurge: { demiurge: {
members: [...[ownerId], validatorId], members: [ownerId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
}, },
@ -39,31 +40,26 @@ export default class RuleGroupService extends AbstractService {
members: [ownerId], members: [ownerId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 0.01,
fields: [...privateFields, 'roles', 'uid', 'utype'], fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'],
min_sig_member: 1, min_sig_member: 0.01,
}, },
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
}, },
validator: { validator: {
members: [validatorId], members: [validatorId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 1,
fields: ['idCertified', 'roles'], fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'],
min_sig_member: 1, min_sig_member: 1,
}, },
{
quorum: 0.0,
fields: [...privateFields],
min_sig_member: 0,
},
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
}, },
apophis: { apophis: {
members: [ownerId], members: [ownerId, validatorId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
} }

View File

@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import User from 'src/sdk/User'; import User from 'src/sdk/User';
import AbstractService from './AbstractService'; import AbstractService from './AbstractService';
import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants';
export default class RuleService extends AbstractService { export default class RuleService extends AbstractService {
@ -29,7 +30,7 @@ export default class RuleService extends AbstractService {
const roles: any = { const roles: any = {
demiurge: { demiurge: {
members: [...[ownerId], validatorId], members: [ownerId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
}, },
@ -37,31 +38,26 @@ export default class RuleService extends AbstractService {
members: [ownerId], members: [ownerId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 0.01,
fields: [...privateFields, 'roles', 'uid', 'utype'], fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'],
min_sig_member: 1, min_sig_member: 0.01,
}, },
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
}, },
validator: { validator: {
members: [validatorId], members: [validatorId],
validation_rules: [ validation_rules: [
{ {
quorum: 0.5, quorum: 1,
fields: ['idCertified', 'roles'], fields: [...privateFields, 'roles', 'isDeleted', 'uid', 'utype'],
min_sig_member: 1, min_sig_member: 1,
}, },
{
quorum: 0.0,
fields: [...privateFields],
min_sig_member: 0,
},
], ],
storages: [] storages: [...DEFAULT_STORAGE_URLS]
}, },
apophis: { apophis: {
members: [ownerId], members: [ownerId, validatorId],
validation_rules: [], validation_rules: [],
storages: [] storages: []
} }

View File

@ -54,4 +54,17 @@ export default class Auth extends BaseApiService {
return Promise.reject(err); return Promise.reject(err);
} }
} }
public async getIdNotUserForOffice(officeId: string): Promise<any[]> {
const baseBackUrl = 'http://localhost:8080';//variables.BACK_API_PROTOCOL + variables.BACK_API_HOST;
const url = new URL(`${baseBackUrl}/api/v1/idnot/office/rattachements`);
url.searchParams.set('idNot', officeId);
try {
return await this.getRequest(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
} }

View File

@ -21,6 +21,7 @@ import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService"; import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService";
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService"; import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants";
type IProps = { type IProps = {
onChange?: (files: File[]) => void; onChange?: (files: File[]) => void;
@ -226,9 +227,8 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
uid: this.props.customer_uid, uid: this.props.customer_uid,
} }
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
DocumentService.createDocument(documentTypeData, validatorId).then((processCreated: any) => { DocumentService.createDocument(documentTypeData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => {
if (processCreated) { if (processCreated) {
const document: any = processCreated.processData; const document: any = processCreated.processData;
resolve(document); resolve(document);
@ -272,9 +272,8 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
file_blob: fileBlob, file_blob: fileBlob,
file_name: fileName file_name: fileName
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
FileService.createFile(fileData, validatorId).then((processCreated: any) => { FileService.createFile(fileData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => {
const fileUid: string = processCreated.processData.uid; const fileUid: string = processCreated.processData.uid;
DocumentService.getDocumentByUid(documentCreated.uid).then((process: any) => { DocumentService.getDocumentByUid(documentCreated.uid).then((process: any) => {

View File

@ -28,6 +28,8 @@ export default function DefaultDeedTypeDashboard(props: IProps) {
deedTypes.sort((a: any, b: any) => a.name.localeCompare(b.name)); deedTypes.sort((a: any, b: any) => a.name.localeCompare(b.name));
setDeedTypes(deedTypes); setDeedTypes(deedTypes);
} else {
console.log('[DefaultDeedTypeDashboard] No deed types found');
} }
}); });
}, []); }, []);
@ -51,7 +53,7 @@ export default function DefaultDeedTypeDashboard(props: IProps) {
} }
bottomButton={{ bottomButton={{
link: Module.getInstance().get().modules.pages.DeedTypes.pages.Create.props.path, link: Module.getInstance().get().modules.pages.DeedTypes.pages.Create.props.path,
text: "Créer une liste de pièces", text: "Créer une liste de pièces", // TODO I think this is misleading, should be "Créer un type d'acte"
}} }}
/> />
); );

View File

@ -13,6 +13,7 @@ import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
import { FileBlob, FileData } from "@Front/Api/Entities/types"; import { FileBlob, FileData } from "@Front/Api/Entities/types";
import WatermarkService from "@Front/Services/WatermarkService"; import WatermarkService from "@Front/Services/WatermarkService";
import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants";
type IProps = { type IProps = {
document: any; document: any;
@ -62,9 +63,8 @@ export default function DepositDocumentComponent(props: IProps) {
file_blob: fileBlob, file_blob: fileBlob,
file_name: fileName file_name: fileName
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
FileService.createFile(fileData, validatorId).then((processCreated: any) => { FileService.createFile(fileData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => {
const fileUid: string = processCreated.processData.uid; const fileUid: string = processCreated.processData.uid;
DocumentService.getDocumentByUid(document.uid!).then((process: any) => { DocumentService.getDocumentByUid(document.uid!).then((process: any) => {
@ -115,9 +115,8 @@ export default function DepositDocumentComponent(props: IProps) {
file_blob: fileBlob, file_blob: fileBlob,
file_name: fileName file_name: fileName
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
FileService.createFile(fileData, validatorId).then((processCreated: any) => { FileService.createFile(fileData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => {
const fileUid: string = processCreated.processData.uid; const fileUid: string = processCreated.processData.uid;
DocumentService.getDocumentByUid(document.uid!).then((process: any) => { DocumentService.getDocumentByUid(document.uid!).then((process: any) => {

View File

@ -18,6 +18,7 @@ import UserStore from "@Front/Stores/UserStore";
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService"; import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService";
import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants";
type IProps = {}; type IProps = {};
export default function DeedTypesCreate(props: IProps) { export default function DeedTypesCreate(props: IProps) {
@ -53,10 +54,9 @@ export default function DeedTypesCreate(props: IProps) {
uid: officeId, uid: officeId,
} }
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
LoaderService.getInstance().show(); LoaderService.getInstance().show();
DeedTypeService.createDeedType(deedTypeData, validatorId).then((processCreated: any) => { DeedTypeService.createDeedType(deedTypeData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => {
ToasterService.getInstance().success({ ToasterService.getInstance().success({
title: "Succès !", title: "Succès !",
description: "Type d'acte créé avec succès" description: "Type d'acte créé avec succès"

View File

@ -17,51 +17,81 @@ import classes from "./classes.module.scss";
import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService"; import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService";
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
import UserStore from "@Front/Stores/UserStore"; import UserStore from "@Front/Stores/UserStore";
import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants";
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[]>([]);
const router = useRouter(); const router = useRouter();
const handleCancel = useCallback(() => {
router.push(Module.getInstance().get().modules.pages.DocumentTypes.props.path);
}, [router]);
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 user: any = UserStore.instance.getUser(); const user: any = UserStore.instance.getUser();
const officeId: string = user.office.uid; if (!user) {
console.error("DocumentTypesCreate: User not found - user is null or undefined");
return;
}
const office = UserStore.instance.getOffice();
if (!office) {
console.error("DocumentTypesCreate: office not found - office is undefined or null");
return;
}
const officeId = office.processId;
const officeIdNot = office.processData.idNot;
const documentFormModel = DocumentType.hydrate<DocumentType>({ // const documentFormModel = DocumentType.hydrate<DocumentType>({
...values, // ...values,
office: Office.hydrate<Office>({ // office: Office.hydrate<Office>({
uid: officeId, // uid: officeId,
}) // })
}); // });
await validateOrReject(documentFormModel, { groups: ["createDocumentType"] }); // await validateOrReject(documentFormModel, { groups: ["createDocumentType"] });
const documentTypeData: any = { const documentTypeData: any = {
...values, ...values,
office: { office: {
uid: officeId, uid: officeId,
idNot: officeIdNot,
} }
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
LoaderService.getInstance().show(); LoaderService.getInstance().show();
DocumentTypeService.createDocumentType(documentTypeData, validatorId).then((processCreated: any) => { try {
const processCreated = await DocumentTypeService.createDocumentType(documentTypeData, DEFAULT_VALIDATOR_ID);
ToasterService.getInstance().success({ ToasterService.getInstance().success({
title: "Succès !", title: "Succès !",
description: "Type de document créé avec succès" description: "Type de document créé avec succès"
}); });
const documentTypeUid = processCreated.processId.split(':')[0];
if (!documentTypeUid) {
console.error("DocumentTypesCreate: documentTypeUid is undefined - processCreated.processId is missing");
return;
}
router.push( router.push(
Module.getInstance() Module.getInstance()
.get() .get()
.modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path.replace("[uid]", processCreated.processData.uid), .modules.pages.DocumentTypes.pages.DocumentTypesInformations.props.path.replace("[uid]", documentTypeUid),
); );
} catch (apiError) {
ToasterService.getInstance().error({
title: "Erreur !",
description: "Une erreur est survenue lors de la création du type de document"
});
console.error("Document type creation error:", apiError);
} finally {
LoaderService.getInstance().hide(); LoaderService.getInstance().hide();
}); }
} catch (e) { } catch (e) {
if (e instanceof Array) { if (e instanceof Array) {
setValidationError(e); setValidationError(e);
} }
LoaderService.getInstance().hide();
} }
}, },
[router], [router],
@ -90,7 +120,7 @@ export default function DocumentTypesCreate(props: IProps) {
validationError={validationError.find((error) => error.property === "public_description")} validationError={validationError.find((error) => error.property === "public_description")}
/> />
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.PRIMARY} styletype={EButtonstyletype.OUTLINED}> <Button variant={EButtonVariant.PRIMARY} styletype={EButtonstyletype.OUTLINED} onClick={handleCancel}>
Annuler Annuler
</Button> </Button>
<Button type="submit">Créer le document</Button> <Button type="submit">Créer le document</Button>

View File

@ -26,9 +26,12 @@ export default function DocumentTypesEdit() {
async function getDocumentType() { async function getDocumentType() {
if (!documentTypeUid) return; if (!documentTypeUid) return;
LoaderService.getInstance().show(); LoaderService.getInstance().show();
DocumentTypeService.getDocumentTypeByUid(documentTypeUid as string).then((process: any) => { DocumentTypeService.getDocumentTypeByProcessId(documentTypeUid as string).then((process: any) => {
if (process) { if (process) {
const documentType: any = process.processData; const documentType: any = {
...process.processData,
processId: process.processId
};
setDocumentTypeSelected(documentType); setDocumentTypeSelected(documentType);
} }
LoaderService.getInstance().hide(); LoaderService.getInstance().hide();
@ -53,7 +56,7 @@ export default function DocumentTypesEdit() {
return; return;
} }
LoaderService.getInstance().show(); LoaderService.getInstance().show();
DocumentTypeService.getDocumentTypeByUid(documentTypeUid as string).then((process: any) => { DocumentTypeService.getDocumentTypeByProcessId(documentTypeUid as string).then((process: any) => {
if (process) { if (process) {
DocumentTypeService.updateDocumentType(process, values).then(() => { DocumentTypeService.updateDocumentType(process, values).then(() => {
router.push( router.push(

View File

@ -22,13 +22,23 @@ export default function DocumentTypesInformations() {
useEffect(() => { useEffect(() => {
async function getDocument() { async function getDocument() {
if (!documentTypeUid) return; if (!documentTypeUid) {
console.log('DocumentTypesInformations: documentTypeUid is not available yet');
return;
}
DocumentTypeService.getDocumentTypeByUid(documentTypeUid as string).then((process: any) => { DocumentTypeService.getDocumentTypeByProcessId(documentTypeUid as string).then((process: any) => {
if (process) { if (process) {
const document: any = process.processData; const document: any = {
...process.processData,
processId: process.processId
};
setDocumentSelected(document); setDocumentSelected(document);
} else {
console.log('DocumentTypesInformations: No process found for processId:', documentTypeUid);
} }
}).catch((error) => {
console.error('DocumentTypesInformations: Error fetching document:', error);
}); });
} }
@ -69,13 +79,15 @@ export default function DocumentTypesInformations() {
</div> </div>
</div> </div>
<div className={classes["right"]}> <div className={classes["right"]}>
<Link {(documentSelected as any)?.processId && (
href={Module.getInstance() <Link
.get() href={Module.getInstance()
.modules.pages.DocumentTypes.pages.Edit.props.path.replace("[uid]", documentSelected?.uid ?? "")} .get()
className={classes["edit-icon-container"]}> .modules.pages.DocumentTypes.pages.Edit.props.path.replace("[uid]", (documentSelected as any).processId)}
<Image src={PenICon} alt="edit informations" /> className={classes["edit-icon-container"]}>
</Link> <Image src={PenICon} alt="edit informations" />
</Link>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -21,6 +21,7 @@ import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoub
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService";
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService"; import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
import { DEFAULT_VALIDATOR_ID } from "../../../../Config/AppConstants";
enum ESelectedOption { enum ESelectedOption {
EXISTING_CUSTOMER = "existing_customer", EXISTING_CUSTOMER = "existing_customer",
@ -78,10 +79,9 @@ export default function AddClientToFolder(props: IProps) {
const customerData: any = { const customerData: any = {
contact: values contact: values
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
LoaderService.getInstance().show(); LoaderService.getInstance().show();
CustomerService.createCustomer(customerData, validatorId).then((processCreated: any) => { CustomerService.createCustomer(customerData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => {
FolderService.getFolderByUid(folderUid as string).then((process: any) => { FolderService.getFolderByUid(folderUid as string).then((process: any) => {
if (process) { if (process) {
const customers: any[] = []; const customers: any[] = [];

View File

@ -13,6 +13,7 @@ import classes from "./classes.module.scss";
import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService"; import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService";
import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService"; import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService";
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants";
type IProps = { type IProps = {
isCreateDocumentModalVisible: boolean; isCreateDocumentModalVisible: boolean;
@ -85,10 +86,9 @@ export default function ParameterDocuments(props: IProps) {
}, },
public_description: visibleDescription, public_description: visibleDescription,
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
const documentType: any = await new Promise<any>((resolve: (documentType: any) => void) => { const documentType: any = await new Promise<any>((resolve: (documentType: any) => void) => {
DocumentTypeService.createDocumentType(documentTypeData, validatorId).then((processCreated: any) => { DocumentTypeService.createDocumentType(documentTypeData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => {
const documentType: any = processCreated.processData; const documentType: any = processCreated.processData;
resolve(documentType); resolve(documentType);
}); });

View File

@ -18,6 +18,7 @@ import backgroundImage from "@Assets/images/background_refonte.svg";
import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService"; import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService"; import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants";
export default function AskDocuments() { export default function AskDocuments() {
const router = useRouter(); const router = useRouter();
@ -78,9 +79,8 @@ export default function AskDocuments() {
document_status: EDocumentStatus.ASKED, document_status: EDocumentStatus.ASKED,
file_uid: null, file_uid: null,
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
await DocumentService.createDocument(documentData, validatorId); await DocumentService.createDocument(documentData, DEFAULT_VALIDATOR_ID);
} }
FolderService.refreshFolderByUid(folderUid as string).then(() => { FolderService.refreshFolderByUid(folderUid as string).then(() => {

View File

@ -18,6 +18,7 @@ import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService";
import NoteService from "src/common/Api/LeCoffreApi/sdk/NoteService"; import NoteService from "src/common/Api/LeCoffreApi/sdk/NoteService";
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants";
type IProps = {}; type IProps = {};
@ -124,10 +125,8 @@ class CreateCustomerNoteClass extends BasePage<IPropsClass, IState> {
uid: this.state.customer.uid uid: this.state.customer.uid
} }
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
LoaderService.getInstance().show(); LoaderService.getInstance().show();
NoteService.createNote(noteData, validatorId).then(() => { NoteService.createNote(noteData, DEFAULT_VALIDATOR_ID).then(() => {
this.props.router.push(this.backwardPath); this.props.router.push(this.backwardPath);
}); });
} catch (error) { } catch (error) {

View File

@ -23,6 +23,7 @@ import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService";
import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService"; import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService";
import FileService from "src/common/Api/LeCoffreApi/sdk/FileService"; import FileService from "src/common/Api/LeCoffreApi/sdk/FileService";
import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService";
import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants";
enum EClientSelection { enum EClientSelection {
ALL_CLIENTS = "all_clients", ALL_CLIENTS = "all_clients",
@ -97,9 +98,8 @@ export default function SendDocuments() {
file_blob: fileBlob, file_blob: fileBlob,
file_name: fileName file_name: fileName
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
FileService.createFile(fileData, validatorId).then((processCreated: any) => { FileService.createFile(fileData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => {
const fileUid: string = processCreated.processData.uid; const fileUid: string = processCreated.processData.uid;
const documentData: any = { const documentData: any = {
@ -117,7 +117,7 @@ export default function SendDocuments() {
document_status: EDocumentNotaryStatus.SENT document_status: EDocumentNotaryStatus.SENT
}; };
DocumentService.createDocument(documentData, validatorId).then(() => { DocumentService.createDocument(documentData, DEFAULT_VALIDATOR_ID).then(() => {
FolderService.refreshFolderByUid(folderUid as string).then(() => resolve()); FolderService.refreshFolderByUid(folderUid as string).then(() => resolve());
}); });
}); });

View File

@ -51,7 +51,7 @@ export default function StepEmail(props: IProps) {
); );
*/ */
router.push( 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`, `https://qual-connexion.idnot.fr/user/IdPOAuth2/authorize/idnot_idp_v1?client_id=B3CE56353EDB15A9&redirect_uri=http://127.0.0.1:3000/authorized-client&scope=openid,profile&response_type=code`,
); );
}, [router]); }, [router]);

View File

@ -21,6 +21,8 @@ import UserStore from "@Front/Stores/UserStore";
import AuthModal from "src/sdk/AuthModal"; import AuthModal from "src/sdk/AuthModal";
import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService";
import MessageBus from "src/sdk/MessageBus";
import { resolve } from "path";
export enum LoginStep { export enum LoginStep {
EMAIL, EMAIL,
@ -41,6 +43,7 @@ export default function Login() {
const [totpCode, setTotpCode] = useState<string>(""); const [totpCode, setTotpCode] = useState<string>("");
const [email, setEmail] = useState<string>(""); const [email, setEmail] = useState<string>("");
const [partialPhoneNumber, setPartialPhoneNumber] = useState<string>(""); const [partialPhoneNumber, setPartialPhoneNumber] = useState<string>("");
const [sessionId, setSessionId] = useState<string>("");
const [validationErrors, setValidationErrors] = useState<ValidationError[]>([]); const [validationErrors, setValidationErrors] = useState<ValidationError[]>([]);
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false); const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
@ -92,10 +95,13 @@ export default function Login() {
// If the code is valid setting it in state // If the code is valid setting it in state
if (res.validCode) { if (res.validCode) {
setTotpCode(values["totpCode"]); setTotpCode(values["totpCode"]);
setSessionId(res.sessionId); // Store the session ID
} }
*/ */
if ('1234' === values["totpCode"]) { if ('1234' === values["totpCode"]) {
setTotpCode(values["totpCode"]); setTotpCode(values["totpCode"]);
// For testing, set a mock session ID
setSessionId("mock-session-id-123");
} }
setValidationErrors([]); setValidationErrors([]);
@ -265,17 +271,43 @@ export default function Login() {
{isAuthModalOpen && <AuthModal {isAuthModalOpen && <AuthModal
isOpen={isAuthModalOpen} isOpen={isAuthModalOpen}
onClose={() => { onClose={() => {
CustomerService.getCustomers().then((processes: any[]) => { // After 4nk authentication is complete, get the process for the pairing ID
if (processes.length > 0) { MessageBus.getInstance().initMessageListener();
const customers: any[] = processes.map((process: any) => process.processData); MessageBus.getInstance().isReady().then(async () => {
const customer: any = customers.find((customer: any) => customer.contact.email === email); try {
if (customer) { // Find the customer
UserStore.instance.connect(customer); const customer: any = (await CustomerService.getCustomers())
.map((process: any) => process.processData)
.find((customer: any) => customer.contact.email === email);
// Get the pairing ID
const pairingId = await MessageBus.getInstance().getPairingId();
console.log('[Login] Got pairing ID:', pairingId);
// Get all processes
const processes = await MessageBus.getInstance().getProcesses();
console.log('[Login] Got processes:', Object.keys(processes));
const targetProcess = processes[pairingId];
if (targetProcess) {
console.log('[Login] Found target process:', targetProcess);
// Connect the user with the process data
UserStore.instance.connect(customer /*targetProcess*/);
router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path); router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path);
} else {
console.error('[Login] No process found for pairing ID:', pairingId);
// Handle the case where no process is found
} }
MessageBus.getInstance().destroyMessageListener();
} catch (error) {
console.error('[Login] Error getting process:', error);
MessageBus.getInstance().destroyMessageListener();
} }
setIsAuthModalOpen(false);
}); });
setIsAuthModalOpen(false);
}} }}
/>} />}
</div> </div>

View File

@ -26,6 +26,7 @@ import RoleService from "src/common/Api/LeCoffreApi/sdk/RoleService";
import OfficeService from "src/common/Api/LeCoffreApi/sdk/OfficeService"; import OfficeService from "src/common/Api/LeCoffreApi/sdk/OfficeService";
import OfficeRoleService from "src/common/Api/LeCoffreApi/sdk/OfficeRoleService"; import OfficeRoleService from "src/common/Api/LeCoffreApi/sdk/OfficeRoleService";
import CollaboratorService from "src/common/Api/LeCoffreApi/sdk/CollaboratorService"; import CollaboratorService from "src/common/Api/LeCoffreApi/sdk/CollaboratorService";
import { DEFAULT_STORAGE_URLS, DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants";
export default function LoginCallBack() { export default function LoginCallBack() {
const router = useRouter(); const router = useRouter();
@ -45,10 +46,23 @@ export default function LoginCallBack() {
const getOffice = async (idNotUser: any) => { const getOffice = async (idNotUser: any) => {
return await new Promise<any>((resolve: (office: any) => void) => { return await new Promise<any>((resolve: (office: any) => void) => {
OfficeService.getOffices().then((processes: any[]) => { OfficeService.getOffices().then((processes: any[]) => {
const officeFound: any = processes.length > 0 ? processes.map((process: any) => process.processData).find((office: any) => office.idNot === idNotUser.office.idNot) : null; const officeFound: any = processes.length > 0 ? processes.find((office: any) => office.processData.idNot === idNotUser.office.idNot) : null;
if (officeFound) { if (officeFound) {
resolve(officeFound); resolve(officeFound);
} else { } else {
// Some info must be here or have some value, just to be sure
if (!idNotUser.office.office_status || idNotUser.office.office_status !== 'ACTIVATED') {
console.error(`[LoginCallback] office_status is not ACTIVATED for idNot ${idNotUser.office.idNot}`);
return;
}
// I guess if we don't have crpcen that's also a big problem
if (!idNotUser.office.crpcen) {
console.error(`[LoginCallback] crpcen is not set for idNot ${idNotUser.office.idNot}`);
return;
}
// We create office
const officeData: any = { const officeData: any = {
idNot: idNotUser.office.idNot, idNot: idNotUser.office.idNot,
name: idNotUser.office.name, name: idNotUser.office.name,
@ -60,17 +74,29 @@ export default function LoginCallBack() {
city: idNotUser.office.address.city city: idNotUser.office.address.city
} }
}, },
office_status: 'ACTIVATED' office_status: idNotUser.office.office_status // must be ACTIVATED though
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
OfficeService.createOffice(officeData, validatorId).then((process: any) => { Auth.getInstance().getIdNotUserForOffice(idNotUser.office.idNot).then((users: any) => {
if (process) { console.log('users : ', users);
const office: any = process.processData; const activeUsers = users.result.filter((user: any) => user.activite === 'En exercice');
resolve(office); let officeCollaborators: any[] = [];
for (const user of activeUsers) {
CollaboratorService.getCollaboratorByUid(user.uid).then((collaborator: any) => {
console.log('collaborator : ', collaborator);
officeCollaborators.push(collaborator);
});
} }
OfficeService.createOffice(officeData, officeCollaborators, DEFAULT_VALIDATOR_ID, [...DEFAULT_STORAGE_URLS]).then((process: any) => {
if (process) {
const office: any = process.processData;
resolve(office);
}
});
}); });
} }
return;
}); });
}); });
}; };
@ -79,22 +105,36 @@ export default function LoginCallBack() {
return await new Promise<any>(async (resolve: (role: any) => void) => { return await new Promise<any>(async (resolve: (role: any) => void) => {
const processFound: any | null = await CollaboratorService.getCollaboratorBy({ idNot: idNotUser.idNot }); const processFound: any | null = await CollaboratorService.getCollaboratorBy({ idNot: idNotUser.idNot });
if (processFound) { if (processFound) {
resolve(processFound.processData); console.log('Found a collaborator for idNot', idNotUser.idNot);
} else { // TODO: check if the collaborator is in the office process
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
const office: any = await getOffice(idNotUser); const office: any = await getOffice(idNotUser);
// Take the role of the collaborator
if (!await ImportData.isDone()) { MessageBus.getInstance().getRolesForProcess(processFound.processId).then((roles: any) => {
LoaderService.getInstance().hide(); console.log('roles : ', roles);
setShowProgress(true); // We should find one pairing id in the role 'owner'
const owners = roles['owner'].members;
await ImportData.import(office, validatorId, (info: ProgressInfo) => { if (owners.length !== 1) {
setProgressInfo(info); console.error('[LoginCallback] owner should have 1 member');
return;
}
const ownerPairingId = owners[0];
// Now we can check if the owner pairing id is in the office roles
MessageBus.getInstance().getRolesForProcess(office.processId).then((officeRoles: any) => {
const officeOwners = officeRoles['owner'].members;
if (!officeOwners.includes(ownerPairingId)) {
// We add the newly created collaborator to the office roles
OfficeService.addCollaborators(office, officeRoles, [ownerPairingId]).then((process: any) => {
resolve(processFound);
});
} else {
// Nothing to do
resolve(processFound);
}
}); });
});
setShowProgress(false); } else {
LoaderService.getInstance().show(); console.log('No collaborator found for idNot', idNotUser.idNot);
} const office: any = await getOffice(idNotUser);
const role: any = (await RoleService.getRoles()) const role: any = (await RoleService.getRoles())
.map((process: any) => process.processData) .map((process: any) => process.processData)
@ -102,9 +142,21 @@ export default function LoginCallBack() {
const officeRole: any = (await OfficeRoleService.getOfficeRoles()) const officeRole: any = (await OfficeRoleService.getOfficeRoles())
.map((process: any) => process.processData) .map((process: any) => process.processData)
.filter((officeRole: any) => officeRole.office.uid === office.uid) .filter((officeRole: any) => officeRole.office.uid === office.processData.uid)
.find((officeRole: any) => officeRole.name === idNotUser.office_role.name); .find((officeRole: any) => officeRole.name === idNotUser.office_role.name);
if (!office || !role || !officeRole) {
LoaderService.getInstance().hide();
setShowProgress(true);
await ImportData.import(office, DEFAULT_VALIDATOR_ID, (info: ProgressInfo) => {
setProgressInfo(info);
});
setShowProgress(false);
LoaderService.getInstance().show();
}
const collaboratorData: any = { const collaboratorData: any = {
idNot: idNotUser.idNot, idNot: idNotUser.idNot,
contact: idNotUser.contact, contact: idNotUser.contact,
@ -119,12 +171,40 @@ export default function LoginCallBack() {
} }
}; };
CollaboratorService.createCollaborator(collaboratorData, validatorId).then((process: any) => { CollaboratorService.createCollaborator(collaboratorData, DEFAULT_VALIDATOR_ID).then((newCollaborator: any) => {
if (process) { if (newCollaborator) {
const collaborator: any = process.processData; // Now that we created the collaborator, we must check that it's in the office roles (probably not)
resolve(collaborator); MessageBus.getInstance().getRolesForProcess(newCollaborator.processId).then((roles: any) => {
console.log('roles : ', roles);
// We should have our own pairing id in roles['owner']
const owner = roles['owner'].members;
if (owner.length !== 1) {
console.error('[LoginCallback] owner should have 1 member');
return;
}
const ownerPairingId = owner[0];
if (ownerPairingId !== newCollaborator.processData.uid) {
console.error('[LoginCallback] owner pairing id is not the same as the collaborator uid');
return;
}
// is ownerPairingId in roles for the office process?
MessageBus.getInstance().getRolesForProcess(office.processId).then((officeRoles: any) => {
const officeOwners = officeRoles['owner'].members;
if (!officeOwners.includes(ownerPairingId)) {
// We add the newly created collaborator to the office roles
OfficeService.addCollaborators(office, officeRoles, [ownerPairingId]).then((process: any) => {
resolve(newCollaborator);
});
} else {
// Nothing to do
resolve(newCollaborator);
}
});
});
} }
}); });
} }
}); });
}; };
@ -153,6 +233,7 @@ export default function LoginCallBack() {
const user: any = await Auth.getInstance().getIdNotUser(code as string); const user: any = await Auth.getInstance().getIdNotUser(code as string);
setIdNotUser(user.idNotUser); setIdNotUser(user.idNotUser);
setIsAuthModalOpen(true); setIsAuthModalOpen(true);
console.log('[LoginCallback] idNotUser', idNotUser);
/* /*
const token: any = null; const token: any = null;
if (!token) return router.push(Module.getInstance().get().modules.pages.Login.props.path); if (!token) return router.push(Module.getInstance().get().modules.pages.Login.props.path);
@ -274,7 +355,11 @@ export default function LoginCallBack() {
MessageBus.getInstance().initMessageListener(); MessageBus.getInstance().initMessageListener();
MessageBus.getInstance().isReady().then(async () => { MessageBus.getInstance().isReady().then(async () => {
const collaborator: any = await getCollaborator(idNotUser); const collaborator: any = await getCollaborator(idNotUser);
UserStore.instance.connect(collaborator); if (!UserStore.instance.connect(collaborator)) {
console.error('[LoginCallback] collaborator not connected');
router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
return;
}
MessageBus.getInstance().destroyMessageListener(); MessageBus.getInstance().destroyMessageListener();
LoaderService.getInstance().hide(); LoaderService.getInstance().hide();

View File

@ -11,6 +11,7 @@ import Loader from "@Front/Components/DesignSystem/Loader";
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
import OfficeRibService from "src/common/Api/LeCoffreApi/sdk/OfficeRibService"; import OfficeRibService from "src/common/Api/LeCoffreApi/sdk/OfficeRibService";
import { DEFAULT_VALIDATOR_ID } from "../../../Config/AppConstants";
export default function Rib() { export default function Rib() {
const [documentList, setDocumentList] = useState<File[]>([]); const [documentList, setDocumentList] = useState<File[]>([]);
@ -80,9 +81,8 @@ export default function Rib() {
file_blob: fileBlob, file_blob: fileBlob,
file_name: fileName file_name: fileName
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
OfficeRibService.createOfficeRib(fileData, validatorId).then(() => { OfficeRibService.createOfficeRib(fileData, DEFAULT_VALIDATOR_ID).then(() => {
LoaderService.getInstance().hide(); LoaderService.getInstance().hide();
onCloseRibModal(); onCloseRibModal();

View File

@ -20,6 +20,7 @@ import { ValidationError } from "class-validator";
import RoleService from "src/common/Api/LeCoffreApi/sdk/RoleService"; import RoleService from "src/common/Api/LeCoffreApi/sdk/RoleService";
import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService";
import UserStore from "@Front/Stores/UserStore"; import UserStore from "@Front/Stores/UserStore";
import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants";
type IProps = {}; type IProps = {};
export default function RolesCreate(props: IProps) { export default function RolesCreate(props: IProps) {
@ -54,10 +55,9 @@ export default function RolesCreate(props: IProps) {
uid: officeId, uid: officeId,
} }
}; };
const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0';
LoaderService.getInstance().show(); LoaderService.getInstance().show();
RoleService.createRole(roleData, validatorId).then((processCreated: any) => { RoleService.createRole(roleData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => {
if (processCreated) { if (processCreated) {
ToasterService.getInstance().success({ ToasterService.getInstance().success({
title: "Succès !", title: "Succès !",

View File

@ -0,0 +1,20 @@
/**
* Application-wide constants
* This file contains constants that are used across multiple components and services
*/
export const APP_CONSTANTS = {
/**
* Default validator ID used for creating various entities (customers, documents, roles, etc.)
* This is a system-level validator that has permissions to create and manage entities
*/
DEFAULT_VALIDATOR_ID: 'c87bbb4873fd4c8427655b083b098c4b3f3a8ebf436d286b69c8036db4a2a029:0',
} as const;
// Export individual constants for easier imports
export const DEFAULT_VALIDATOR_ID = APP_CONSTANTS.DEFAULT_VALIDATOR_ID;
// Define
export const DEFAULT_STORAGE_URLS = [
'https://dev3.4nkweb.com/storage'
] as const;

View File

@ -5,6 +5,8 @@ export class FrontendVariables {
public BACK_API_HOST!: string; public BACK_API_HOST!: string;
public BACK_API_PORT!: string;
public BACK_API_ROOT_URL!: string; public BACK_API_ROOT_URL!: string;
public BACK_API_VERSION!: string; public BACK_API_VERSION!: string;

View File

@ -24,6 +24,7 @@ type AppPropsWithLayout = AppProps & {
} & { } & {
backApiProtocol: string; backApiProtocol: string;
backApiHost: string; backApiHost: string;
backApiPort: string;
backApiRootUrl: string; backApiRootUrl: string;
backApiVersion: string; backApiVersion: string;
frontAppHost: string; frontAppHost: string;
@ -46,6 +47,7 @@ const MyApp = (({
pageProps, pageProps,
backApiProtocol, backApiProtocol,
backApiHost, backApiHost,
backApiPort,
backApiRootUrl, backApiRootUrl,
backApiVersion, backApiVersion,
frontAppHost, frontAppHost,
@ -65,6 +67,7 @@ const MyApp = (({
const instance = FrontendVariables.getInstance(); const instance = FrontendVariables.getInstance();
instance.BACK_API_PROTOCOL = backApiProtocol; instance.BACK_API_PROTOCOL = backApiProtocol;
instance.BACK_API_HOST = backApiHost; instance.BACK_API_HOST = backApiHost;
instance.BACK_API_PORT = backApiPort;
instance.BACK_API_ROOT_URL = backApiRootUrl; instance.BACK_API_ROOT_URL = backApiRootUrl;
instance.BACK_API_VERSION = backApiVersion; instance.BACK_API_VERSION = backApiVersion;
instance.FRONT_APP_HOST = frontAppHost; instance.FRONT_APP_HOST = frontAppHost;
@ -129,6 +132,7 @@ MyApp.getInitialProps = async () => {
return { return {
backApiProtocol: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_PROTOCOL, backApiProtocol: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_PROTOCOL,
backApiHost: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_HOST, backApiHost: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_HOST,
backApiPort: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_PORT,
backApiRootUrl: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_ROOT_URL, backApiRootUrl: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_ROOT_URL,
backApiVersion: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_VERSION, backApiVersion: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_VERSION,
frontAppHost: publicRuntimeConfig.NEXT_PUBLIC_FRONT_APP_HOST, frontAppHost: publicRuntimeConfig.NEXT_PUBLIC_FRONT_APP_HOST,

View File

@ -70,6 +70,20 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
iframeRef.current.contentWindow!.postMessage({ type: 'GET_PAIRING_ID', accessToken: message.accessToken, messageId }, targetOrigin); iframeRef.current.contentWindow!.postMessage({ type: 'GET_PAIRING_ID', accessToken: message.accessToken, messageId }, targetOrigin);
break; break;
} }
case 'PAIRING_CREATED': {
console.log('[AuthModal] PAIRING_CREATED:', message);
User.getInstance().setPairingId(message.userPairingId);
setAuthSuccess(true);
setTimeout(() => {
setShowIframe(false);
setIsIframeReady(false);
setAuthSuccess(false);
onClose();
}, 500);
break;
}
case 'GET_PAIRING_ID': { case 'GET_PAIRING_ID': {
User.getInstance().setPairingId(message.userPairingId); User.getInstance().setPairingId(message.userPairingId);
@ -83,6 +97,36 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
}, 500); }, 500);
break; break;
} }
case 'ERROR': {
console.error('[AuthModal] handleMessage: error', message);
if (message.messageId.includes('GET_PAIRING_ID')) {
// We are not paired yet
const accessToken = User.getInstance().getAccessToken();
if (accessToken) {
// create a new pairing
const messageId = `CREATE_PAIRING_${uuidv4()}`;
iframeRef.current.contentWindow!.postMessage({ type: 'CREATE_PAIRING', accessToken, messageId }, targetOrigin);
} else {
// We don't have an access token
// Shouldn't happen
console.error('[AuthModal] handleMessage: error: we don\'t have an access token');
setShowIframe(false);
setIsIframeReady(false);
setAuthSuccess(false);
onClose();
}
} else if (message.messageId.includes('CREATE_PAIRING')) {
// Something went wrong while creating a pairing
// show stopper for now
console.error('[AuthModal] CREATE_PAIRING error:', message.error);
setShowIframe(false);
setIsIframeReady(false);
setAuthSuccess(false);
onClose();
}
break;
}
} }
}; };
window.addEventListener('message', handleMessage); window.addEventListener('message', handleMessage);

View File

@ -11,8 +11,8 @@ import { FileBlob } from '../front/Api/Entities/types';
export default class MessageBus { export default class MessageBus {
private static instance: MessageBus; private static instance: MessageBus;
private errors: { [key: string]: string } = {};
private isListening: boolean = false; private isListening: boolean = false;
private messagesSent: Set<string> = new Set();
public static getInstance(): MessageBus { public static getInstance(): MessageBus {
if (!MessageBus.instance) { if (!MessageBus.instance) {
@ -27,6 +27,11 @@ export default class MessageBus {
public destroyMessageListener(): void { public destroyMessageListener(): void {
window.removeEventListener('message', this.handleMessage.bind(this)); window.removeEventListener('message', this.handleMessage.bind(this));
this.isListening = false; // Reset the flag when destroying listener
}
public resetListeningState(): void {
this.isListening = false; // Allow external components to reset listening state
} }
public isReady(): Promise<void> { public isReady(): Promise<void> {
@ -41,27 +46,39 @@ export default class MessageBus {
}); });
} }
public isWaitingForMessage(): boolean {
return this.messagesSent.size > 0;
}
public requestLink(): Promise<void> { public requestLink(): Promise<void> {
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => { return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
const messageId = `REQUEST_LINK_${uuidv4()}`; const messageId = `REQUEST_LINK_${uuidv4()}`;
console.log('[MessageBus] requestLink - waiting for messageId:', messageId);
const unsubscribe = EventBus.getInstance().on('LINK_ACCEPTED', (responseId: string, message: { accessToken: string, refreshToken: string }) => { const unsubscribe = EventBus.getInstance().on('LINK_ACCEPTED', (responseId: string, message: { accessToken: string, refreshToken: string }) => {
console.log('[MessageBus] LINK_ACCEPTED received with responseId:', responseId, 'expected:', messageId);
if (responseId !== messageId) { if (responseId !== messageId) {
return; return;
} }
console.log('[MessageBus] LINK_ACCEPTED matched - resolving');
unsubscribe(); unsubscribe();
unsubscribeError();
User.getInstance().setTokens(message.accessToken, message.refreshToken); User.getInstance().setTokens(message.accessToken, message.refreshToken);
resolve(); resolve();
}); });
const unsubscribeError = EventBus.getInstance().on('ERROR_LINK_ACCEPTED', (responseId: string, error: string) => { const unsubscribeError = EventBus.getInstance().on('ERROR_LINK_ACCEPTED', (responseId: string, error: string) => {
console.log('[MessageBus] ERROR_LINK_ACCEPTED received with responseId:', responseId, 'expected:', messageId);
if (responseId !== messageId) { if (responseId !== messageId) {
return; return;
} }
console.log('[MessageBus] ERROR_LINK_ACCEPTED matched - rejecting with error:', error);
unsubscribe();
unsubscribeError(); unsubscribeError();
reject(error); reject(error);
}); });
console.log('[MessageBus] requestLink - sending REQUEST_LINK message');
this.sendMessage({ this.sendMessage({
type: 'REQUEST_LINK', type: 'REQUEST_LINK',
messageId messageId
@ -69,6 +86,40 @@ export default class MessageBus {
}); });
} }
public createPairing(): Promise<void> {
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
this.checkToken().then(() => {
const messageId = `CREATE_PAIRING_${uuidv4()}`;
const unsubscribe = EventBus.getInstance().on('PAIRING_CREATED', (responseId: string, pairingId: string) => {
if (responseId !== messageId) {
return;
}
unsubscribe();
User.getInstance().setPairingId(pairingId);
resolve();
});
const unsubscribeError = EventBus.getInstance().on('ERROR_CREATE_PAIRING', (responseId: string, error: string) => {
if (responseId !== messageId) {
return;
}
unsubscribeError();
reject(error);
});
const user = User.getInstance();
const accessToken = user.getAccessToken()!;
this.sendMessage({
type: 'CREATE_PAIRING',
accessToken,
messageId
});
}).catch(reject);
});
}
public getPairingId(): Promise<string> { public getPairingId(): Promise<string> {
return new Promise<string>((resolve: (pairingId: string) => void, reject: (error: string) => void) => { return new Promise<string>((resolve: (pairingId: string) => void, reject: (error: string) => void) => {
this.checkToken().then(() => { this.checkToken().then(() => {
@ -98,7 +149,7 @@ export default class MessageBus {
accessToken, accessToken,
messageId messageId
}); });
}).catch(console.error); }).catch(reject);
}); });
} }
@ -298,6 +349,114 @@ export default class MessageBus {
}); });
} }
public getRolesForProcess(processId: string): Promise<any> {
return new Promise<any>((resolve: (roles: any[]) => void, reject: (error: string) => void) => {
this.getAllProcesses().then((processes: any) => {
const process = processes[processId];
if (!process.states || process.states.length < 2) {
reject('No states found for process');
}
const roles = process.states[process.states.length - 2].roles;
if (!roles) {
reject('No roles found for process');
}
resolve(roles);
});
});
}
// Returns all processes details, including processes we only have public data for
public getAllProcessesDecoded(filterPublicValues: (publicValues: { [key: string]: any }) => boolean): Promise<any[]> {
return new Promise<any[]>((resolve: (processesDecoded: any[]) => void, reject: (error: string) => void) => {
this.getAllProcesses().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);
});
}
// Returns details about processes that we are involved in
public getProcessesDecoded(filterPublicValues: (publicValues: { [key: string]: any }) => boolean): Promise<any[]> { public getProcessesDecoded(filterPublicValues: (publicValues: { [key: string]: any }) => boolean): Promise<any[]> {
return new Promise<any[]>((resolve: (processesDecoded: any[]) => void, reject: (error: string) => void) => { return new Promise<any[]>((resolve: (processesDecoded: any[]) => void, reject: (error: string) => void) => {
this.getProcesses().then(async (processes: any) => { this.getProcesses().then(async (processes: any) => {
@ -417,7 +576,7 @@ export default class MessageBus {
accessToken, accessToken,
messageId messageId
}); });
}).catch(console.error); }).catch(reject);
}); });
} }
@ -454,12 +613,48 @@ export default class MessageBus {
accessToken, accessToken,
messageId messageId
}); });
}).catch(console.error); }).catch(reject);
}); });
} }
public getProcesses(): Promise<any> { // Returns all processes, including processes that have nothing to do with us
return new Promise<any>((resolve: (processes: any) => void, reject: (error: string) => void) => { public getAllProcesses(): Promise<{ [processId: string]: any }> {
return new Promise<{ [processId: string]: any }>((resolve: (processes: { [processId: string]: 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();
resolve(processes);
});
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(reject);
});
}
// Returns processes that we are involved in
public getProcesses(): Promise<{ [processId: string]: any }> {
return new Promise<{ [processId: string]: any }>((resolve: (processes: { [processId: string]: any }) => void, reject: (error: string) => void) => {
this.checkToken().then(() => { this.checkToken().then(() => {
const messageId = `GET_PROCESSES_${uuidv4()}`; const messageId = `GET_PROCESSES_${uuidv4()}`;
@ -471,7 +666,7 @@ export default class MessageBus {
// Filter processes by my processes // Filter processes by my processes
setTimeout(() => { setTimeout(() => {
this.getMyProcesses().then((myProcesses: any) => { this.getMyProcesses().then((myProcesses: string[]) => {
const processesFiltered: { [processId: string]: any } = {}; const processesFiltered: { [processId: string]: any } = {};
for (const processId of myProcesses) { for (const processId of myProcesses) {
const process = processes[processId]; const process = processes[processId];
@ -500,16 +695,18 @@ export default class MessageBus {
accessToken, accessToken,
messageId messageId
}); });
}).catch(console.error); }).catch(reject);
}); });
} }
public getMyProcesses(): Promise<any> { // Returns the processes id of processes we are involved in
return new Promise<any>((resolve: (processes: any) => void, reject: (error: string) => void) => { // It's meant to be used to filter processes in the getProcesses() method
public getMyProcesses(): Promise<string[]> {
return new Promise<string[]>((resolve: (processes: string[]) => void, reject: (error: string) => void) => {
this.checkToken().then(() => { this.checkToken().then(() => {
const messageId = `GET_MY_PROCESSES_${uuidv4()}`; const messageId = `GET_MY_PROCESSES_${uuidv4()}`;
const unsubscribe = EventBus.getInstance().on('GET_MY_PROCESSES', (responseId: string, processes: any) => { const unsubscribe = EventBus.getInstance().on('GET_MY_PROCESSES', (responseId: string, processes: string[]) => {
if (responseId !== messageId) { if (responseId !== messageId) {
return; return;
} }
@ -533,7 +730,7 @@ export default class MessageBus {
accessToken, accessToken,
messageId messageId
}); });
}).catch(console.error); }).catch(reject);
}); });
} }
@ -568,7 +765,7 @@ export default class MessageBus {
accessToken, accessToken,
messageId messageId
}); });
}).catch(console.error); }).catch(reject);
}); });
} }
@ -603,7 +800,7 @@ export default class MessageBus {
accessToken, accessToken,
messageId messageId
}); });
}).catch(console.error); }).catch(reject);
}); });
} }
@ -638,7 +835,7 @@ export default class MessageBus {
accessToken, accessToken,
messageId messageId
}); });
}).catch(console.error); }).catch(reject);
}); });
} }
@ -672,7 +869,7 @@ export default class MessageBus {
accessToken, accessToken,
messageId messageId
}); });
}).catch(console.error); }).catch(reject);
}); });
} }
@ -874,6 +1071,7 @@ export default class MessageBus {
const targetOrigin = IframeReference.getTargetOrigin(); const targetOrigin = IframeReference.getTargetOrigin();
const iframe = IframeReference.getIframe(); const iframe = IframeReference.getIframe();
iframe.contentWindow?.postMessage(message, targetOrigin); iframe.contentWindow?.postMessage(message, targetOrigin);
this.messagesSent.add(message.messageId);
} catch (error) { } catch (error) {
console.error('[MessageBus] sendMessage: error', error); console.error('[MessageBus] sendMessage: error', error);
} }
@ -975,19 +1173,26 @@ export default class MessageBus {
case 'ERROR': case 'ERROR':
console.error('Error:', message); console.error('Error:', message);
this.errors[message.messageId] = message.error; // Extract operation type from messageId by splitting on last underscore
const operationType = this.extractOperationTypeFromMessageId(message.messageId);
EventBus.getInstance().emit(`ERROR_${operationType}`, message.messageId, message.error);
break; break;
} }
} }
private doHandleMessage(messageId: string, messageType: string, message: any, callback: (message: any) => any) { private extractOperationTypeFromMessageId(messageId: string): string {
if (this.errors[messageId]) { // Split on last underscore to extract operation type
const error = this.errors[messageId]; // e.g., "GET_PAIRING_ID_abc123" -> "GET_PAIRING_ID"
delete this.errors[messageId]; const lastUnderscoreIndex = messageId.lastIndexOf('_');
EventBus.getInstance().emit(`ERROR_${messageType}`, messageId, error); if (lastUnderscoreIndex === -1) {
return; return messageId; // No underscore found, return as-is
} }
return messageId.substring(0, lastUnderscoreIndex);
}
private doHandleMessage(messageId: string, messageType: string, message: any, callback: (message: any) => any) {
EventBus.getInstance().emit('MESSAGE_RECEIVED', message); EventBus.getInstance().emit('MESSAGE_RECEIVED', message);
EventBus.getInstance().emit(messageType, messageId, callback(message)); EventBus.getInstance().emit(messageType, messageId, callback(message));
this.messagesSent.delete(messageId);
} }
} }