@rsuci/shared-form-components 1.0.86 → 1.0.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/form-renderer/FormRenderer.d.ts.map +1 -1
- package/dist/components/form-renderer/FormRenderer.js +12 -6
- package/dist/components/form-renderer/GroupeInstanceTabs.d.ts +2 -2
- package/dist/components/form-renderer/GroupeInstanceTabs.d.ts.map +1 -1
- package/dist/components/form-renderer/GroupeInstanceTabs.js +4 -22
- package/dist/hooks/useFormInstances.js +8 -8
- package/dist/hooks/useFormRenderer.d.ts.map +1 -1
- package/dist/hooks/useFormRenderer.js +17 -0
- package/dist/lib/__tests__/groupeInstanceManager.test.d.ts +6 -0
- package/dist/lib/__tests__/groupeInstanceManager.test.d.ts.map +1 -0
- package/dist/lib/__tests__/groupeInstanceManager.test.js +454 -0
- package/dist/lib/utils/groupeInstanceManager.d.ts +15 -4
- package/dist/lib/utils/groupeInstanceManager.d.ts.map +1 -1
- package/dist/lib/utils/groupeInstanceManager.js +102 -207
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/form-renderer/FormRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAA4D,MAAM,OAAO,CAAC;AACjF,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EAIf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAsBlF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,yBAAyB;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAClD,oCAAoC;IACpC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,iCAAiC;IACjC,SAAS,EAAE,qBAAqB,CAAC;IACjC,wBAAwB;IACxB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,wCAAwC;IACxC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iCAAiC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"FormRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/form-renderer/FormRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAA4D,MAAM,OAAO,CAAC;AACjF,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EAIf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAsBlF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,yBAAyB;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAClD,oCAAoC;IACpC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,iCAAiC;IACjC,SAAS,EAAE,qBAAqB,CAAC;IACjC,wBAAwB;IACxB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,wCAAwC;IACxC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iCAAiC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAwyBD;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgBpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -206,16 +206,22 @@ const FormRendererInner = () => {
|
|
|
206
206
|
navigation.changeInstance(instanceIndex);
|
|
207
207
|
};
|
|
208
208
|
// Handler pour l'ajout d'instance
|
|
209
|
-
const handleInstanceAdded = (instance) => {
|
|
210
|
-
|
|
209
|
+
const handleInstanceAdded = (instance, updatedGroupe) => {
|
|
210
|
+
// Mettre à jour le groupe via l'état React (pas de mutation directe)
|
|
211
|
+
instances.setGroupesWithInstances(prev => prev.map(g => g.code === updatedGroupe.code ? updatedGroupe : g));
|
|
212
|
+
navigation.updateTotalInstances(updatedGroupe.instances?.length || 1);
|
|
211
213
|
// Naviguer vers la nouvelle instance
|
|
212
|
-
if (
|
|
213
|
-
navigation.changeInstance(
|
|
214
|
+
if (updatedGroupe.instances) {
|
|
215
|
+
navigation.changeInstance(updatedGroupe.instances.length - 1);
|
|
214
216
|
}
|
|
215
217
|
};
|
|
216
218
|
// Handler pour la suppression d'instance
|
|
217
|
-
const handleInstanceRemoved = (instanceNumber) => {
|
|
218
|
-
|
|
219
|
+
const handleInstanceRemoved = (instanceNumber, updatedGroupe, updatedResponses) => {
|
|
220
|
+
// CRITIQUE : mettre à jour les réponses via l'état React
|
|
221
|
+
context.setResponses(updatedResponses);
|
|
222
|
+
// Mettre à jour le groupe via l'état React
|
|
223
|
+
instances.setGroupesWithInstances(prev => prev.map(g => g.code === updatedGroupe.code ? updatedGroupe : g));
|
|
224
|
+
navigation.updateTotalInstances(updatedGroupe.instances?.length || 1);
|
|
219
225
|
// Revenir à la première instance
|
|
220
226
|
navigation.changeInstance(0);
|
|
221
227
|
};
|
|
@@ -9,8 +9,8 @@ export interface GroupeInstanceTabsProps {
|
|
|
9
9
|
currentInstanceIndex: number;
|
|
10
10
|
responses: Record<string, EnqueteReponse>;
|
|
11
11
|
onInstanceChange: (instanceIndex: number) => void;
|
|
12
|
-
onInstanceAdded: (instance: GroupeInstance) => void;
|
|
13
|
-
onInstanceRemoved: (instanceNumber: number) => void;
|
|
12
|
+
onInstanceAdded: (instance: GroupeInstance, updatedGroupe: GroupeFormulaire) => void;
|
|
13
|
+
onInstanceRemoved: (instanceNumber: number, updatedGroupe: GroupeFormulaire, updatedResponses: Record<string, EnqueteReponse>) => void;
|
|
14
14
|
disabled?: boolean;
|
|
15
15
|
}
|
|
16
16
|
declare const GroupeInstanceTabs: React.FC<GroupeInstanceTabsProps>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupeInstanceTabs.d.ts","sourceRoot":"","sources":["../../../src/components/form-renderer/GroupeInstanceTabs.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAIvF,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,gBAAgB,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,gBAAgB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,eAAe,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"GroupeInstanceTabs.d.ts","sourceRoot":"","sources":["../../../src/components/form-renderer/GroupeInstanceTabs.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAIvF,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,gBAAgB,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,gBAAgB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,eAAe,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACrF,iBAAiB,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,IAAI,CAAC;IACvI,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAuQzD,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -26,12 +26,6 @@ const GroupeInstanceTabs = ({ groupe, currentInstanceIndex, responses, onInstanc
|
|
|
26
26
|
useEffect(() => {
|
|
27
27
|
const newMaxInstances = GroupeInstanceManager.getMaxInstances(groupe, responses);
|
|
28
28
|
if (newMaxInstances !== maxInstances) {
|
|
29
|
-
console.log('GroupeInstanceTabs - Changement de maxInstances détecté:', {
|
|
30
|
-
groupeCode: groupe.code,
|
|
31
|
-
oldMaxInstances: maxInstances,
|
|
32
|
-
newMaxInstances,
|
|
33
|
-
codeVariable: groupe.codeVariable
|
|
34
|
-
});
|
|
35
29
|
setForceUpdate(prev => prev + 1);
|
|
36
30
|
}
|
|
37
31
|
}, [responses, groupe.codeVariable, groupe.maxInstances]);
|
|
@@ -39,18 +33,6 @@ const GroupeInstanceTabs = ({ groupe, currentInstanceIndex, responses, onInstanc
|
|
|
39
33
|
// Le nombre d'onglets affichés = Math.max(maxInstances, instances existantes)
|
|
40
34
|
const minDisplayCount = Math.max(maxInstances, groupe.instances.length);
|
|
41
35
|
const displayedInstances = groupe.instances.slice(0, minDisplayCount);
|
|
42
|
-
console.log('GroupeInstanceTabs - Debug avec protection:', {
|
|
43
|
-
groupeCode: groupe.code,
|
|
44
|
-
codeVariable: groupe.codeVariable,
|
|
45
|
-
maxInstances,
|
|
46
|
-
totalInstances: groupe.instances.length,
|
|
47
|
-
minDisplayCount,
|
|
48
|
-
displayedInstances: displayedInstances.length,
|
|
49
|
-
canAdd: canAdd.canProceed,
|
|
50
|
-
canRemove: canRemove.canProceed,
|
|
51
|
-
canAddMessage: canAdd.constraints[0]?.message,
|
|
52
|
-
canRemoveMessage: canRemove.constraints[0]?.message
|
|
53
|
-
});
|
|
54
36
|
// Fonction pour déterminer l'état d'une instance
|
|
55
37
|
const getInstanceState = (instance) => {
|
|
56
38
|
const responseCount = Object.keys(instance.reponses).length;
|
|
@@ -109,8 +91,8 @@ const GroupeInstanceTabs = ({ groupe, currentInstanceIndex, responses, onInstanc
|
|
|
109
91
|
setError(null);
|
|
110
92
|
try {
|
|
111
93
|
const result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
112
|
-
if (result.success && result.instance) {
|
|
113
|
-
onInstanceAdded(result.instance);
|
|
94
|
+
if (result.success && result.instance && result.updatedGroupe) {
|
|
95
|
+
onInstanceAdded(result.instance, result.updatedGroupe);
|
|
114
96
|
}
|
|
115
97
|
else {
|
|
116
98
|
setError(result.error || 'Erreur lors de l\'ajout de l\'instance');
|
|
@@ -143,8 +125,8 @@ const GroupeInstanceTabs = ({ groupe, currentInstanceIndex, responses, onInstanc
|
|
|
143
125
|
return;
|
|
144
126
|
}
|
|
145
127
|
const result = GroupeInstanceManager.removeInstance(groupe, currentInstance.numeroInstance, responses);
|
|
146
|
-
if (result.success) {
|
|
147
|
-
onInstanceRemoved(currentInstance.numeroInstance);
|
|
128
|
+
if (result.success && result.updatedGroupe && result.updatedResponses) {
|
|
129
|
+
onInstanceRemoved(currentInstance.numeroInstance, result.updatedGroupe, result.updatedResponses);
|
|
148
130
|
}
|
|
149
131
|
else {
|
|
150
132
|
setError(result.error || 'Erreur lors de la suppression de l\'instance');
|
|
@@ -81,16 +81,16 @@ export function useFormInstances(initialGroupes, initialResponses, options = {})
|
|
|
81
81
|
*/
|
|
82
82
|
const addInstance = useCallback((groupe, responses) => {
|
|
83
83
|
const result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
84
|
-
if (result.success && result.instance) {
|
|
84
|
+
if (result.success && result.instance && result.updatedGroupe) {
|
|
85
85
|
log('Instance ajoutée:', {
|
|
86
86
|
groupeCode: groupe.code,
|
|
87
87
|
instanceNumber: result.instance.numeroInstance
|
|
88
88
|
});
|
|
89
|
-
// Mettre à jour les groupes
|
|
89
|
+
// Mettre à jour les groupes avec le nouveau groupe immutable
|
|
90
90
|
setGroupesWithInstances(prev => {
|
|
91
91
|
const updated = prev.map(g => {
|
|
92
|
-
if (g.code ===
|
|
93
|
-
return
|
|
92
|
+
if (g.code === result.updatedGroupe.code) {
|
|
93
|
+
return result.updatedGroupe;
|
|
94
94
|
}
|
|
95
95
|
return g;
|
|
96
96
|
});
|
|
@@ -110,16 +110,16 @@ export function useFormInstances(initialGroupes, initialResponses, options = {})
|
|
|
110
110
|
*/
|
|
111
111
|
const removeInstance = useCallback((groupe, instanceNumber, responses) => {
|
|
112
112
|
const result = GroupeInstanceManager.removeInstance(groupe, instanceNumber, responses);
|
|
113
|
-
if (result.success) {
|
|
113
|
+
if (result.success && result.updatedGroupe) {
|
|
114
114
|
log('Instance supprimée:', {
|
|
115
115
|
groupeCode: groupe.code,
|
|
116
116
|
instanceNumber
|
|
117
117
|
});
|
|
118
|
-
// Mettre à jour les groupes
|
|
118
|
+
// Mettre à jour les groupes avec le nouveau groupe immutable
|
|
119
119
|
setGroupesWithInstances(prev => {
|
|
120
120
|
const updated = prev.map(g => {
|
|
121
|
-
if (g.code ===
|
|
122
|
-
return
|
|
121
|
+
if (g.code === result.updatedGroupe.code) {
|
|
122
|
+
return result.updatedGroupe;
|
|
123
123
|
}
|
|
124
124
|
return g;
|
|
125
125
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFormRenderer.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormRenderer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAqB,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAqB,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAoB,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"useFormRenderer.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormRenderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ import { useState, useCallback, useEffect, useMemo } from 'react';
|
|
|
7
7
|
import { useFormNavigation } from './useFormNavigation';
|
|
8
8
|
import { useFormValidation } from './useFormValidation';
|
|
9
9
|
import { useFormInstances } from './useFormInstances';
|
|
10
|
+
import { GroupeInstanceManager } from '../lib/utils/groupeInstanceManager';
|
|
10
11
|
/**
|
|
11
12
|
* Hook principal pour gérer un formulaire d'enquête
|
|
12
13
|
*
|
|
@@ -94,6 +95,20 @@ export function useFormRenderer(formulaire, enquete, options = { config: { mode:
|
|
|
94
95
|
// === Mise à jour des réponses ===
|
|
95
96
|
const updateResponse = useCallback((variableCode, value, variable, numeroMembre) => {
|
|
96
97
|
const responseKey = getResponseKey(variableCode, numeroMembre);
|
|
98
|
+
// Validation de la variable de contrôle : empêcher la réduction en-dessous des instances existantes
|
|
99
|
+
if (isControlVariable(variable) && value !== null && value !== '') {
|
|
100
|
+
const numericValue = typeof value === 'number' ? value : parseInt(String(value), 10);
|
|
101
|
+
if (!isNaN(numericValue)) {
|
|
102
|
+
const groupesMultiples = (instances.groupesWithInstances.length > 0
|
|
103
|
+
? instances.groupesWithInstances
|
|
104
|
+
: formulaire?.groupes || []).filter(g => g.estMultiple);
|
|
105
|
+
const validation = GroupeInstanceManager.canModifyControlVariable(variableCode, numericValue, groupesMultiples, responses);
|
|
106
|
+
if (!validation.canProceed) {
|
|
107
|
+
console.warn('[FormRenderer] Variable de contrôle rejetée:', validation.constraints[0]?.message);
|
|
108
|
+
return; // Rejeter le changement - l'input revient à la valeur précédente
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
97
112
|
const newResponse = {
|
|
98
113
|
variableId: variable.id,
|
|
99
114
|
variableCode: variableCode,
|
|
@@ -129,9 +144,11 @@ export function useFormRenderer(formulaire, enquete, options = { config: { mode:
|
|
|
129
144
|
setHasUnsavedChanges(true);
|
|
130
145
|
}, [
|
|
131
146
|
getResponseKey,
|
|
147
|
+
isControlVariable,
|
|
132
148
|
currentGroup,
|
|
133
149
|
currentInstance,
|
|
134
150
|
instances,
|
|
151
|
+
formulaire?.groupes,
|
|
135
152
|
responses,
|
|
136
153
|
onResponsesChange
|
|
137
154
|
]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groupeInstanceManager.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/groupeInstanceManager.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests unitaires pour GroupeInstanceManager
|
|
3
|
+
* Couvre le comptage, l'ajout/suppression, la validation et les scénarios d'intégration
|
|
4
|
+
*/
|
|
5
|
+
import { GroupeInstanceManager } from '../utils/groupeInstanceManager';
|
|
6
|
+
// === Helpers de création de données de test ===
|
|
7
|
+
function createVariable(code, overrides = {}) {
|
|
8
|
+
return {
|
|
9
|
+
id: Math.floor(Math.random() * 10000),
|
|
10
|
+
code,
|
|
11
|
+
designation: `Variable ${code}`,
|
|
12
|
+
typeCode: 'STRING',
|
|
13
|
+
groupeCode: 'TEST_GROUP',
|
|
14
|
+
ordre: 1,
|
|
15
|
+
estObligatoire: false,
|
|
16
|
+
estVisible: true,
|
|
17
|
+
...overrides
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function createGroupe(overrides = {}) {
|
|
21
|
+
return {
|
|
22
|
+
code: 'TEST_GROUP',
|
|
23
|
+
designation: 'Groupe Test',
|
|
24
|
+
ordre: 1,
|
|
25
|
+
estMultiple: false,
|
|
26
|
+
variables: [createVariable('S1_01'), createVariable('S1_02')],
|
|
27
|
+
...overrides
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function createMultipleGroupe(overrides = {}) {
|
|
31
|
+
return createGroupe({
|
|
32
|
+
estMultiple: true,
|
|
33
|
+
codeVariable: 'S0_23',
|
|
34
|
+
...overrides
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function createInstance(numero, reponses = {}) {
|
|
38
|
+
return {
|
|
39
|
+
numeroInstance: numero,
|
|
40
|
+
libelle: `Groupe Test ${numero}`,
|
|
41
|
+
estComplete: false,
|
|
42
|
+
reponses
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function createResponse(variableCode, valeur, numeroMembre) {
|
|
46
|
+
return {
|
|
47
|
+
variableId: Math.floor(Math.random() * 10000),
|
|
48
|
+
variableCode,
|
|
49
|
+
valeur,
|
|
50
|
+
numeroMembre,
|
|
51
|
+
dateModification: new Date()
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function createControlResponse(value) {
|
|
55
|
+
return {
|
|
56
|
+
S0_23: createResponse('S0_23', value)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// === Tests ===
|
|
60
|
+
describe('GroupeInstanceManager', () => {
|
|
61
|
+
describe('getMaxInstances', () => {
|
|
62
|
+
it('devrait retourner 1 pour un groupe non multiple', () => {
|
|
63
|
+
const groupe = createGroupe({ estMultiple: false });
|
|
64
|
+
expect(GroupeInstanceManager.getMaxInstances(groupe, {})).toBe(1);
|
|
65
|
+
});
|
|
66
|
+
it('devrait retourner 1 pour un groupe multiple sans codeVariable', () => {
|
|
67
|
+
const groupe = createGroupe({ estMultiple: true, codeVariable: undefined });
|
|
68
|
+
expect(GroupeInstanceManager.getMaxInstances(groupe, {})).toBe(1);
|
|
69
|
+
});
|
|
70
|
+
it('devrait retourner la valeur numerique de la variable de controle', () => {
|
|
71
|
+
const groupe = createMultipleGroupe();
|
|
72
|
+
const responses = createControlResponse(3);
|
|
73
|
+
expect(GroupeInstanceManager.getMaxInstances(groupe, responses)).toBe(3);
|
|
74
|
+
});
|
|
75
|
+
it('devrait parser une valeur string de la variable de controle', () => {
|
|
76
|
+
const groupe = createMultipleGroupe();
|
|
77
|
+
const responses = {
|
|
78
|
+
S0_23: createResponse('S0_23', '5')
|
|
79
|
+
};
|
|
80
|
+
expect(GroupeInstanceManager.getMaxInstances(groupe, responses)).toBe(5);
|
|
81
|
+
});
|
|
82
|
+
it('devrait retourner 1 si aucune reponse pour la variable de controle', () => {
|
|
83
|
+
const groupe = createMultipleGroupe();
|
|
84
|
+
expect(GroupeInstanceManager.getMaxInstances(groupe, {})).toBe(1);
|
|
85
|
+
});
|
|
86
|
+
it('devrait trouver la reponse par variableCode quand la cle directe manque', () => {
|
|
87
|
+
const groupe = createMultipleGroupe();
|
|
88
|
+
const responses = {
|
|
89
|
+
'some_other_key': createResponse('S0_23', 4)
|
|
90
|
+
};
|
|
91
|
+
expect(GroupeInstanceManager.getMaxInstances(groupe, responses)).toBe(4);
|
|
92
|
+
});
|
|
93
|
+
it('devrait retourner strictement la valeur de controle (pas gonflee par instances existantes)', () => {
|
|
94
|
+
// Apres Fix 1c: getMaxInstances ne doit PAS gonfler au-dela de la variable de controle
|
|
95
|
+
const groupe = createMultipleGroupe({
|
|
96
|
+
instances: [createInstance(1), createInstance(2), createInstance(3), createInstance(4), createInstance(5)]
|
|
97
|
+
});
|
|
98
|
+
const responses = createControlResponse(3);
|
|
99
|
+
// Le max devrait etre 3 (valeur de controle), pas 5 (nombre d'instances)
|
|
100
|
+
expect(GroupeInstanceManager.getMaxInstances(groupe, responses)).toBe(3);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe('getInstancesCount', () => {
|
|
104
|
+
it('devrait retourner 1 pour un groupe non multiple', () => {
|
|
105
|
+
const groupe = createGroupe({ estMultiple: false });
|
|
106
|
+
expect(GroupeInstanceManager.getInstancesCount(groupe, {})).toBe(1);
|
|
107
|
+
});
|
|
108
|
+
it('devrait retourner le nombre d\'instances si elles existent', () => {
|
|
109
|
+
const groupe = createMultipleGroupe({
|
|
110
|
+
instances: [createInstance(1), createInstance(2), createInstance(3)]
|
|
111
|
+
});
|
|
112
|
+
expect(GroupeInstanceManager.getInstancesCount(groupe, {})).toBe(3);
|
|
113
|
+
});
|
|
114
|
+
it('devrait utiliser le fallback par numeroMembre quand instances est vide', () => {
|
|
115
|
+
const groupe = createMultipleGroupe({ instances: undefined });
|
|
116
|
+
const responses = {
|
|
117
|
+
'S1_01_1': createResponse('S1_01', 'val1', 1),
|
|
118
|
+
'S1_01_2': createResponse('S1_01', 'val2', 2),
|
|
119
|
+
'S1_02_1': createResponse('S1_02', 'val3', 1),
|
|
120
|
+
'S1_02_2': createResponse('S1_02', 'val4', 2),
|
|
121
|
+
};
|
|
122
|
+
expect(GroupeInstanceManager.getInstancesCount(groupe, responses)).toBe(2);
|
|
123
|
+
});
|
|
124
|
+
it('devrait retourner au minimum 1', () => {
|
|
125
|
+
const groupe = createMultipleGroupe({ instances: undefined });
|
|
126
|
+
expect(GroupeInstanceManager.getInstancesCount(groupe, {})).toBe(1);
|
|
127
|
+
});
|
|
128
|
+
it('ne devrait PAS fausser le comptage avec des codes variables contenant des chiffres', () => {
|
|
129
|
+
// Apres Fix 1a: le parsing de cles "S1_01" ne doit pas creer un faux instance numero 1
|
|
130
|
+
const groupe = createMultipleGroupe({
|
|
131
|
+
instances: [createInstance(1), createInstance(2)]
|
|
132
|
+
});
|
|
133
|
+
// Meme s'il y a des cles avec des formats ambigus, instances.length prime
|
|
134
|
+
expect(GroupeInstanceManager.getInstancesCount(groupe, {})).toBe(2);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
describe('createInstance', () => {
|
|
138
|
+
it('devrait creer une instance avec le bon numero et libelle', () => {
|
|
139
|
+
const groupe = createMultipleGroupe();
|
|
140
|
+
const instance = GroupeInstanceManager.createInstance(groupe, 3);
|
|
141
|
+
expect(instance.numeroInstance).toBe(3);
|
|
142
|
+
expect(instance.libelle).toBe('Groupe Test 3');
|
|
143
|
+
expect(instance.estComplete).toBe(false);
|
|
144
|
+
expect(instance.reponses).toEqual({});
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe('initializeInstances', () => {
|
|
148
|
+
it('devrait creer au moins 1 instance si aucune reponse', () => {
|
|
149
|
+
const groupe = createMultipleGroupe();
|
|
150
|
+
const responses = createControlResponse(3);
|
|
151
|
+
const instances = GroupeInstanceManager.initializeInstances(groupe, responses);
|
|
152
|
+
expect(instances.length).toBeGreaterThanOrEqual(1);
|
|
153
|
+
});
|
|
154
|
+
it('devrait creer des instances basees sur les reponses existantes avec numeroMembre', () => {
|
|
155
|
+
const groupe = createMultipleGroupe();
|
|
156
|
+
const responses = {
|
|
157
|
+
...createControlResponse(3),
|
|
158
|
+
'S1_01_1': createResponse('S1_01', 'Alice', 1),
|
|
159
|
+
'S1_02_1': createResponse('S1_02', '25', 1),
|
|
160
|
+
'S1_01_2': createResponse('S1_01', 'Bob', 2),
|
|
161
|
+
'S1_02_2': createResponse('S1_02', '30', 2),
|
|
162
|
+
};
|
|
163
|
+
const instances = GroupeInstanceManager.initializeInstances(groupe, responses);
|
|
164
|
+
expect(instances.length).toBe(2);
|
|
165
|
+
});
|
|
166
|
+
it('ne devrait PAS muter l\'objet responses original', () => {
|
|
167
|
+
const groupe = createMultipleGroupe();
|
|
168
|
+
const responses = {
|
|
169
|
+
...createControlResponse(2),
|
|
170
|
+
'S1_01_1': createResponse('S1_01', 'Alice', 1),
|
|
171
|
+
};
|
|
172
|
+
const originalKeys = Object.keys(responses).sort();
|
|
173
|
+
GroupeInstanceManager.initializeInstances(groupe, responses);
|
|
174
|
+
expect(Object.keys(responses).sort()).toEqual(originalKeys);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe('canAddInstance', () => {
|
|
178
|
+
it('devrait retourner false pour un groupe non multiple', () => {
|
|
179
|
+
const groupe = createGroupe({ estMultiple: false });
|
|
180
|
+
const result = GroupeInstanceManager.canAddInstance(groupe, {});
|
|
181
|
+
expect(result.canProceed).toBe(false);
|
|
182
|
+
});
|
|
183
|
+
it('devrait retourner true si currentCount < maxInstances', () => {
|
|
184
|
+
const groupe = createMultipleGroupe({
|
|
185
|
+
instances: [createInstance(1)]
|
|
186
|
+
});
|
|
187
|
+
const responses = createControlResponse(3);
|
|
188
|
+
const result = GroupeInstanceManager.canAddInstance(groupe, responses);
|
|
189
|
+
expect(result.canProceed).toBe(true);
|
|
190
|
+
});
|
|
191
|
+
it('devrait retourner false si currentCount >= maxInstances', () => {
|
|
192
|
+
const groupe = createMultipleGroupe({
|
|
193
|
+
instances: [createInstance(1), createInstance(2), createInstance(3)]
|
|
194
|
+
});
|
|
195
|
+
const responses = createControlResponse(3);
|
|
196
|
+
const result = GroupeInstanceManager.canAddInstance(groupe, responses);
|
|
197
|
+
expect(result.canProceed).toBe(false);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
describe('canRemoveInstance', () => {
|
|
201
|
+
it('devrait retourner true si plus de 1 instance', () => {
|
|
202
|
+
const groupe = createMultipleGroupe({
|
|
203
|
+
instances: [createInstance(1), createInstance(2)]
|
|
204
|
+
});
|
|
205
|
+
const result = GroupeInstanceManager.canRemoveInstance(groupe, {});
|
|
206
|
+
expect(result.canProceed).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
it('devrait retourner false si 1 seule instance', () => {
|
|
209
|
+
const groupe = createMultipleGroupe({
|
|
210
|
+
instances: [createInstance(1)]
|
|
211
|
+
});
|
|
212
|
+
const result = GroupeInstanceManager.canRemoveInstance(groupe, {});
|
|
213
|
+
expect(result.canProceed).toBe(false);
|
|
214
|
+
});
|
|
215
|
+
it('devrait retourner false si aucune instance', () => {
|
|
216
|
+
const groupe = createMultipleGroupe({
|
|
217
|
+
instances: []
|
|
218
|
+
});
|
|
219
|
+
const result = GroupeInstanceManager.canRemoveInstance(groupe, {});
|
|
220
|
+
expect(result.canProceed).toBe(false);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
describe('addInstance', () => {
|
|
224
|
+
it('devrait ajouter une instance et retourner success=true', () => {
|
|
225
|
+
const groupe = createMultipleGroupe({
|
|
226
|
+
instances: [createInstance(1)]
|
|
227
|
+
});
|
|
228
|
+
const responses = createControlResponse(3);
|
|
229
|
+
const result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
230
|
+
expect(result.success).toBe(true);
|
|
231
|
+
expect(result.instance).toBeDefined();
|
|
232
|
+
expect(result.instance.numeroInstance).toBe(2);
|
|
233
|
+
});
|
|
234
|
+
it('ne devrait PAS muter le groupe original (immutabilite)', () => {
|
|
235
|
+
const groupe = createMultipleGroupe({
|
|
236
|
+
instances: [createInstance(1)]
|
|
237
|
+
});
|
|
238
|
+
const originalLength = groupe.instances.length;
|
|
239
|
+
const responses = createControlResponse(3);
|
|
240
|
+
const result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
241
|
+
expect(result.success).toBe(true);
|
|
242
|
+
// Apres Fix 2a: le groupe original ne doit PAS avoir ete mute
|
|
243
|
+
expect(groupe.instances.length).toBe(originalLength);
|
|
244
|
+
// Le nouveau groupe doit etre retourne dans updatedGroupe
|
|
245
|
+
expect(result.updatedGroupe).toBeDefined();
|
|
246
|
+
expect(result.updatedGroupe.instances.length).toBe(originalLength + 1);
|
|
247
|
+
});
|
|
248
|
+
it('devrait refuser si deja au maximum', () => {
|
|
249
|
+
const groupe = createMultipleGroupe({
|
|
250
|
+
instances: [createInstance(1), createInstance(2)]
|
|
251
|
+
});
|
|
252
|
+
const responses = createControlResponse(2);
|
|
253
|
+
const result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
254
|
+
expect(result.success).toBe(false);
|
|
255
|
+
expect(result.error).toBeDefined();
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
describe('removeInstance', () => {
|
|
259
|
+
it('devrait supprimer une instance et retourner success=true', () => {
|
|
260
|
+
const groupe = createMultipleGroupe({
|
|
261
|
+
instances: [createInstance(1), createInstance(2)]
|
|
262
|
+
});
|
|
263
|
+
const responses = {
|
|
264
|
+
...createControlResponse(2),
|
|
265
|
+
'S1_01_1': createResponse('S1_01', 'Alice', 1),
|
|
266
|
+
'S1_01_2': createResponse('S1_01', 'Bob', 2),
|
|
267
|
+
};
|
|
268
|
+
const result = GroupeInstanceManager.removeInstance(groupe, 1, responses);
|
|
269
|
+
expect(result.success).toBe(true);
|
|
270
|
+
});
|
|
271
|
+
it('ne devrait PAS muter le groupe original ni les reponses (immutabilite)', () => {
|
|
272
|
+
const groupe = createMultipleGroupe({
|
|
273
|
+
instances: [createInstance(1), createInstance(2)]
|
|
274
|
+
});
|
|
275
|
+
const responses = {
|
|
276
|
+
...createControlResponse(2),
|
|
277
|
+
'S1_01_1': createResponse('S1_01', 'Alice', 1),
|
|
278
|
+
'S1_01_2': createResponse('S1_01', 'Bob', 2),
|
|
279
|
+
};
|
|
280
|
+
const originalGroupeInstancesLength = groupe.instances.length;
|
|
281
|
+
const originalResponsesKeys = Object.keys(responses).sort();
|
|
282
|
+
const result = GroupeInstanceManager.removeInstance(groupe, 1, responses);
|
|
283
|
+
expect(result.success).toBe(true);
|
|
284
|
+
// Apres Fix 2b: le groupe original ne doit PAS avoir ete mute
|
|
285
|
+
expect(groupe.instances.length).toBe(originalGroupeInstancesLength);
|
|
286
|
+
// Les reponses originales ne doivent PAS avoir ete mutees
|
|
287
|
+
expect(Object.keys(responses).sort()).toEqual(originalResponsesKeys);
|
|
288
|
+
// Les nouveaux objets doivent etre retournes
|
|
289
|
+
expect(result.updatedGroupe).toBeDefined();
|
|
290
|
+
expect(result.updatedResponses).toBeDefined();
|
|
291
|
+
});
|
|
292
|
+
it('devrait refuser si 1 seule instance', () => {
|
|
293
|
+
const groupe = createMultipleGroupe({
|
|
294
|
+
instances: [createInstance(1)]
|
|
295
|
+
});
|
|
296
|
+
const result = GroupeInstanceManager.removeInstance(groupe, 1, {});
|
|
297
|
+
expect(result.success).toBe(false);
|
|
298
|
+
});
|
|
299
|
+
it('devrait reordonner les instances apres suppression (2 supprime de [1,2,3] → [1,2])', () => {
|
|
300
|
+
const groupe = createMultipleGroupe({
|
|
301
|
+
instances: [createInstance(1), createInstance(2), createInstance(3)]
|
|
302
|
+
});
|
|
303
|
+
const responses = {
|
|
304
|
+
...createControlResponse(3),
|
|
305
|
+
'S1_01_1': createResponse('S1_01', 'Alice', 1),
|
|
306
|
+
'S1_01_2': createResponse('S1_01', 'Bob', 2),
|
|
307
|
+
'S1_01_3': createResponse('S1_01', 'Charlie', 3),
|
|
308
|
+
};
|
|
309
|
+
const result = GroupeInstanceManager.removeInstance(groupe, 2, responses);
|
|
310
|
+
expect(result.success).toBe(true);
|
|
311
|
+
// Apres Fix 2b+2c: verifier via updatedGroupe
|
|
312
|
+
const updatedGroupe = result.updatedGroupe;
|
|
313
|
+
if (updatedGroupe) {
|
|
314
|
+
expect(updatedGroupe.instances.length).toBe(2);
|
|
315
|
+
expect(updatedGroupe.instances[0].numeroInstance).toBe(1);
|
|
316
|
+
expect(updatedGroupe.instances[1].numeroInstance).toBe(2);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
it('devrait mettre a jour les cles de reponses apres reordonnancement', () => {
|
|
320
|
+
const groupe = createMultipleGroupe({
|
|
321
|
+
instances: [createInstance(1), createInstance(2), createInstance(3)]
|
|
322
|
+
});
|
|
323
|
+
const responses = {
|
|
324
|
+
...createControlResponse(3),
|
|
325
|
+
'S1_01_1': createResponse('S1_01', 'Alice', 1),
|
|
326
|
+
'S1_01_2': createResponse('S1_01', 'Bob', 2),
|
|
327
|
+
'S1_01_3': createResponse('S1_01', 'Charlie', 3),
|
|
328
|
+
};
|
|
329
|
+
const result = GroupeInstanceManager.removeInstance(groupe, 1, responses);
|
|
330
|
+
expect(result.success).toBe(true);
|
|
331
|
+
// Apres Fix 2b+2c: verifier via updatedResponses
|
|
332
|
+
const updatedResponses = result.updatedResponses;
|
|
333
|
+
if (updatedResponses) {
|
|
334
|
+
// Instance 2 (Bob) → instance 1, Instance 3 (Charlie) → instance 2
|
|
335
|
+
expect(updatedResponses['S1_01_1']?.valeur).toBe('Bob');
|
|
336
|
+
expect(updatedResponses['S1_01_2']?.valeur).toBe('Charlie');
|
|
337
|
+
expect(updatedResponses['S1_01_3']).toBeUndefined();
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
describe('canModifyControlVariable', () => {
|
|
342
|
+
it('devrait retourner true si newValue >= existingInstances', () => {
|
|
343
|
+
const groupe = createMultipleGroupe({
|
|
344
|
+
instances: [createInstance(1), createInstance(2)]
|
|
345
|
+
});
|
|
346
|
+
const result = GroupeInstanceManager.canModifyControlVariable('S0_23', 3, [groupe], {});
|
|
347
|
+
expect(result.canProceed).toBe(true);
|
|
348
|
+
});
|
|
349
|
+
it('devrait retourner true si newValue = existingInstances (egal)', () => {
|
|
350
|
+
const groupe = createMultipleGroupe({
|
|
351
|
+
instances: [createInstance(1), createInstance(2)]
|
|
352
|
+
});
|
|
353
|
+
const result = GroupeInstanceManager.canModifyControlVariable('S0_23', 2, [groupe], {});
|
|
354
|
+
expect(result.canProceed).toBe(true);
|
|
355
|
+
});
|
|
356
|
+
it('devrait retourner false si newValue < existingInstances', () => {
|
|
357
|
+
const groupe = createMultipleGroupe({
|
|
358
|
+
instances: [createInstance(1), createInstance(2), createInstance(3)]
|
|
359
|
+
});
|
|
360
|
+
const result = GroupeInstanceManager.canModifyControlVariable('S0_23', 2, [groupe], {});
|
|
361
|
+
expect(result.canProceed).toBe(false);
|
|
362
|
+
});
|
|
363
|
+
it('devrait retourner true si aucun groupe n\'utilise cette variable', () => {
|
|
364
|
+
const groupe = createMultipleGroupe({
|
|
365
|
+
codeVariable: 'OTHER_VAR',
|
|
366
|
+
instances: [createInstance(1), createInstance(2)]
|
|
367
|
+
});
|
|
368
|
+
const result = GroupeInstanceManager.canModifyControlVariable('S0_23', 1, [groupe], {});
|
|
369
|
+
expect(result.canProceed).toBe(true);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
describe('isInstanceComplete', () => {
|
|
373
|
+
it('devrait retourner true quand toutes les variables obligatoires sont remplies', () => {
|
|
374
|
+
const groupe = createMultipleGroupe({
|
|
375
|
+
variables: [
|
|
376
|
+
createVariable('S1_01', { estObligatoire: true }),
|
|
377
|
+
createVariable('S1_02', { estObligatoire: false })
|
|
378
|
+
]
|
|
379
|
+
});
|
|
380
|
+
const instance = createInstance(1, {
|
|
381
|
+
S1_01: createResponse('S1_01', 'Alice', 1)
|
|
382
|
+
});
|
|
383
|
+
expect(GroupeInstanceManager.isInstanceComplete(instance, groupe)).toBe(true);
|
|
384
|
+
});
|
|
385
|
+
it('devrait retourner false quand une variable obligatoire manque', () => {
|
|
386
|
+
const groupe = createMultipleGroupe({
|
|
387
|
+
variables: [
|
|
388
|
+
createVariable('S1_01', { estObligatoire: true }),
|
|
389
|
+
createVariable('S1_02', { estObligatoire: true })
|
|
390
|
+
]
|
|
391
|
+
});
|
|
392
|
+
const instance = createInstance(1, {
|
|
393
|
+
S1_01: createResponse('S1_01', 'Alice', 1)
|
|
394
|
+
});
|
|
395
|
+
expect(GroupeInstanceManager.isInstanceComplete(instance, groupe)).toBe(false);
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
describe('scenarios integration', () => {
|
|
399
|
+
it('S0_23=2, creer 2 instances, supprimer 1, ajouter 1 → doit fonctionner', () => {
|
|
400
|
+
// Setup: S0_23=2, 2 instances
|
|
401
|
+
let groupe = createMultipleGroupe({
|
|
402
|
+
instances: [createInstance(1), createInstance(2)]
|
|
403
|
+
});
|
|
404
|
+
let responses = {
|
|
405
|
+
...createControlResponse(2),
|
|
406
|
+
'S1_01_1': createResponse('S1_01', 'Alice', 1),
|
|
407
|
+
'S1_01_2': createResponse('S1_01', 'Bob', 2),
|
|
408
|
+
};
|
|
409
|
+
// Supprimer instance 1
|
|
410
|
+
const removeResult = GroupeInstanceManager.removeInstance(groupe, 1, responses);
|
|
411
|
+
expect(removeResult.success).toBe(true);
|
|
412
|
+
// Utiliser les objets mis a jour si disponibles (apres Fix 2)
|
|
413
|
+
const updatedGroupe = removeResult.updatedGroupe || groupe;
|
|
414
|
+
const updatedResponses = removeResult.updatedResponses || responses;
|
|
415
|
+
// Ajouter une nouvelle instance
|
|
416
|
+
const addResult = GroupeInstanceManager.addInstance(updatedGroupe, updatedResponses);
|
|
417
|
+
expect(addResult.success).toBe(true);
|
|
418
|
+
});
|
|
419
|
+
it('S0_23=3, creer 3 instances, tenter d\'ajouter → doit bloquer', () => {
|
|
420
|
+
const groupe = createMultipleGroupe({
|
|
421
|
+
instances: [createInstance(1), createInstance(2), createInstance(3)]
|
|
422
|
+
});
|
|
423
|
+
const responses = createControlResponse(3);
|
|
424
|
+
const result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
425
|
+
expect(result.success).toBe(false);
|
|
426
|
+
});
|
|
427
|
+
it('cycle complet ajout/suppression/ajout avec immutabilite', () => {
|
|
428
|
+
// Debut: 1 instance, max 3
|
|
429
|
+
let groupe = createMultipleGroupe({
|
|
430
|
+
instances: [createInstance(1)]
|
|
431
|
+
});
|
|
432
|
+
let responses = createControlResponse(3);
|
|
433
|
+
// Ajouter instance 2
|
|
434
|
+
let result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
435
|
+
expect(result.success).toBe(true);
|
|
436
|
+
groupe = result.updatedGroupe || groupe;
|
|
437
|
+
// Ajouter instance 3
|
|
438
|
+
result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
439
|
+
expect(result.success).toBe(true);
|
|
440
|
+
groupe = result.updatedGroupe || groupe;
|
|
441
|
+
// Tenter d'ajouter instance 4 → refuse
|
|
442
|
+
result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
443
|
+
expect(result.success).toBe(false);
|
|
444
|
+
// Supprimer instance 2
|
|
445
|
+
const removeResult = GroupeInstanceManager.removeInstance(groupe, 2, responses);
|
|
446
|
+
expect(removeResult.success).toBe(true);
|
|
447
|
+
groupe = removeResult.updatedGroupe || groupe;
|
|
448
|
+
responses = removeResult.updatedResponses || responses;
|
|
449
|
+
// Ajouter a nouveau → doit fonctionner
|
|
450
|
+
result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
451
|
+
expect(result.success).toBe(true);
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
});
|
|
@@ -15,6 +15,8 @@ export declare class GroupeInstanceManager {
|
|
|
15
15
|
static getMinInstances(groupe: GroupeFormulaire): number;
|
|
16
16
|
/**
|
|
17
17
|
* Compte le nombre d'instances existantes pour un groupe
|
|
18
|
+
* Source de vérité : groupe.instances.length
|
|
19
|
+
* Fallback par numeroMembre uniquement pour l'initialisation
|
|
18
20
|
*/
|
|
19
21
|
static getInstancesCount(groupe: GroupeFormulaire, responses: Record<string, EnqueteReponse>): number;
|
|
20
22
|
/**
|
|
@@ -42,18 +44,23 @@ export declare class GroupeInstanceManager {
|
|
|
42
44
|
*/
|
|
43
45
|
static canModifyControlVariable(variableCode: string, newValue: number, groupesMultiples: GroupeFormulaire[], responses: Record<string, EnqueteReponse>): ConstraintValidationResult;
|
|
44
46
|
/**
|
|
45
|
-
* Ajoute une nouvelle instance à un groupe
|
|
47
|
+
* Ajoute une nouvelle instance à un groupe (immutable)
|
|
48
|
+
* Retourne un nouveau groupe sans muter l'original
|
|
46
49
|
*/
|
|
47
50
|
static addInstance(groupe: GroupeFormulaire, responses: Record<string, EnqueteReponse>): {
|
|
48
51
|
success: boolean;
|
|
49
52
|
instance?: GroupeInstance;
|
|
53
|
+
updatedGroupe?: GroupeFormulaire;
|
|
50
54
|
error?: string;
|
|
51
55
|
};
|
|
52
56
|
/**
|
|
53
|
-
* Supprime une instance d'un groupe
|
|
57
|
+
* Supprime une instance d'un groupe (immutable)
|
|
58
|
+
* Retourne de nouveaux objets sans muter les originaux
|
|
54
59
|
*/
|
|
55
60
|
static removeInstance(groupe: GroupeFormulaire, instanceNumber: number, responses: Record<string, EnqueteReponse>): {
|
|
56
61
|
success: boolean;
|
|
62
|
+
updatedGroupe?: GroupeFormulaire;
|
|
63
|
+
updatedResponses?: Record<string, EnqueteReponse>;
|
|
57
64
|
error?: string;
|
|
58
65
|
};
|
|
59
66
|
/**
|
|
@@ -81,8 +88,12 @@ export declare class GroupeInstanceManager {
|
|
|
81
88
|
progressPercentage: number;
|
|
82
89
|
};
|
|
83
90
|
/**
|
|
84
|
-
* Réorganise les numéros d'instances après suppression
|
|
91
|
+
* Réorganise les numéros d'instances après suppression (immutable)
|
|
92
|
+
* Retourne de nouveaux objets sans muter les originaux
|
|
85
93
|
*/
|
|
86
|
-
static reorderInstances(
|
|
94
|
+
static reorderInstances(instances: GroupeInstance[], responses: Record<string, EnqueteReponse>, groupe: GroupeFormulaire): {
|
|
95
|
+
reorderedInstances: GroupeInstance[];
|
|
96
|
+
reorderedResponses: Record<string, EnqueteReponse>;
|
|
97
|
+
};
|
|
87
98
|
}
|
|
88
99
|
//# sourceMappingURL=groupeInstanceManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groupeInstanceManager.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/groupeInstanceManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,cAAc,EAEd,0BAA0B,EAE3B,MAAM,qBAAqB,CAAC;AAE7B,qBAAa,qBAAqB;IAEhC;;;OAGG;IACH,MAAM,CAAC,eAAe,CACpB,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GACxC,MAAM;
|
|
1
|
+
{"version":3,"file":"groupeInstanceManager.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/groupeInstanceManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,19 +9,8 @@ export class GroupeInstanceManager {
|
|
|
9
9
|
*/
|
|
10
10
|
static getMaxInstances(groupe, responses) {
|
|
11
11
|
if (!groupe.estMultiple || !groupe.codeVariable) {
|
|
12
|
-
console.log('GroupeInstanceManager.getMaxInstances - Groupe non multiple ou sans variable de contrôle:', {
|
|
13
|
-
estMultiple: groupe.estMultiple,
|
|
14
|
-
codeVariable: groupe.codeVariable,
|
|
15
|
-
groupeCode: groupe.code
|
|
16
|
-
});
|
|
17
12
|
return 1;
|
|
18
13
|
}
|
|
19
|
-
console.log('GroupeInstanceManager.getMaxInstances - Recherche variable de contrôle:', {
|
|
20
|
-
groupeCode: groupe.code,
|
|
21
|
-
codeVariable: groupe.codeVariable,
|
|
22
|
-
responsesKeys: Object.keys(responses),
|
|
23
|
-
responses: responses
|
|
24
|
-
});
|
|
25
14
|
// Chercher la réponse de la variable de contrôle
|
|
26
15
|
// D'abord essayer avec la clé directe
|
|
27
16
|
let controlResponse = responses[groupe.codeVariable];
|
|
@@ -31,17 +20,8 @@ export class GroupeInstanceManager {
|
|
|
31
20
|
if (foundResponse) {
|
|
32
21
|
controlResponse = foundResponse;
|
|
33
22
|
}
|
|
34
|
-
console.log('GroupeInstanceManager.getMaxInstances - Recherche par variableCode:', {
|
|
35
|
-
found: !!foundResponse,
|
|
36
|
-
controlResponse: foundResponse
|
|
37
|
-
});
|
|
38
23
|
}
|
|
39
24
|
const controlValue = controlResponse?.valeur;
|
|
40
|
-
console.log('GroupeInstanceManager.getMaxInstances - Valeur de contrôle trouvée:', {
|
|
41
|
-
controlResponse: controlResponse,
|
|
42
|
-
controlValue: controlValue,
|
|
43
|
-
valueType: typeof controlValue
|
|
44
|
-
});
|
|
45
25
|
// Convertir la valeur en nombre
|
|
46
26
|
let maxFromControl = 1; // Valeur par défaut
|
|
47
27
|
if (typeof controlValue === 'number') {
|
|
@@ -53,20 +33,9 @@ export class GroupeInstanceManager {
|
|
|
53
33
|
maxFromControl = parsed;
|
|
54
34
|
}
|
|
55
35
|
}
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const protectedMax = Math.max(maxFromControl, existingInstancesCount);
|
|
60
|
-
console.log('GroupeInstanceManager.getMaxInstances - Résultat avec protection:', {
|
|
61
|
-
groupeCode: groupe.code,
|
|
62
|
-
codeVariable: groupe.codeVariable,
|
|
63
|
-
maxFromControl: maxFromControl,
|
|
64
|
-
existingInstancesCount: existingInstancesCount,
|
|
65
|
-
protectedMax: protectedMax,
|
|
66
|
-
isProtectionActive: protectedMax > maxFromControl
|
|
67
|
-
});
|
|
68
|
-
// S'assurer qu'on a au moins 1 instance
|
|
69
|
-
return Math.max(1, protectedMax);
|
|
36
|
+
// Le max pour l'ajout est strictement la valeur de la variable de contrôle
|
|
37
|
+
// La protection d'affichage des onglets existants est dans GroupeInstanceTabs (minDisplayCount)
|
|
38
|
+
return Math.max(1, maxFromControl);
|
|
70
39
|
}
|
|
71
40
|
/**
|
|
72
41
|
* Récupère le nombre minimum d'instances (basé sur les instances existantes)
|
|
@@ -80,19 +49,19 @@ export class GroupeInstanceManager {
|
|
|
80
49
|
}
|
|
81
50
|
/**
|
|
82
51
|
* Compte le nombre d'instances existantes pour un groupe
|
|
52
|
+
* Source de vérité : groupe.instances.length
|
|
53
|
+
* Fallback par numeroMembre uniquement pour l'initialisation
|
|
83
54
|
*/
|
|
84
55
|
static getInstancesCount(groupe, responses) {
|
|
85
56
|
if (!groupe.estMultiple) {
|
|
86
57
|
return 1;
|
|
87
58
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
// Compter les instances basées sur les réponses
|
|
59
|
+
// Source de vérité : le tableau instances
|
|
60
|
+
if (groupe.instances && groupe.instances.length > 0) {
|
|
61
|
+
return groupe.instances.length;
|
|
62
|
+
}
|
|
63
|
+
// Fallback pour l'initialisation : compter par numeroMembre uniquement
|
|
94
64
|
const instanceNumbers = new Set();
|
|
95
|
-
// Méthode 1: Par numeroMembre
|
|
96
65
|
groupe.variables.forEach(variable => {
|
|
97
66
|
Object.values(responses).forEach(response => {
|
|
98
67
|
if (response.variableCode === variable.code && response.numeroMembre) {
|
|
@@ -100,30 +69,7 @@ export class GroupeInstanceManager {
|
|
|
100
69
|
}
|
|
101
70
|
});
|
|
102
71
|
});
|
|
103
|
-
|
|
104
|
-
groupe.variables.forEach(variable => {
|
|
105
|
-
Object.keys(responses).forEach(key => {
|
|
106
|
-
const response = responses[key];
|
|
107
|
-
if (response.variableCode === variable.code) {
|
|
108
|
-
// Extraire le numéro d'instance de la clé si elle suit le format "CODE_NUMERO"
|
|
109
|
-
const keyParts = key.split('_');
|
|
110
|
-
if (keyParts.length >= 2) {
|
|
111
|
-
const lastPart = keyParts[keyParts.length - 1];
|
|
112
|
-
const instanceNum = parseInt(lastPart, 10);
|
|
113
|
-
if (!isNaN(instanceNum) && instanceNum > 0) {
|
|
114
|
-
instanceNumbers.add(instanceNum);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
const count = Math.max(1, instanceNumbers.size);
|
|
121
|
-
console.log('GroupeInstanceManager.getInstancesCount - Résultat:', {
|
|
122
|
-
groupeCode: groupe.code,
|
|
123
|
-
instanceNumbers: Array.from(instanceNumbers).sort(),
|
|
124
|
-
finalCount: count
|
|
125
|
-
});
|
|
126
|
-
return count;
|
|
72
|
+
return Math.max(1, instanceNumbers.size);
|
|
127
73
|
}
|
|
128
74
|
/**
|
|
129
75
|
* Crée une nouvelle instance pour un groupe multiple
|
|
@@ -144,12 +90,6 @@ export class GroupeInstanceManager {
|
|
|
144
90
|
if (!groupe.estMultiple) {
|
|
145
91
|
return [];
|
|
146
92
|
}
|
|
147
|
-
console.log('GroupeInstanceManager.initializeInstances - Début:', {
|
|
148
|
-
groupeCode: groupe.code,
|
|
149
|
-
codeVariable: groupe.codeVariable,
|
|
150
|
-
responsesKeys: Object.keys(responses),
|
|
151
|
-
allResponses: responses
|
|
152
|
-
});
|
|
153
93
|
// Identifier toutes les instances existantes pour ce groupe
|
|
154
94
|
const instanceNumbers = new Set();
|
|
155
95
|
// Collecter les réponses existantes pour ce groupe (avec et sans numeroMembre)
|
|
@@ -164,29 +104,8 @@ export class GroupeInstanceManager {
|
|
|
164
104
|
}
|
|
165
105
|
}
|
|
166
106
|
});
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
const response = responses[key];
|
|
170
|
-
const belongsToGroup = groupe.variables.some(v => v.code === response.variableCode);
|
|
171
|
-
if (belongsToGroup) {
|
|
172
|
-
// Extraire le numéro d'instance de la clé si elle suit le format "CODE_NUMERO"
|
|
173
|
-
const keyParts = key.split('_');
|
|
174
|
-
if (keyParts.length >= 2) {
|
|
175
|
-
const lastPart = keyParts[keyParts.length - 1];
|
|
176
|
-
const instanceNum = parseInt(lastPart, 10);
|
|
177
|
-
if (!isNaN(instanceNum) && instanceNum > 0) {
|
|
178
|
-
instanceNumbers.add(instanceNum);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
console.log('GroupeInstanceManager.initializeInstances - Données trouvées:', {
|
|
184
|
-
groupeCode: groupe.code,
|
|
185
|
-
instanceNumbers: Array.from(instanceNumbers).sort(),
|
|
186
|
-
groupResponsesCount: groupResponses.length,
|
|
187
|
-
responsesWithNumeroMembre: groupResponses.filter(r => r.numeroMembre).length,
|
|
188
|
-
responsesWithoutNumeroMembre: groupResponses.filter(r => !r.numeroMembre).length
|
|
189
|
-
});
|
|
107
|
+
// Note: le parsing de clé par format "CODE_NUMERO" a été supprimé car il
|
|
108
|
+
// produisait des faux positifs pour les codes variables contenant des chiffres (ex: S1_01)
|
|
190
109
|
const maxInstances = this.getMaxInstances(groupe, responses);
|
|
191
110
|
// Déterminer le nombre d'instances à créer
|
|
192
111
|
let instanceCount = Math.max(1, instanceNumbers.size);
|
|
@@ -197,12 +116,6 @@ export class GroupeInstanceManager {
|
|
|
197
116
|
if (instanceCount === 0) {
|
|
198
117
|
instanceCount = 1;
|
|
199
118
|
}
|
|
200
|
-
console.log('GroupeInstanceManager.initializeInstances - Calcul instances:', {
|
|
201
|
-
groupeCode: groupe.code,
|
|
202
|
-
maxInstances,
|
|
203
|
-
existingInstancesCount: instanceNumbers.size,
|
|
204
|
-
finalInstanceCount: instanceCount
|
|
205
|
-
});
|
|
206
119
|
const instances = [];
|
|
207
120
|
// Séparer les réponses avec et sans numeroMembre
|
|
208
121
|
const responsesWithMember = groupResponses.filter(r => r.numeroMembre);
|
|
@@ -226,12 +139,6 @@ export class GroupeInstanceManager {
|
|
|
226
139
|
if (i === 1) {
|
|
227
140
|
existingResponse = responsesWithoutMember.find(r => r.variableCode === variable.code);
|
|
228
141
|
if (existingResponse) {
|
|
229
|
-
console.log('GroupeInstanceManager.initializeInstances - Attribution réponse sans numeroMembre:', {
|
|
230
|
-
groupeCode: groupe.code,
|
|
231
|
-
instanceNumber: i,
|
|
232
|
-
variableCode: variable.code,
|
|
233
|
-
originalResponse: existingResponse
|
|
234
|
-
});
|
|
235
142
|
// Créer une copie avec numeroMembre pour cette instance
|
|
236
143
|
existingResponse = {
|
|
237
144
|
...existingResponse,
|
|
@@ -241,35 +148,13 @@ export class GroupeInstanceManager {
|
|
|
241
148
|
}
|
|
242
149
|
}
|
|
243
150
|
if (existingResponse) {
|
|
244
|
-
console.log('GroupeInstanceManager.initializeInstances - Réponse trouvée:', {
|
|
245
|
-
groupeCode: groupe.code,
|
|
246
|
-
instanceNumber: i,
|
|
247
|
-
variableCode: variable.code,
|
|
248
|
-
hasNumeroMembre: !!existingResponse.numeroMembre,
|
|
249
|
-
response: existingResponse
|
|
250
|
-
});
|
|
251
151
|
instance.reponses[variable.code] = existingResponse;
|
|
252
152
|
}
|
|
253
153
|
});
|
|
254
154
|
// Vérifier si l'instance est complète
|
|
255
155
|
instance.estComplete = this.isInstanceComplete(instance, groupe);
|
|
256
156
|
instances.push(instance);
|
|
257
|
-
console.log('GroupeInstanceManager.initializeInstances - Instance créée:', {
|
|
258
|
-
groupeCode: groupe.code,
|
|
259
|
-
instanceNumber: i,
|
|
260
|
-
responsesCount: Object.keys(instance.reponses).length,
|
|
261
|
-
isComplete: instance.estComplete
|
|
262
|
-
});
|
|
263
157
|
}
|
|
264
|
-
console.log('GroupeInstanceManager.initializeInstances - Résultat final:', {
|
|
265
|
-
groupeCode: groupe.code,
|
|
266
|
-
instancesCreated: instances.length,
|
|
267
|
-
instances: instances.map(inst => ({
|
|
268
|
-
numero: inst.numeroInstance,
|
|
269
|
-
responsesCount: Object.keys(inst.reponses).length,
|
|
270
|
-
isComplete: inst.estComplete
|
|
271
|
-
}))
|
|
272
|
-
});
|
|
273
158
|
return instances;
|
|
274
159
|
}
|
|
275
160
|
/**
|
|
@@ -332,12 +217,6 @@ export class GroupeInstanceManager {
|
|
|
332
217
|
const constraints = [];
|
|
333
218
|
// CORRECTION: Utiliser le nombre d'instances du groupe directement
|
|
334
219
|
const currentCount = groupe.instances?.length || 0;
|
|
335
|
-
console.log('GroupeInstanceManager.canRemoveInstance - Debug:', {
|
|
336
|
-
groupeCode: groupe.code,
|
|
337
|
-
instancesLength: groupe.instances?.length,
|
|
338
|
-
currentCount,
|
|
339
|
-
canRemove: currentCount > 1
|
|
340
|
-
});
|
|
341
220
|
if (currentCount <= 1) {
|
|
342
221
|
constraints.push({
|
|
343
222
|
type: 'instance_limit',
|
|
@@ -368,7 +247,7 @@ export class GroupeInstanceManager {
|
|
|
368
247
|
// Trouver tous les groupes qui utilisent cette variable de contrôle
|
|
369
248
|
const affectedGroups = groupesMultiples.filter(g => g.codeVariable === variableCode);
|
|
370
249
|
for (const groupe of affectedGroups) {
|
|
371
|
-
const existingInstances = this.getInstancesCount(groupe, responses);
|
|
250
|
+
const existingInstances = groupe.instances?.length || this.getInstancesCount(groupe, responses);
|
|
372
251
|
if (newValue < existingInstances) {
|
|
373
252
|
constraints.push({
|
|
374
253
|
type: 'bidirectional_control',
|
|
@@ -396,7 +275,8 @@ export class GroupeInstanceManager {
|
|
|
396
275
|
};
|
|
397
276
|
}
|
|
398
277
|
/**
|
|
399
|
-
* Ajoute une nouvelle instance à un groupe
|
|
278
|
+
* Ajoute une nouvelle instance à un groupe (immutable)
|
|
279
|
+
* Retourne un nouveau groupe sans muter l'original
|
|
400
280
|
*/
|
|
401
281
|
static addInstance(groupe, responses) {
|
|
402
282
|
const validation = this.canAddInstance(groupe, responses);
|
|
@@ -406,29 +286,25 @@ export class GroupeInstanceManager {
|
|
|
406
286
|
error: validation.constraints[0]?.message || 'Impossible d\'ajouter une instance'
|
|
407
287
|
};
|
|
408
288
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
groupe.instances = [];
|
|
412
|
-
}
|
|
413
|
-
const newInstanceNumber = groupe.instances.length + 1;
|
|
289
|
+
const currentInstances = groupe.instances || [];
|
|
290
|
+
const newInstanceNumber = currentInstances.length + 1;
|
|
414
291
|
const newInstance = this.createInstance(groupe, newInstanceNumber);
|
|
415
|
-
//
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
totalInstances: groupe.instances.length,
|
|
423
|
-
maxInstances: this.getMaxInstances(groupe, responses)
|
|
424
|
-
});
|
|
292
|
+
// Construire un nouveau groupe avec l'instance ajoutée (sans muter l'original)
|
|
293
|
+
const updatedInstances = [...currentInstances, newInstance];
|
|
294
|
+
const updatedGroupe = {
|
|
295
|
+
...groupe,
|
|
296
|
+
instances: updatedInstances,
|
|
297
|
+
instancesCount: updatedInstances.length
|
|
298
|
+
};
|
|
425
299
|
return {
|
|
426
300
|
success: true,
|
|
427
|
-
instance: newInstance
|
|
301
|
+
instance: newInstance,
|
|
302
|
+
updatedGroupe
|
|
428
303
|
};
|
|
429
304
|
}
|
|
430
305
|
/**
|
|
431
|
-
* Supprime une instance d'un groupe
|
|
306
|
+
* Supprime une instance d'un groupe (immutable)
|
|
307
|
+
* Retourne de nouveaux objets sans muter les originaux
|
|
432
308
|
*/
|
|
433
309
|
static removeInstance(groupe, instanceNumber, responses) {
|
|
434
310
|
const validation = this.canRemoveInstance(groupe, responses);
|
|
@@ -438,26 +314,29 @@ export class GroupeInstanceManager {
|
|
|
438
314
|
error: validation.constraints[0]?.message || 'Impossible de supprimer cette instance'
|
|
439
315
|
};
|
|
440
316
|
}
|
|
441
|
-
//
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
// Trouver si cette réponse appartient à ce groupe
|
|
451
|
-
const belongsToGroup = groupe.variables.some(v => v.code === response.variableCode);
|
|
452
|
-
if (belongsToGroup) {
|
|
453
|
-
delete responses[key];
|
|
454
|
-
}
|
|
317
|
+
// Filtrer les instances (sans muter l'original)
|
|
318
|
+
const filteredInstances = (groupe.instances || []).filter(i => i.numeroInstance !== instanceNumber);
|
|
319
|
+
// Construire de nouvelles réponses en excluant celles de l'instance supprimée
|
|
320
|
+
const cleanedResponses = {};
|
|
321
|
+
Object.entries(responses).forEach(([key, response]) => {
|
|
322
|
+
const belongsToGroup = groupe.variables.some(v => v.code === response.variableCode);
|
|
323
|
+
if (belongsToGroup && response.numeroMembre === instanceNumber) {
|
|
324
|
+
// Exclure les réponses de l'instance supprimée
|
|
325
|
+
return;
|
|
455
326
|
}
|
|
327
|
+
cleanedResponses[key] = { ...response };
|
|
456
328
|
});
|
|
457
329
|
// Réorganiser les instances pour maintenir une numérotation séquentielle
|
|
458
|
-
this.reorderInstances(
|
|
330
|
+
const { reorderedInstances, reorderedResponses } = this.reorderInstances(filteredInstances, cleanedResponses, groupe);
|
|
331
|
+
const updatedGroupe = {
|
|
332
|
+
...groupe,
|
|
333
|
+
instances: reorderedInstances,
|
|
334
|
+
instancesCount: reorderedInstances.length
|
|
335
|
+
};
|
|
459
336
|
return {
|
|
460
|
-
success: true
|
|
337
|
+
success: true,
|
|
338
|
+
updatedGroupe,
|
|
339
|
+
updatedResponses: reorderedResponses
|
|
461
340
|
};
|
|
462
341
|
}
|
|
463
342
|
/**
|
|
@@ -473,14 +352,6 @@ export class GroupeInstanceManager {
|
|
|
473
352
|
if (!groupe.instances) {
|
|
474
353
|
groupe.instances = this.initializeInstances(groupe, responses);
|
|
475
354
|
}
|
|
476
|
-
// PROTECTION DES DONNÉES : Ne plus supprimer automatiquement les instances
|
|
477
|
-
// La logique de protection est maintenant dans getMaxInstances()
|
|
478
|
-
console.log('GroupeInstanceManager.updateGroupeProperties - Protection des données active:', {
|
|
479
|
-
groupeCode: groupe.code,
|
|
480
|
-
currentInstances: groupe.instances.length,
|
|
481
|
-
maxInstances: groupe.maxInstances,
|
|
482
|
-
protectionActive: groupe.instances.length > 0
|
|
483
|
-
});
|
|
484
355
|
groupe.instancesCount = groupe.instances.length;
|
|
485
356
|
// Mettre à jour le statut de completion des instances
|
|
486
357
|
groupe.instances.forEach(instance => {
|
|
@@ -567,40 +438,64 @@ export class GroupeInstanceManager {
|
|
|
567
438
|
};
|
|
568
439
|
}
|
|
569
440
|
/**
|
|
570
|
-
* Réorganise les numéros d'instances après suppression
|
|
441
|
+
* Réorganise les numéros d'instances après suppression (immutable)
|
|
442
|
+
* Retourne de nouveaux objets sans muter les originaux
|
|
571
443
|
*/
|
|
572
|
-
static reorderInstances(
|
|
573
|
-
if (!
|
|
574
|
-
return;
|
|
444
|
+
static reorderInstances(instances, responses, groupe) {
|
|
445
|
+
if (!instances || instances.length === 0) {
|
|
446
|
+
return { reorderedInstances: [], reorderedResponses: { ...responses } };
|
|
575
447
|
}
|
|
576
|
-
// Trier les instances par numéro
|
|
577
|
-
|
|
578
|
-
//
|
|
579
|
-
|
|
448
|
+
// Trier les instances par numéro (sur des copies)
|
|
449
|
+
const sortedInstances = [...instances].sort((a, b) => a.numeroInstance - b.numeroInstance);
|
|
450
|
+
// Construire le mapping oldNumber → newNumber
|
|
451
|
+
const numberMapping = new Map();
|
|
452
|
+
sortedInstances.forEach((instance, index) => {
|
|
580
453
|
const newNumber = index + 1;
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
454
|
+
if (instance.numeroInstance !== newNumber) {
|
|
455
|
+
numberMapping.set(instance.numeroInstance, newNumber);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
// Créer les nouvelles instances avec les numéros mis à jour
|
|
459
|
+
const reorderedInstances = sortedInstances.map((instance, index) => {
|
|
460
|
+
const newNumber = index + 1;
|
|
461
|
+
if (instance.numeroInstance === newNumber) {
|
|
462
|
+
return { ...instance }; // Copie sans changement de numéro
|
|
463
|
+
}
|
|
464
|
+
// Copier avec nouveau numéro
|
|
465
|
+
const newReponses = {};
|
|
466
|
+
Object.entries(instance.reponses).forEach(([variableCode, response]) => {
|
|
467
|
+
newReponses[variableCode] = { ...response, numeroMembre: newNumber };
|
|
468
|
+
});
|
|
469
|
+
return {
|
|
470
|
+
...instance,
|
|
471
|
+
numeroInstance: newNumber,
|
|
472
|
+
libelle: `${groupe.designation} ${newNumber}`,
|
|
473
|
+
reponses: newReponses
|
|
474
|
+
};
|
|
475
|
+
});
|
|
476
|
+
// Si aucun renumérotage nécessaire, retourner une copie simple
|
|
477
|
+
if (numberMapping.size === 0) {
|
|
478
|
+
return { reorderedInstances, reorderedResponses: { ...responses } };
|
|
479
|
+
}
|
|
480
|
+
// Construire les nouvelles réponses avec les clés mises à jour
|
|
481
|
+
const reorderedResponses = {};
|
|
482
|
+
Object.entries(responses).forEach(([key, response]) => {
|
|
483
|
+
const belongsToGroup = groupe.variables.some(v => v.code === response.variableCode);
|
|
484
|
+
if (belongsToGroup && response.numeroMembre && numberMapping.has(response.numeroMembre)) {
|
|
485
|
+
const newNumber = numberMapping.get(response.numeroMembre);
|
|
486
|
+
const newKey = `${response.variableCode}_${newNumber}`;
|
|
487
|
+
reorderedResponses[newKey] = { ...response, numeroMembre: newNumber };
|
|
488
|
+
}
|
|
489
|
+
else if (belongsToGroup && response.numeroMembre && !numberMapping.has(response.numeroMembre)) {
|
|
490
|
+
// Réponse du groupe mais pas de renumérotage nécessaire - garder avec la bonne clé
|
|
491
|
+
const correctKey = `${response.variableCode}_${response.numeroMembre}`;
|
|
492
|
+
reorderedResponses[correctKey] = { ...response };
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
// Réponse hors du groupe, garder telle quelle
|
|
496
|
+
reorderedResponses[key] = { ...response };
|
|
603
497
|
}
|
|
604
498
|
});
|
|
499
|
+
return { reorderedInstances, reorderedResponses };
|
|
605
500
|
}
|
|
606
501
|
}
|
package/package.json
CHANGED