diff --git a/next.config.js b/next.config.js index f22a82d2..7301060d 100644 --- a/next.config.js +++ b/next.config.js @@ -9,6 +9,7 @@ const nextConfig = { // Will be available on both server and client 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_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_VERSION: process.env.NEXT_PUBLIC_BACK_API_VERSION, NEXT_PUBLIC_FRONT_APP_HOST: process.env.NEXT_PUBLIC_FRONT_APP_HOST, @@ -26,6 +27,7 @@ const nextConfig = { serverRuntimeConfig: { 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_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_VERSION: process.env.NEXT_PUBLIC_BACK_API_VERSION, NEXT_PUBLIC_FRONT_APP_HOST: process.env.NEXT_PUBLIC_FRONT_APP_HOST, @@ -43,6 +45,7 @@ const nextConfig = { env: { 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_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_VERSION: process.env.NEXT_PUBLIC_BACK_API_VERSION, NEXT_PUBLIC_FRONT_APP_HOST: process.env.NEXT_PUBLIC_FRONT_APP_HOST, diff --git a/src/common/Api/LeCoffreApi/sdk/CollaboratorService.ts b/src/common/Api/LeCoffreApi/sdk/CollaboratorService.ts index 0fe3d856..81ac8469 100644 --- a/src/common/Api/LeCoffreApi/sdk/CollaboratorService.ts +++ b/src/common/Api/LeCoffreApi/sdk/CollaboratorService.ts @@ -7,6 +7,7 @@ import AbstractService from './AbstractService'; import OfficeService from './OfficeService'; import RoleService from './RoleService'; import OfficeRoleService from './OfficeRoleService'; +import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants'; export default class CollaboratorService extends AbstractService { @@ -33,7 +34,7 @@ export default class CollaboratorService extends AbstractService { const roles: any = { demiurge: { - members: [...[ownerId], validatorId], + members: [ownerId], validation_rules: [], storages: [] }, @@ -41,28 +42,23 @@ export default class CollaboratorService extends AbstractService { members: [ownerId], validation_rules: [ { - quorum: 0.5, - fields: [...privateFields, 'roles', 'uid', 'utype'], + quorum: 1, + fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'], min_sig_member: 1, }, ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, validator: { members: [validatorId], validation_rules: [ { - quorum: 0.5, - fields: ['idCertified', 'roles'], + quorum: 1, + fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'], min_sig_member: 1, }, - { - quorum: 0.0, - fields: [...privateFields], - min_sig_member: 0, - }, ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, apophis: { members: [ownerId], @@ -265,26 +261,34 @@ export default class CollaboratorService extends AbstractService { if (process.processData.office) { const office: any = (await OfficeService.getOfficeByUid(process.processData.office.uid)).processData; - process.processData.office = { - uid: office.uid, - idNot: office.idNot, - crpcen: office.crpcen, - name: office.name, - office_status: office.office_status - }; + if (office) { + process.processData.office = { + uid: office.uid, + idNot: office.idNot, + crpcen: office.crpcen, + name: office.name, + office_status: office.office_status + }; - if (progressCallback) { - progressiveProcess.processData.office = process.processData.office; - progressCallback(JSON.parse(JSON.stringify(progressiveProcess))); + if (progressCallback) { + progressiveProcess.processData.office = process.processData.office; + progressCallback(JSON.parse(JSON.stringify(progressiveProcess))); + } + } else { + console.error('Office not found'); } } if (process.processData.role) { const role: any = (await RoleService.getRoleByUid(process.processData.role.uid)).processData; - process.processData.role = { - uid: role.uid, - name: role.name - }; + if (!role) { + console.error('Role not found'); + } else { + process.processData.role = { + uid: role.uid, + name: role.name + }; + } if (progressCallback) { progressiveProcess.processData.role = process.processData.role; @@ -294,16 +298,20 @@ export default class CollaboratorService extends AbstractService { if (process.processData.office_role) { const officeRole: any = (await OfficeRoleService.getOfficeRoleByUid(process.processData.office_role.uid)).processData; - process.processData.office_role = { - uid: officeRole.uid, - name: officeRole.name, - rules: officeRole.rules?.map((rule: any) => { - return { - uid: rule.uid, - name: rule.name - }; - }) - }; + if (!officeRole) { + console.error('Office role not found'); + } else { + process.processData.office_role = { + uid: officeRole.uid, + name: officeRole.name, + rules: officeRole.rules?.map((rule: any) => { + return { + uid: rule.uid, + name: rule.name + }; + }) + }; + } if (progressCallback) { progressiveProcess.processData.office_role = process.processData.office_role; diff --git a/src/common/Api/LeCoffreApi/sdk/DatabaseService.ts b/src/common/Api/LeCoffreApi/sdk/DatabaseService.ts index d8cbd83e..c5b0e18d 100644 --- a/src/common/Api/LeCoffreApi/sdk/DatabaseService.ts +++ b/src/common/Api/LeCoffreApi/sdk/DatabaseService.ts @@ -27,7 +27,8 @@ export default class DatabaseService { try { // 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 const response = await fetch(url, { @@ -49,4 +50,28 @@ export default class DatabaseService { 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}`; + } } diff --git a/src/common/Api/LeCoffreApi/sdk/DeedTypeService.ts b/src/common/Api/LeCoffreApi/sdk/DeedTypeService.ts index b07dae69..9f67c8ac 100644 --- a/src/common/Api/LeCoffreApi/sdk/DeedTypeService.ts +++ b/src/common/Api/LeCoffreApi/sdk/DeedTypeService.ts @@ -4,7 +4,7 @@ import User from 'src/sdk/User'; import AbstractService from './AbstractService'; -import DocumentTypeService from './DocumentTypeService'; +import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants'; export default class DeedTypeService extends AbstractService { @@ -12,7 +12,7 @@ export default class DeedTypeService extends AbstractService { super(); } - public static createDeedType(deedTypeData: any, validatorId: string): Promise { + public static async createDeedType(deedTypeData: any, validatorId: string): Promise<{ processId: string, processData: any }> { const ownerId: string = User.getInstance().getPairingId()!; const processData: any = { @@ -25,105 +25,65 @@ export default class DeedTypeService extends AbstractService { }; const privateFields: string[] = Object.keys(processData); - privateFields.splice(privateFields.indexOf('uid'), 1); - privateFields.splice(privateFields.indexOf('utype'), 1); - privateFields.splice(privateFields.indexOf('isDeleted'), 1); + const allFields: string[] = [...privateFields, 'roles']; const roles: any = { demiurge: { - members: [...[ownerId], validatorId], + members: [ownerId], validation_rules: [], storages: [] }, owner: { - members: [ownerId], + members: [ownerId, validatorId], validation_rules: [ { - quorum: 0.5, - fields: [...privateFields, 'roles', 'uid', 'utype'], - min_sig_member: 1, + quorum: 0.01, + fields: allFields, + min_sig_member: 0.01, }, ], - storages: [] - }, - validator: { - members: [validatorId], - validation_rules: [ - { - quorum: 0.5, - fields: ['idCertified', 'roles'], - min_sig_member: 1, - }, - { - quorum: 0.0, - fields: [...privateFields], - min_sig_member: 0, - }, - ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, apophis: { - members: [ownerId], + members: [ownerId, validatorId], validation_rules: [], storages: [] } }; - return new Promise((resolve: (processCreated: any) => void, reject: (error: string) => void) => { - this.messageBus.createProcess(processData, privateFields, roles).then((processCreated: any) => { - this.messageBus.notifyUpdate(processCreated.processId, processCreated.process.states[0].state_id).then(() => { - this.messageBus.validateState(processCreated.processId, processCreated.process.states[0].state_id).then((_stateValidated: any) => { - this.getDeedTypeByUid(processCreated.processData.uid).then(resolve).catch(reject); - }).catch(reject); - }).catch(reject); - }).catch(reject); - }); + try { + const processCreated = await this.messageBus.createProcess(processData, privateFields, roles); + await this.messageBus.notifyUpdate(processCreated.processId, processCreated.process.states[0].state_id); + await this.messageBus.validateState(processCreated.processId, processCreated.process.states[0].state_id); + const finalProcessData = await this.messageBus.getProcessData(processCreated.processId); + + return { processId: processCreated.processId, processData: finalProcessData }; + } 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 const items: any[] = this.getItems('_deed_types_'); - if (items.length > 0 && !waitForAll) { + if (items.length > 0) { setTimeout(() => callback([...items]), 0); } - - this.messageBus.getProcessesDecoded((publicValues: any) => - publicValues['uid'] && - publicValues['utype'] && - publicValues['utype'] === 'deedType' && - publicValues['isDeleted'] && - publicValues['isDeleted'] === 'false' && - !items.map((item: any) => item.processData.uid).includes(publicValues['uid']) - ).then(async (processes: any[]) => { + + this.messageBus.getProcessesDecoded((values: any) => { + return values['utype'] === 'deedType' + && values['isDeleted'] === 'false'; + }).then(async (processes: any) => { if (processes.length === 0) { - if (waitForAll) { - callback([...items]); - } + callback([...items]); return; } const updatedItems: any[] = [...items]; for (let processIndex = 0; processIndex < processes.length; processIndex++) { - let 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); - } - + const process = processes[processIndex]; + // Update cache this.setItem('_deed_types_', process); @@ -133,90 +93,33 @@ export default class DeedTypeService extends AbstractService { } else { updatedItems.push(process); } - - if (!waitForAll) { - callback([...updatedItems]); - } } - if (waitForAll) { - callback([...updatedItems]); - } + callback([...updatedItems]); }); } - public static getDeedTypeByUid(uid: string): Promise { - // Check if we have valid cache - const item: any = this.getItem('_deed_types_', uid); - if (item) { - return Promise.resolve(item); + public static async updateDeedType(processId: string, newData: any): Promise { + try { + const processUpdated = await this.messageBus.updateProcess( + processId, + { 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((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 { - return new Promise((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 { - 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; - } } diff --git a/src/common/Api/LeCoffreApi/sdk/DocumentTypeService.ts b/src/common/Api/LeCoffreApi/sdk/DocumentTypeService.ts index 2d3de4d2..6bab2586 100644 --- a/src/common/Api/LeCoffreApi/sdk/DocumentTypeService.ts +++ b/src/common/Api/LeCoffreApi/sdk/DocumentTypeService.ts @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid'; import User from 'src/sdk/User'; import AbstractService from './AbstractService'; +import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants'; export default class DocumentTypeService extends AbstractService { @@ -10,7 +11,7 @@ export default class DocumentTypeService extends AbstractService { super(); } - public static createDocumentType(documentTypeData: any, validatorId: string): Promise { + public static createDocumentType(documentTypeData: any, validatorId: string): Promise<{ processId: string, processData: any }> { const ownerId: string = User.getInstance().getPairingId()!; const processData: any = { @@ -23,45 +24,27 @@ export default class DocumentTypeService extends AbstractService { }; const privateFields: string[] = Object.keys(processData); - privateFields.splice(privateFields.indexOf('uid'), 1); - privateFields.splice(privateFields.indexOf('utype'), 1); - privateFields.splice(privateFields.indexOf('isDeleted'), 1); + const allFields: string[] = [...privateFields, 'roles']; const roles: any = { demiurge: { - members: [...[ownerId], validatorId], + members: [ownerId], validation_rules: [], storages: [] }, owner: { - members: [ownerId], + members: [ownerId, validatorId], validation_rules: [ { - quorum: 0.5, - fields: [...privateFields, 'roles', 'uid', 'utype'], - min_sig_member: 1, + quorum: 0.01, + fields: allFields, + min_sig_member: 0.01, }, ], - storages: [] - }, - validator: { - members: [validatorId], - validation_rules: [ - { - quorum: 0.5, - fields: ['idCertified', 'roles'], - min_sig_member: 1, - }, - { - quorum: 0.0, - fields: [...privateFields], - min_sig_member: 0, - }, - ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, apophis: { - members: [ownerId], + members: [ownerId, validatorId], validation_rules: [], storages: [] } @@ -71,39 +54,80 @@ export default class DocumentTypeService extends AbstractService { this.messageBus.createProcess(processData, privateFields, roles).then((processCreated: any) => { this.messageBus.notifyUpdate(processCreated.processId, processCreated.process.states[0].state_id).then(() => { this.messageBus.validateState(processCreated.processId, processCreated.process.states[0].state_id).then((_stateValidated: any) => { - 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); }); } - public static getDocumentTypes(): Promise { + public static getDocumentTypes(callback: (processes: any[]) => void): void { // Check if we have valid cache const items: any[] = this.getItems('_document_types_'); - - return this.messageBus.getProcessesDecoded((publicValues: any) => - publicValues['uid'] && - publicValues['utype'] && - publicValues['utype'] === 'documentType' && - publicValues['isDeleted'] && - publicValues['isDeleted'] === 'false' && - !items.map((item: any) => item.processData.uid).includes(publicValues['uid']) - ).then(async (processes: any[]) => { + if (items.length > 0) { + setTimeout(() => callback([...items]), 0); + } + + this.messageBus.getProcessesDecoded((values: any) => { + return values['utype'] === 'documentType' + && values['isDeleted'] === 'false'; + }).then(async (processes: any) => { if (processes.length === 0) { - return items; - } else { - for (const process of processes) { - // Update cache - this.setItem('_document_types_', process); - - items.push(process); - } - return items; + callback([...items]); + return; } + + 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 { + // Check if we have valid cache + const item: any = this.getItem('_document_types_', processId); + if (item) { + return Promise.resolve(item); + } + + return new Promise((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 { // Check if we have valid cache 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; this.messageBus.notifyUpdate(process.processId, newStateId).then(() => { this.messageBus.validateState(process.processId, newStateId).then((_stateValidated) => { - const documentTypeUid: string = process.processData.uid; - this.removeItem('_document_types_', documentTypeUid); + this.removeItem('_document_types_', process.processId); - this.getDocumentTypeByUid(documentTypeUid).then(resolve).catch(reject); + this.getDocumentTypeByProcessId(process.processId).then(resolve).catch(reject); }).catch(reject); }).catch(reject); }).catch(reject); diff --git a/src/common/Api/LeCoffreApi/sdk/ImportData.ts b/src/common/Api/LeCoffreApi/sdk/ImportData.ts index eb7b00c8..9fa35f98 100644 --- a/src/common/Api/LeCoffreApi/sdk/ImportData.ts +++ b/src/common/Api/LeCoffreApi/sdk/ImportData.ts @@ -8,6 +8,9 @@ import RuleService from './RuleService'; import RuleGroupService from './RuleGroupService'; import RoleService from './RoleService'; import OfficeRoleService from './OfficeRoleService'; +import { DEFAULT_VALIDATOR_ID } from '@Front/Config/AppConstants'; + +const mandatoryRoles = ['Notaire', 'Collaborateur']; /** * 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 const result = await step.function(stepProgressCallback, results); - if (result) { + if (result !== undefined) { 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()) { @@ -122,7 +129,7 @@ export default class ImportData { const roles: any = { demiurge: { - members: [...[ownerId], validatorId], + members: [ownerId], validation_rules: [], storages: [] }, @@ -172,6 +179,7 @@ export default class ImportData { } private static async importRules(onProgress?: (progress: number, description?: string) => void): Promise { + console.log('Importing rules'); const rules: any[] = []; const INIT_PROGRESS = 0; @@ -201,10 +209,14 @@ export default class ImportData { 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)); + if (filteredRules.length === 0) { + console.debug('All rules already imported'); + } + const totalFilteredRules = filteredRules.length; for (let i = 0; i < totalFilteredRules; i++) { - const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; - rules.push((await RuleService.createRule(filteredRules[i], validatorId)).processData); + console.log(`Adding rule ${filteredRules[i].name}`); + rules.push((await RuleService.createRule(filteredRules[i], DEFAULT_VALIDATOR_ID)).processData); const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS; const ruleProgressIncrement = progressRange / (totalFilteredRules * totalPages); @@ -225,6 +237,7 @@ export default class ImportData { } private static async importRuleGroups(onProgress?: (progress: number, description?: string) => void): Promise { + console.log('Importing rule groups'); const ruleGroups: any[] = []; const INIT_PROGRESS = 0; @@ -256,8 +269,8 @@ export default class ImportData { const totalFilteredRuleGroups = filteredRuleGroups.length; for (let i = 0; i < totalFilteredRuleGroups; i++) { - const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; - ruleGroups.push((await RuleGroupService.createRuleGroup(filteredRuleGroups[i], validatorId)).processData); + console.log(`Adding rule group ${filteredRuleGroups[i].name}`); + ruleGroups.push((await RuleGroupService.createRuleGroup(filteredRuleGroups[i], DEFAULT_VALIDATOR_ID)).processData); const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS; const ruleProgressIncrement = progressRange / (totalFilteredRuleGroups * totalPages); @@ -278,6 +291,7 @@ export default class ImportData { } private static async importRoles(onProgress?: (progress: number, description?: string) => void): Promise { + console.log('Importing roles'); // Constantes de progression - pourraient être paramétrées const INIT_PROGRESS = 0; const FETCH_PROGRESS = 30; @@ -309,10 +323,14 @@ export default class ImportData { onProgress?.(FETCH_PROGRESS, 'Récupération des rôles existants'); const roles: any[] = processes.map((process: any) => process.processData); if (roles.length === 0) { + const defaultRolesNames: string[] = defaultRoles.map((role: any) => role.name); const totalRoles = defaultRoles.length; for (let i = 0; i < totalRoles; i++) { - const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; - roles.push((await RoleService.createRole(defaultRoles[i], validatorId)).processData); + if (!defaultRolesNames.includes(roles[i].name)) { + 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 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 { + 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 { - // 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 FETCH_PROGRESS = 30; - const CREATE_START_PROGRESS = FETCH_PROGRESS; const CREATE_END_PROGRESS = 90; const FINAL_PROGRESS = 100; - onProgress?.(INIT_PROGRESS, 'Initialisation'); - return await new Promise((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[] = [ - { - name: 'Notaire', - 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); + let page = 1; + let limit = 10; + let totalPages = 1; - // Progression dynamique pendant la création des rôles d'office - const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS; - const progress = CREATE_START_PROGRESS + ((i + 1) / totalOfficeRoles) * progressRange; - onProgress?.(progress, `Création du rôle d'office ${i + 1}/${totalOfficeRoles} : ${defaultOfficeRoles[i].name}`); - } - } - onProgress?.(FINAL_PROGRESS, 'Importation des rôles d\'office terminée'); - resolve(officeRoles); - }); + onProgress?.(FETCH_PROGRESS, 'Récupération des rôles d\'office existants'); + let result = await DatabaseService.getTableData('office_roles', page, limit); + if (result && result.success && result.pagination) { + totalPages = result.pagination.totalPages || 1; + } + + const FETCH_PAGE_PROGRESS_START = FETCH_PROGRESS; + 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; } } diff --git a/src/common/Api/LeCoffreApi/sdk/OfficeRoleService.ts b/src/common/Api/LeCoffreApi/sdk/OfficeRoleService.ts index a9aabc1d..9b7d099d 100644 --- a/src/common/Api/LeCoffreApi/sdk/OfficeRoleService.ts +++ b/src/common/Api/LeCoffreApi/sdk/OfficeRoleService.ts @@ -6,6 +6,7 @@ import AbstractService from './AbstractService'; import OfficeService from './OfficeService'; import RuleService from './RuleService'; +import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants'; export default class OfficeRoleService extends AbstractService { @@ -32,7 +33,7 @@ export default class OfficeRoleService extends AbstractService { const roles: any = { demiurge: { - members: [...[ownerId], validatorId], + members: [ownerId], validation_rules: [], storages: [] }, @@ -40,31 +41,26 @@ export default class OfficeRoleService extends AbstractService { members: [ownerId], validation_rules: [ { - quorum: 0.5, - fields: [...privateFields, 'roles', 'uid', 'utype'], - min_sig_member: 1, + quorum: 0.01, + fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'], + min_sig_member: 0.01, }, ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, validator: { members: [validatorId], validation_rules: [ { - quorum: 0.5, - fields: ['idCertified', 'roles'], + quorum: 1, + fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'], min_sig_member: 1, }, - { - quorum: 0.0, - fields: [...privateFields], - min_sig_member: 0, - }, ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, apophis: { - members: [ownerId], + members: [ownerId, validatorId], validation_rules: [], storages: [] } @@ -97,8 +93,6 @@ export default class OfficeRoleService extends AbstractService { return items; } else { for (let process of processes) { - process = await this.completeOfficeRole(process); - // Update cache this.setItem('_office_roles_', process); @@ -125,14 +119,11 @@ export default class OfficeRoleService extends AbstractService { publicValues['isDeleted'] && publicValues['isDeleted'] === 'false' ).then(async (processes: any[]) => { - if (processes.length === 0) { + if (processes.length !== 1) { resolve(null); } else { - let process: any = processes[0]; - process = await this.completeOfficeRole(process); - // Update cache - this.setItem('_office_roles_', process); + this.setItem('_office_roles_', processes[0]); resolve(process); } @@ -156,24 +147,4 @@ export default class OfficeRoleService extends AbstractService { }); } - private static async completeOfficeRole(process: any): Promise { - if (process.processData.office) { - process.processData.office = await new Promise(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(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; - } } diff --git a/src/common/Api/LeCoffreApi/sdk/OfficeService.ts b/src/common/Api/LeCoffreApi/sdk/OfficeService.ts index 4b139c9f..8dde15ca 100644 --- a/src/common/Api/LeCoffreApi/sdk/OfficeService.ts +++ b/src/common/Api/LeCoffreApi/sdk/OfficeService.ts @@ -10,9 +10,12 @@ export default class OfficeService extends AbstractService { super(); } - public static createOffice(officeData: any, validatorId: string): Promise { + public static createOffice(officeData: any, owners: string[], validatorId: string, storageUrls: string[]): Promise { const ownerId: string = User.getInstance().getPairingId()!; + // Create a set for all owners to avoid duplicates + const ownersSet: Set = new Set([...owners, ownerId]); + const processData: any = { uid: uuidv4(), utype: 'office', @@ -29,39 +32,34 @@ export default class OfficeService extends AbstractService { const roles: any = { demiurge: { - members: [...[ownerId], validatorId], + members: Array.from(ownersSet), validation_rules: [], storages: [] }, owner: { - members: [ownerId], + members: Array.from(ownersSet), validation_rules: [ { - quorum: 0.5, - fields: [...privateFields, 'roles', 'uid', 'utype'], - min_sig_member: 1, + quorum: 0.01, // effectively any owner can make any change + fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'], + min_sig_member: 0.01, // need to sign with at least one device }, ], - storages: [] + storages: storageUrls }, validator: { members: [validatorId], validation_rules: [ { - quorum: 0.5, - fields: ['idCertified', 'roles'], + quorum: 1, // validator can do anything alone + fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'], min_sig_member: 1, }, - { - quorum: 0.0, - fields: [...privateFields], - min_sig_member: 0, - }, ], - storages: [] + storages: storageUrls }, apophis: { - members: [ownerId], + members: Array.from(ownersSet), // any owner can terminate the office validation_rules: [], storages: [] } @@ -149,4 +147,43 @@ export default class OfficeService extends AbstractService { }).catch(reject); }); } + public static addCollaborators(process: any, existingRoles: any, collaborators: any[]): Promise { + return new Promise((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); + }); + + } } diff --git a/src/common/Api/LeCoffreApi/sdk/RoleService.ts b/src/common/Api/LeCoffreApi/sdk/RoleService.ts index 6f21a627..e62b017d 100644 --- a/src/common/Api/LeCoffreApi/sdk/RoleService.ts +++ b/src/common/Api/LeCoffreApi/sdk/RoleService.ts @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid'; import User from 'src/sdk/User'; import AbstractService from './AbstractService'; +import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants'; export default class RoleService extends AbstractService { @@ -29,7 +30,7 @@ export default class RoleService extends AbstractService { const roles: any = { demiurge: { - members: [...[ownerId], validatorId], + members: [ownerId], validation_rules: [], storages: [] }, @@ -37,31 +38,26 @@ export default class RoleService extends AbstractService { members: [ownerId], validation_rules: [ { - quorum: 0.5, - fields: [...privateFields, 'roles', 'uid', 'utype'], - min_sig_member: 1, + quorum: 0.01, + fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'], + min_sig_member: 0.01, }, ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, validator: { members: [validatorId], validation_rules: [ { - quorum: 0.5, - fields: ['idCertified', 'roles'], + quorum: 1, + fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'], min_sig_member: 1, }, - { - quorum: 0.0, - fields: [...privateFields], - min_sig_member: 0, - }, ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, apophis: { - members: [ownerId], + members: [ownerId, validatorId], validation_rules: [], storages: [] } diff --git a/src/common/Api/LeCoffreApi/sdk/RuleGroupService.ts b/src/common/Api/LeCoffreApi/sdk/RuleGroupService.ts index c0775cde..f9d58ff1 100644 --- a/src/common/Api/LeCoffreApi/sdk/RuleGroupService.ts +++ b/src/common/Api/LeCoffreApi/sdk/RuleGroupService.ts @@ -5,6 +5,7 @@ import User from 'src/sdk/User'; import AbstractService from './AbstractService'; import RuleService from './RuleService'; +import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants'; export default class RuleGroupService extends AbstractService { @@ -31,7 +32,7 @@ export default class RuleGroupService extends AbstractService { const roles: any = { demiurge: { - members: [...[ownerId], validatorId], + members: [ownerId], validation_rules: [], storages: [] }, @@ -39,31 +40,26 @@ export default class RuleGroupService extends AbstractService { members: [ownerId], validation_rules: [ { - quorum: 0.5, - fields: [...privateFields, 'roles', 'uid', 'utype'], - min_sig_member: 1, + quorum: 0.01, + fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'], + min_sig_member: 0.01, }, ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, validator: { members: [validatorId], validation_rules: [ { - quorum: 0.5, - fields: ['idCertified', 'roles'], + quorum: 1, + fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'], min_sig_member: 1, }, - { - quorum: 0.0, - fields: [...privateFields], - min_sig_member: 0, - }, ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, apophis: { - members: [ownerId], + members: [ownerId, validatorId], validation_rules: [], storages: [] } diff --git a/src/common/Api/LeCoffreApi/sdk/RuleService.ts b/src/common/Api/LeCoffreApi/sdk/RuleService.ts index 9fd9bb4d..66a798e8 100644 --- a/src/common/Api/LeCoffreApi/sdk/RuleService.ts +++ b/src/common/Api/LeCoffreApi/sdk/RuleService.ts @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid'; import User from 'src/sdk/User'; import AbstractService from './AbstractService'; +import { DEFAULT_STORAGE_URLS } from '@Front/Config/AppConstants'; export default class RuleService extends AbstractService { @@ -29,7 +30,7 @@ export default class RuleService extends AbstractService { const roles: any = { demiurge: { - members: [...[ownerId], validatorId], + members: [ownerId], validation_rules: [], storages: [] }, @@ -37,31 +38,26 @@ export default class RuleService extends AbstractService { members: [ownerId], validation_rules: [ { - quorum: 0.5, - fields: [...privateFields, 'roles', 'uid', 'utype'], - min_sig_member: 1, + quorum: 0.01, + fields: [...privateFields, 'roles', 'uid', 'utype', 'isDeleted'], + min_sig_member: 0.01, }, ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, validator: { members: [validatorId], validation_rules: [ { - quorum: 0.5, - fields: ['idCertified', 'roles'], + quorum: 1, + fields: [...privateFields, 'roles', 'isDeleted', 'uid', 'utype'], min_sig_member: 1, }, - { - quorum: 0.0, - fields: [...privateFields], - min_sig_member: 0, - }, ], - storages: [] + storages: [...DEFAULT_STORAGE_URLS] }, apophis: { - members: [ownerId], + members: [ownerId, validatorId], validation_rules: [], storages: [] } diff --git a/src/front/Api/Auth/IdNot/index.ts b/src/front/Api/Auth/IdNot/index.ts index 2aad0c33..31dd35b0 100644 --- a/src/front/Api/Auth/IdNot/index.ts +++ b/src/front/Api/Auth/IdNot/index.ts @@ -54,4 +54,17 @@ export default class Auth extends BaseApiService { return Promise.reject(err); } } + + public async getIdNotUserForOffice(officeId: string): Promise { + 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); + } + } } diff --git a/src/front/Components/DesignSystem/DepositOtherDocument/index.tsx b/src/front/Components/DesignSystem/DepositOtherDocument/index.tsx index d6b39eb9..055a8627 100644 --- a/src/front/Components/DesignSystem/DepositOtherDocument/index.tsx +++ b/src/front/Components/DesignSystem/DepositOtherDocument/index.tsx @@ -21,6 +21,7 @@ import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService"; import FileService from "src/common/Api/LeCoffreApi/sdk/FileService"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService"; +import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; type IProps = { onChange?: (files: File[]) => void; @@ -226,9 +227,8 @@ export default class DepositOtherDocument extends React.Component { + DocumentService.createDocument(documentTypeData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => { if (processCreated) { const document: any = processCreated.processData; resolve(document); @@ -272,9 +272,8 @@ export default class DepositOtherDocument extends React.Component { + FileService.createFile(fileData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => { const fileUid: string = processCreated.processData.uid; DocumentService.getDocumentByUid(documentCreated.uid).then((process: any) => { diff --git a/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx b/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx index 018c4f63..f8440245 100644 --- a/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx +++ b/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx @@ -28,6 +28,8 @@ export default function DefaultDeedTypeDashboard(props: IProps) { deedTypes.sort((a: any, b: any) => a.name.localeCompare(b.name)); setDeedTypes(deedTypes); + } else { + console.log('[DefaultDeedTypeDashboard] No deed types found'); } }); }, []); @@ -51,7 +53,7 @@ export default function DefaultDeedTypeDashboard(props: IProps) { } bottomButton={{ 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" }} /> ); diff --git a/src/front/Components/Layouts/ClientDashboard/DepositDocumentComponent/index.tsx b/src/front/Components/Layouts/ClientDashboard/DepositDocumentComponent/index.tsx index 4cc67148..1dc95911 100644 --- a/src/front/Components/Layouts/ClientDashboard/DepositDocumentComponent/index.tsx +++ b/src/front/Components/Layouts/ClientDashboard/DepositDocumentComponent/index.tsx @@ -13,6 +13,7 @@ import FileService from "src/common/Api/LeCoffreApi/sdk/FileService"; import { FileBlob, FileData } from "@Front/Api/Entities/types"; import WatermarkService from "@Front/Services/WatermarkService"; +import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; type IProps = { document: any; @@ -62,9 +63,8 @@ export default function DepositDocumentComponent(props: IProps) { file_blob: fileBlob, 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; DocumentService.getDocumentByUid(document.uid!).then((process: any) => { @@ -115,9 +115,8 @@ export default function DepositDocumentComponent(props: IProps) { file_blob: fileBlob, 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; DocumentService.getDocumentByUid(document.uid!).then((process: any) => { diff --git a/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx b/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx index c914ea78..5255a395 100644 --- a/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx +++ b/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx @@ -18,6 +18,7 @@ import UserStore from "@Front/Stores/UserStore"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService"; +import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; type IProps = {}; export default function DeedTypesCreate(props: IProps) { @@ -53,10 +54,9 @@ export default function DeedTypesCreate(props: IProps) { uid: officeId, } }; - const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; LoaderService.getInstance().show(); - DeedTypeService.createDeedType(deedTypeData, validatorId).then((processCreated: any) => { + DeedTypeService.createDeedType(deedTypeData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => { ToasterService.getInstance().success({ title: "Succès !", description: "Type d'acte créé avec succès" diff --git a/src/front/Components/Layouts/DocumentTypes/DocumentTypesCreate/index.tsx b/src/front/Components/Layouts/DocumentTypes/DocumentTypesCreate/index.tsx index 0e53c8d3..81b65a47 100644 --- a/src/front/Components/Layouts/DocumentTypes/DocumentTypesCreate/index.tsx +++ b/src/front/Components/Layouts/DocumentTypes/DocumentTypesCreate/index.tsx @@ -17,51 +17,81 @@ import classes from "./classes.module.scss"; import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import UserStore from "@Front/Stores/UserStore"; +import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; type IProps = {}; export default function DocumentTypesCreate(props: IProps) { const [validationError, setValidationError] = useState([]); const router = useRouter(); + + const handleCancel = useCallback(() => { + router.push(Module.getInstance().get().modules.pages.DocumentTypes.props.path); + }, [router]); + const onSubmitHandler = useCallback( async (e: React.FormEvent | null, values: { [key: string]: string }) => { try { 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({ - ...values, - office: Office.hydrate({ - uid: officeId, - }) - }); - await validateOrReject(documentFormModel, { groups: ["createDocumentType"] }); + // const documentFormModel = DocumentType.hydrate({ + // ...values, + // office: Office.hydrate({ + // uid: officeId, + // }) + // }); + // await validateOrReject(documentFormModel, { groups: ["createDocumentType"] }); const documentTypeData: any = { ...values, office: { uid: officeId, + idNot: officeIdNot, } }; - const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; LoaderService.getInstance().show(); - DocumentTypeService.createDocumentType(documentTypeData, validatorId).then((processCreated: any) => { + try { + const processCreated = await DocumentTypeService.createDocumentType(documentTypeData, DEFAULT_VALIDATOR_ID); ToasterService.getInstance().success({ title: "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( Module.getInstance() .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(); - }); + } } catch (e) { if (e instanceof Array) { setValidationError(e); } + LoaderService.getInstance().hide(); } }, [router], @@ -90,7 +120,7 @@ export default function DocumentTypesCreate(props: IProps) { validationError={validationError.find((error) => error.property === "public_description")} />
- diff --git a/src/front/Components/Layouts/DocumentTypes/DocumentTypesEdit/index.tsx b/src/front/Components/Layouts/DocumentTypes/DocumentTypesEdit/index.tsx index bf17ef91..8eac9e3b 100644 --- a/src/front/Components/Layouts/DocumentTypes/DocumentTypesEdit/index.tsx +++ b/src/front/Components/Layouts/DocumentTypes/DocumentTypesEdit/index.tsx @@ -26,9 +26,12 @@ export default function DocumentTypesEdit() { async function getDocumentType() { if (!documentTypeUid) return; LoaderService.getInstance().show(); - DocumentTypeService.getDocumentTypeByUid(documentTypeUid as string).then((process: any) => { + DocumentTypeService.getDocumentTypeByProcessId(documentTypeUid as string).then((process: any) => { if (process) { - const documentType: any = process.processData; + const documentType: any = { + ...process.processData, + processId: process.processId + }; setDocumentTypeSelected(documentType); } LoaderService.getInstance().hide(); @@ -53,7 +56,7 @@ export default function DocumentTypesEdit() { return; } LoaderService.getInstance().show(); - DocumentTypeService.getDocumentTypeByUid(documentTypeUid as string).then((process: any) => { + DocumentTypeService.getDocumentTypeByProcessId(documentTypeUid as string).then((process: any) => { if (process) { DocumentTypeService.updateDocumentType(process, values).then(() => { router.push( diff --git a/src/front/Components/Layouts/DocumentTypes/DocumentTypesInformations/index.tsx b/src/front/Components/Layouts/DocumentTypes/DocumentTypesInformations/index.tsx index 892ee1c0..b5669140 100644 --- a/src/front/Components/Layouts/DocumentTypes/DocumentTypesInformations/index.tsx +++ b/src/front/Components/Layouts/DocumentTypes/DocumentTypesInformations/index.tsx @@ -22,13 +22,23 @@ export default function DocumentTypesInformations() { useEffect(() => { 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) { - const document: any = process.processData; + const document: any = { + ...process.processData, + processId: process.processId + }; 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() {
- - edit informations - + {(documentSelected as any)?.processId && ( + + edit informations + + )}
diff --git a/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx b/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx index ae2f53c3..d306e764 100644 --- a/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx +++ b/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx @@ -21,6 +21,7 @@ import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoub import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService"; +import { DEFAULT_VALIDATOR_ID } from "../../../../Config/AppConstants"; enum ESelectedOption { EXISTING_CUSTOMER = "existing_customer", @@ -78,10 +79,9 @@ export default function AddClientToFolder(props: IProps) { const customerData: any = { contact: values }; - const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; 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) => { if (process) { const customers: any[] = []; diff --git a/src/front/Components/Layouts/Folder/AskDocuments/ParameterDocuments/index.tsx b/src/front/Components/Layouts/Folder/AskDocuments/ParameterDocuments/index.tsx index 05d90cae..9d1a82d6 100644 --- a/src/front/Components/Layouts/Folder/AskDocuments/ParameterDocuments/index.tsx +++ b/src/front/Components/Layouts/Folder/AskDocuments/ParameterDocuments/index.tsx @@ -13,6 +13,7 @@ import classes from "./classes.module.scss"; import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService"; import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; +import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; type IProps = { isCreateDocumentModalVisible: boolean; @@ -85,10 +86,9 @@ export default function ParameterDocuments(props: IProps) { }, public_description: visibleDescription, }; - const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; const documentType: any = await new Promise((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; resolve(documentType); }); diff --git a/src/front/Components/Layouts/Folder/AskDocuments/index.tsx b/src/front/Components/Layouts/Folder/AskDocuments/index.tsx index 9b9e7f30..cb679c91 100644 --- a/src/front/Components/Layouts/Folder/AskDocuments/index.tsx +++ b/src/front/Components/Layouts/Folder/AskDocuments/index.tsx @@ -18,6 +18,7 @@ import backgroundImage from "@Assets/images/background_refonte.svg"; import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService"; import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; +import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; export default function AskDocuments() { const router = useRouter(); @@ -78,9 +79,8 @@ export default function AskDocuments() { document_status: EDocumentStatus.ASKED, 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(() => { diff --git a/src/front/Components/Layouts/Folder/CreateCustomerNote/index.tsx b/src/front/Components/Layouts/Folder/CreateCustomerNote/index.tsx index 55c9db9d..035d3d34 100644 --- a/src/front/Components/Layouts/Folder/CreateCustomerNote/index.tsx +++ b/src/front/Components/Layouts/Folder/CreateCustomerNote/index.tsx @@ -18,6 +18,7 @@ import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; import NoteService from "src/common/Api/LeCoffreApi/sdk/NoteService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; +import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; type IProps = {}; @@ -124,10 +125,8 @@ class CreateCustomerNoteClass extends BasePage { uid: this.state.customer.uid } }; - const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; - LoaderService.getInstance().show(); - NoteService.createNote(noteData, validatorId).then(() => { + NoteService.createNote(noteData, DEFAULT_VALIDATOR_ID).then(() => { this.props.router.push(this.backwardPath); }); } catch (error) { diff --git a/src/front/Components/Layouts/Folder/SendDocuments/index.tsx b/src/front/Components/Layouts/Folder/SendDocuments/index.tsx index 74a00bd5..d3cae195 100644 --- a/src/front/Components/Layouts/Folder/SendDocuments/index.tsx +++ b/src/front/Components/Layouts/Folder/SendDocuments/index.tsx @@ -23,6 +23,7 @@ import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService"; import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService"; import FileService from "src/common/Api/LeCoffreApi/sdk/FileService"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; +import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; enum EClientSelection { ALL_CLIENTS = "all_clients", @@ -97,9 +98,8 @@ export default function SendDocuments() { file_blob: fileBlob, 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 documentData: any = { @@ -117,7 +117,7 @@ export default function SendDocuments() { document_status: EDocumentNotaryStatus.SENT }; - DocumentService.createDocument(documentData, validatorId).then(() => { + DocumentService.createDocument(documentData, DEFAULT_VALIDATOR_ID).then(() => { FolderService.refreshFolderByUid(folderUid as string).then(() => resolve()); }); }); diff --git a/src/front/Components/Layouts/Login/index.tsx b/src/front/Components/Layouts/Login/index.tsx index 4173b937..220c6681 100644 --- a/src/front/Components/Layouts/Login/index.tsx +++ b/src/front/Components/Layouts/Login/index.tsx @@ -21,6 +21,8 @@ import UserStore from "@Front/Stores/UserStore"; import AuthModal from "src/sdk/AuthModal"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; +import MessageBus from "src/sdk/MessageBus"; +import { resolve } from "path"; export enum LoginStep { EMAIL, @@ -41,6 +43,7 @@ export default function Login() { const [totpCode, setTotpCode] = useState(""); const [email, setEmail] = useState(""); const [partialPhoneNumber, setPartialPhoneNumber] = useState(""); + const [sessionId, setSessionId] = useState(""); const [validationErrors, setValidationErrors] = useState([]); const [isAuthModalOpen, setIsAuthModalOpen] = useState(false); @@ -92,10 +95,13 @@ export default function Login() { // If the code is valid setting it in state if (res.validCode) { setTotpCode(values["totpCode"]); + setSessionId(res.sessionId); // Store the session ID } */ if ('1234' === values["totpCode"]) { setTotpCode(values["totpCode"]); + // For testing, set a mock session ID + setSessionId("mock-session-id-123"); } setValidationErrors([]); @@ -265,17 +271,43 @@ export default function Login() { {isAuthModalOpen && { - CustomerService.getCustomers().then((processes: any[]) => { - if (processes.length > 0) { - const customers: any[] = processes.map((process: any) => process.processData); - const customer: any = customers.find((customer: any) => customer.contact.email === email); - if (customer) { - UserStore.instance.connect(customer); + // After 4nk authentication is complete, get the process for the pairing ID + MessageBus.getInstance().initMessageListener(); + MessageBus.getInstance().isReady().then(async () => { + try { + // Find the 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); + } 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); }} />} diff --git a/src/front/Components/Layouts/LoginCallback/index.tsx b/src/front/Components/Layouts/LoginCallback/index.tsx index 5bf98e20..b28d1672 100644 --- a/src/front/Components/Layouts/LoginCallback/index.tsx +++ b/src/front/Components/Layouts/LoginCallback/index.tsx @@ -26,6 +26,7 @@ import RoleService from "src/common/Api/LeCoffreApi/sdk/RoleService"; import OfficeService from "src/common/Api/LeCoffreApi/sdk/OfficeService"; import OfficeRoleService from "src/common/Api/LeCoffreApi/sdk/OfficeRoleService"; import CollaboratorService from "src/common/Api/LeCoffreApi/sdk/CollaboratorService"; +import { DEFAULT_STORAGE_URLS, DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; export default function LoginCallBack() { const router = useRouter(); @@ -45,10 +46,23 @@ export default function LoginCallBack() { const getOffice = async (idNotUser: any) => { return await new Promise((resolve: (office: any) => void) => { 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) { resolve(officeFound); } 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 = { idNot: idNotUser.office.idNot, name: idNotUser.office.name, @@ -60,17 +74,29 @@ export default function LoginCallBack() { 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) => { - if (process) { - const office: any = process.processData; - resolve(office); + Auth.getInstance().getIdNotUserForOffice(idNotUser.office.idNot).then((users: any) => { + console.log('users : ', users); + const activeUsers = users.result.filter((user: any) => user.activite === 'En exercice'); + 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(async (resolve: (role: any) => void) => { const processFound: any | null = await CollaboratorService.getCollaboratorBy({ idNot: idNotUser.idNot }); if (processFound) { - resolve(processFound.processData); - } else { - const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; + console.log('Found a collaborator for idNot', idNotUser.idNot); + // TODO: check if the collaborator is in the office process const office: any = await getOffice(idNotUser); - - if (!await ImportData.isDone()) { - LoaderService.getInstance().hide(); - setShowProgress(true); - - await ImportData.import(office, validatorId, (info: ProgressInfo) => { - setProgressInfo(info); + // Take the role of the collaborator + MessageBus.getInstance().getRolesForProcess(processFound.processId).then((roles: any) => { + console.log('roles : ', roles); + // We should find one pairing id in the role 'owner' + const owners = roles['owner'].members; + if (owners.length !== 1) { + 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); - LoaderService.getInstance().show(); - } + }); + } else { + console.log('No collaborator found for idNot', idNotUser.idNot); + const office: any = await getOffice(idNotUser); const role: any = (await RoleService.getRoles()) .map((process: any) => process.processData) @@ -102,9 +142,21 @@ export default function LoginCallBack() { const officeRole: any = (await OfficeRoleService.getOfficeRoles()) .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); + 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 = { idNot: idNotUser.idNot, contact: idNotUser.contact, @@ -119,12 +171,40 @@ export default function LoginCallBack() { } }; - CollaboratorService.createCollaborator(collaboratorData, validatorId).then((process: any) => { - if (process) { - const collaborator: any = process.processData; - resolve(collaborator); + CollaboratorService.createCollaborator(collaboratorData, DEFAULT_VALIDATOR_ID).then((newCollaborator: any) => { + if (newCollaborator) { + // Now that we created the collaborator, we must check that it's in the office roles (probably not) + 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); setIdNotUser(user.idNotUser); setIsAuthModalOpen(true); + console.log('[LoginCallback] idNotUser', idNotUser); /* const token: any = null; 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().isReady().then(async () => { 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(); LoaderService.getInstance().hide(); diff --git a/src/front/Components/Layouts/Rib/index.tsx b/src/front/Components/Layouts/Rib/index.tsx index 66a6c3b0..2d483da1 100644 --- a/src/front/Components/Layouts/Rib/index.tsx +++ b/src/front/Components/Layouts/Rib/index.tsx @@ -11,6 +11,7 @@ import Loader from "@Front/Components/DesignSystem/Loader"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import OfficeRibService from "src/common/Api/LeCoffreApi/sdk/OfficeRibService"; +import { DEFAULT_VALIDATOR_ID } from "../../../Config/AppConstants"; export default function Rib() { const [documentList, setDocumentList] = useState([]); @@ -80,9 +81,8 @@ export default function Rib() { file_blob: fileBlob, file_name: fileName }; - const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; - OfficeRibService.createOfficeRib(fileData, validatorId).then(() => { + OfficeRibService.createOfficeRib(fileData, DEFAULT_VALIDATOR_ID).then(() => { LoaderService.getInstance().hide(); onCloseRibModal(); diff --git a/src/front/Components/Layouts/Roles/RolesCreate/index.tsx b/src/front/Components/Layouts/Roles/RolesCreate/index.tsx index a6d7cee0..e2ac6696 100644 --- a/src/front/Components/Layouts/Roles/RolesCreate/index.tsx +++ b/src/front/Components/Layouts/Roles/RolesCreate/index.tsx @@ -20,6 +20,7 @@ import { ValidationError } from "class-validator"; import RoleService from "src/common/Api/LeCoffreApi/sdk/RoleService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import UserStore from "@Front/Stores/UserStore"; +import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; type IProps = {}; export default function RolesCreate(props: IProps) { @@ -54,10 +55,9 @@ export default function RolesCreate(props: IProps) { uid: officeId, } }; - const validatorId: string = '884cb36a346a79af8697559f16940141f068bdf1656f88fa0df0e9ecd7311fb8:0'; LoaderService.getInstance().show(); - RoleService.createRole(roleData, validatorId).then((processCreated: any) => { + RoleService.createRole(roleData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => { if (processCreated) { ToasterService.getInstance().success({ title: "Succès !", diff --git a/src/front/Config/AppConstants.ts b/src/front/Config/AppConstants.ts new file mode 100644 index 00000000..1663c182 --- /dev/null +++ b/src/front/Config/AppConstants.ts @@ -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; diff --git a/src/front/Config/VariablesFront.ts b/src/front/Config/VariablesFront.ts index 36e62658..8c5e7b49 100644 --- a/src/front/Config/VariablesFront.ts +++ b/src/front/Config/VariablesFront.ts @@ -5,6 +5,8 @@ export class FrontendVariables { public BACK_API_HOST!: string; + public BACK_API_PORT!: string; + public BACK_API_ROOT_URL!: string; public BACK_API_VERSION!: string; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index c07fcb6d..dc73436a 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -24,6 +24,7 @@ type AppPropsWithLayout = AppProps & { } & { backApiProtocol: string; backApiHost: string; + backApiPort: string; backApiRootUrl: string; backApiVersion: string; frontAppHost: string; @@ -46,6 +47,7 @@ const MyApp = (({ pageProps, backApiProtocol, backApiHost, + backApiPort, backApiRootUrl, backApiVersion, frontAppHost, @@ -65,6 +67,7 @@ const MyApp = (({ const instance = FrontendVariables.getInstance(); instance.BACK_API_PROTOCOL = backApiProtocol; instance.BACK_API_HOST = backApiHost; + instance.BACK_API_PORT = backApiPort; instance.BACK_API_ROOT_URL = backApiRootUrl; instance.BACK_API_VERSION = backApiVersion; instance.FRONT_APP_HOST = frontAppHost; @@ -129,6 +132,7 @@ MyApp.getInitialProps = async () => { return { backApiProtocol: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_PROTOCOL, backApiHost: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_HOST, + backApiPort: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_PORT, backApiRootUrl: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_ROOT_URL, backApiVersion: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_VERSION, frontAppHost: publicRuntimeConfig.NEXT_PUBLIC_FRONT_APP_HOST, diff --git a/src/sdk/AuthModal.tsx b/src/sdk/AuthModal.tsx index d25ff19b..34009241 100644 --- a/src/sdk/AuthModal.tsx +++ b/src/sdk/AuthModal.tsx @@ -70,6 +70,20 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { iframeRef.current.contentWindow!.postMessage({ type: 'GET_PAIRING_ID', accessToken: message.accessToken, messageId }, targetOrigin); 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': { User.getInstance().setPairingId(message.userPairingId); @@ -83,6 +97,36 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { }, 500); 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); diff --git a/src/sdk/MessageBus.ts b/src/sdk/MessageBus.ts index cb0297c3..5ae9ef9b 100644 --- a/src/sdk/MessageBus.ts +++ b/src/sdk/MessageBus.ts @@ -11,8 +11,8 @@ import { FileBlob } from '../front/Api/Entities/types'; export default class MessageBus { private static instance: MessageBus; - private errors: { [key: string]: string } = {}; private isListening: boolean = false; + private messagesSent: Set = new Set(); public static getInstance(): MessageBus { if (!MessageBus.instance) { @@ -27,6 +27,11 @@ export default class MessageBus { public destroyMessageListener(): void { 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 { @@ -41,27 +46,39 @@ export default class MessageBus { }); } + public isWaitingForMessage(): boolean { + return this.messagesSent.size > 0; + } + public requestLink(): Promise { return new Promise((resolve: () => void, reject: (error: string) => void) => { 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 }) => { + console.log('[MessageBus] LINK_ACCEPTED received with responseId:', responseId, 'expected:', messageId); if (responseId !== messageId) { return; } + console.log('[MessageBus] LINK_ACCEPTED matched - resolving'); unsubscribe(); + unsubscribeError(); User.getInstance().setTokens(message.accessToken, message.refreshToken); resolve(); }); 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) { return; } + console.log('[MessageBus] ERROR_LINK_ACCEPTED matched - rejecting with error:', error); + unsubscribe(); unsubscribeError(); reject(error); }); + console.log('[MessageBus] requestLink - sending REQUEST_LINK message'); this.sendMessage({ type: 'REQUEST_LINK', messageId @@ -69,6 +86,40 @@ export default class MessageBus { }); } + public createPairing(): Promise { + return new Promise((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 { return new Promise((resolve: (pairingId: string) => void, reject: (error: string) => void) => { this.checkToken().then(() => { @@ -98,7 +149,7 @@ export default class MessageBus { accessToken, messageId }); - }).catch(console.error); + }).catch(reject); }); } @@ -298,6 +349,114 @@ export default class MessageBus { }); } + public getRolesForProcess(processId: string): Promise { + return new Promise((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 { + return new Promise((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 { return new Promise((resolve: (processesDecoded: any[]) => void, reject: (error: string) => void) => { this.getProcesses().then(async (processes: any) => { @@ -417,7 +576,7 @@ export default class MessageBus { accessToken, messageId }); - }).catch(console.error); + }).catch(reject); }); } @@ -454,12 +613,48 @@ export default class MessageBus { accessToken, messageId }); - }).catch(console.error); + }).catch(reject); }); } - public getProcesses(): Promise { - return new Promise((resolve: (processes: any) => void, reject: (error: string) => void) => { + // Returns all processes, including processes that have nothing to do with us + 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(() => { const messageId = `GET_PROCESSES_${uuidv4()}`; @@ -471,7 +666,7 @@ export default class MessageBus { // Filter processes by my processes setTimeout(() => { - this.getMyProcesses().then((myProcesses: any) => { + this.getMyProcesses().then((myProcesses: string[]) => { const processesFiltered: { [processId: string]: any } = {}; for (const processId of myProcesses) { const process = processes[processId]; @@ -500,16 +695,18 @@ export default class MessageBus { accessToken, messageId }); - }).catch(console.error); + }).catch(reject); }); } - public getMyProcesses(): Promise { - return new Promise((resolve: (processes: any) => void, reject: (error: string) => void) => { + // Returns the processes id of processes we are involved in + // It's meant to be used to filter processes in the getProcesses() method + public getMyProcesses(): Promise { + return new Promise((resolve: (processes: string[]) => void, reject: (error: string) => void) => { this.checkToken().then(() => { const messageId = `GET_MY_PROCESSES_${uuidv4()}`; - const unsubscribe = EventBus.getInstance().on('GET_MY_PROCESSES', (responseId: string, processes: any) => { + const unsubscribe = EventBus.getInstance().on('GET_MY_PROCESSES', (responseId: string, processes: string[]) => { if (responseId !== messageId) { return; } @@ -533,7 +730,7 @@ export default class MessageBus { accessToken, messageId }); - }).catch(console.error); + }).catch(reject); }); } @@ -568,7 +765,7 @@ export default class MessageBus { accessToken, messageId }); - }).catch(console.error); + }).catch(reject); }); } @@ -603,7 +800,7 @@ export default class MessageBus { accessToken, messageId }); - }).catch(console.error); + }).catch(reject); }); } @@ -638,7 +835,7 @@ export default class MessageBus { accessToken, messageId }); - }).catch(console.error); + }).catch(reject); }); } @@ -672,7 +869,7 @@ export default class MessageBus { accessToken, messageId }); - }).catch(console.error); + }).catch(reject); }); } @@ -874,6 +1071,7 @@ export default class MessageBus { const targetOrigin = IframeReference.getTargetOrigin(); const iframe = IframeReference.getIframe(); iframe.contentWindow?.postMessage(message, targetOrigin); + this.messagesSent.add(message.messageId); } catch (error) { console.error('[MessageBus] sendMessage: error', error); } @@ -975,19 +1173,26 @@ export default class MessageBus { case 'ERROR': 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; } } - private doHandleMessage(messageId: string, messageType: string, message: any, callback: (message: any) => any) { - if (this.errors[messageId]) { - const error = this.errors[messageId]; - delete this.errors[messageId]; - EventBus.getInstance().emit(`ERROR_${messageType}`, messageId, error); - return; + private extractOperationTypeFromMessageId(messageId: string): string { + // Split on last underscore to extract operation type + // e.g., "GET_PAIRING_ID_abc123" -> "GET_PAIRING_ID" + const lastUnderscoreIndex = messageId.lastIndexOf('_'); + if (lastUnderscoreIndex === -1) { + 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(messageType, messageId, callback(message)); + this.messagesSent.delete(messageId); } }