@rsuci/shared-form-components 1.0.86 → 1.0.88
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 +24 -33
- 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 +55 -12
- 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/dist/lib/utils/interpolateVariableLabel.d.ts.map +1 -1
- package/dist/lib/utils/interpolateVariableLabel.js +1 -35
- 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;AAuxBD;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgBpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -76,6 +76,9 @@ const FormRendererInner = () => {
|
|
|
76
76
|
const instanceNumber = currentGroup.estMultiple
|
|
77
77
|
? currentInstance?.numeroInstance
|
|
78
78
|
: undefined;
|
|
79
|
+
// Ne pas exécuter les auto-actions sans instance résolue dans un groupe multiple
|
|
80
|
+
if (currentGroup.estMultiple && instanceNumber === undefined)
|
|
81
|
+
return;
|
|
79
82
|
// Évaluer les autoactions pour le groupe courant
|
|
80
83
|
const autoUpdates = evaluateAutoActionsForInstance(currentGroup.code, instanceNumber);
|
|
81
84
|
// Appliquer les mises à jour si nécessaire
|
|
@@ -94,7 +97,6 @@ const FormRendererInner = () => {
|
|
|
94
97
|
: targetVariable.code;
|
|
95
98
|
const currentValue = responses[currentResponseKey]?.valeur;
|
|
96
99
|
if (currentValue !== value) {
|
|
97
|
-
console.log(`🔄 [AutoAction] Setting ${targetKey} = ${JSON.stringify(value)}`);
|
|
98
100
|
updateResponse(targetVariable.code, value, targetVariable, instanceNumber);
|
|
99
101
|
}
|
|
100
102
|
}
|
|
@@ -142,9 +144,15 @@ const FormRendererInner = () => {
|
|
|
142
144
|
let numeroMembre = currentGroup?.estMultiple
|
|
143
145
|
? currentInstance?.numeroInstance
|
|
144
146
|
: undefined;
|
|
147
|
+
// Pour les groupes multiples, ne PAS continuer sans instance résolue
|
|
148
|
+
// (évite de stocker les réponses sans suffixe _N)
|
|
149
|
+
if (currentGroup?.estMultiple && numeroMembre === undefined) {
|
|
150
|
+
console.warn('[FormRenderer] handleVariableChange: groupe multiple sans currentInstance, rejet');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
145
153
|
// Pour les groupes non-multiples, préserver le numeroMembre existant du backend
|
|
146
154
|
// afin que la sauvegarde utilise la même clé que le chargement
|
|
147
|
-
if (numeroMembre === undefined) {
|
|
155
|
+
if (numeroMembre === undefined && !currentGroup?.estMultiple) {
|
|
148
156
|
const existingResponse = responsesByVariableCode.get(variable.code);
|
|
149
157
|
if (existingResponse?.numeroMembre) {
|
|
150
158
|
numeroMembre = existingResponse.numeroMembre;
|
|
@@ -206,16 +214,22 @@ const FormRendererInner = () => {
|
|
|
206
214
|
navigation.changeInstance(instanceIndex);
|
|
207
215
|
};
|
|
208
216
|
// Handler pour l'ajout d'instance
|
|
209
|
-
const handleInstanceAdded = (instance) => {
|
|
210
|
-
|
|
217
|
+
const handleInstanceAdded = (instance, updatedGroupe) => {
|
|
218
|
+
// Mettre à jour le groupe via l'état React (pas de mutation directe)
|
|
219
|
+
instances.setGroupesWithInstances(prev => prev.map(g => g.code === updatedGroupe.code ? updatedGroupe : g));
|
|
220
|
+
navigation.updateTotalInstances(updatedGroupe.instances?.length || 1);
|
|
211
221
|
// Naviguer vers la nouvelle instance
|
|
212
|
-
if (
|
|
213
|
-
navigation.changeInstance(
|
|
222
|
+
if (updatedGroupe.instances) {
|
|
223
|
+
navigation.changeInstance(updatedGroupe.instances.length - 1);
|
|
214
224
|
}
|
|
215
225
|
};
|
|
216
226
|
// Handler pour la suppression d'instance
|
|
217
|
-
const handleInstanceRemoved = (instanceNumber) => {
|
|
218
|
-
|
|
227
|
+
const handleInstanceRemoved = (instanceNumber, updatedGroupe, updatedResponses) => {
|
|
228
|
+
// CRITIQUE : mettre à jour les réponses via l'état React
|
|
229
|
+
context.setResponses(updatedResponses);
|
|
230
|
+
// Mettre à jour le groupe via l'état React
|
|
231
|
+
instances.setGroupesWithInstances(prev => prev.map(g => g.code === updatedGroupe.code ? updatedGroupe : g));
|
|
232
|
+
navigation.updateTotalInstances(updatedGroupe.instances?.length || 1);
|
|
219
233
|
// Revenir à la première instance
|
|
220
234
|
navigation.changeInstance(0);
|
|
221
235
|
};
|
|
@@ -240,24 +254,19 @@ const FormRendererInner = () => {
|
|
|
240
254
|
}
|
|
241
255
|
// Pour les groupes normaux, valider uniquement les variables visibles et obligatoires
|
|
242
256
|
const requiredVariables = currentGroup.variables.filter(v => v.estObligatoire);
|
|
243
|
-
console.log('🔍 [Validation] Mode:', mode, 'Groupe:', currentGroup.code);
|
|
244
|
-
console.log('🔍 [Validation] Variables obligatoires:', requiredVariables.length);
|
|
245
257
|
for (const variable of requiredVariables) {
|
|
246
258
|
// Ignorer les variables LABEL - ce sont des éléments d'affichage uniquement qui ne peuvent pas avoir de valeur
|
|
247
259
|
if (variable.typeCode === 'LABEL') {
|
|
248
|
-
console.log('🔍 [Validation] Variable LABEL ignorée:', variable.code);
|
|
249
260
|
continue;
|
|
250
261
|
}
|
|
251
262
|
// Vérifier si la variable est filtrée par le mode (MENAGE/ENQUETE en mode public)
|
|
252
263
|
const isFilteredByMode = isVariableFilteredByMode(variable);
|
|
253
264
|
if (isFilteredByMode) {
|
|
254
|
-
console.log('🔍 [Validation] Variable filtrée par mode:', variable.code, 'typeCode:', variable.typeCode);
|
|
255
265
|
continue;
|
|
256
266
|
}
|
|
257
267
|
// Vérifier si la variable est visible via le formTree
|
|
258
268
|
const isVisible = isVariableVisible(variable);
|
|
259
269
|
if (!isVisible) {
|
|
260
|
-
console.log('🔍 [Validation] Variable masquée par formTree:', variable.code);
|
|
261
270
|
continue;
|
|
262
271
|
}
|
|
263
272
|
// Vérifier si la variable a une valeur
|
|
@@ -266,13 +275,10 @@ const FormRendererInner = () => {
|
|
|
266
275
|
: variable.code;
|
|
267
276
|
const response = responses[responseKey];
|
|
268
277
|
const hasValue = response && response.valeur != null && response.valeur !== '';
|
|
269
|
-
console.log('🔍 [Validation] Variable:', variable.code, 'hasValue:', hasValue, 'valeur:', response?.valeur);
|
|
270
278
|
if (!hasValue) {
|
|
271
|
-
console.log('❌ [Validation] ECHEC - Variable sans valeur:', variable.code);
|
|
272
279
|
return false;
|
|
273
280
|
}
|
|
274
281
|
}
|
|
275
|
-
console.log('✅ [Validation] Toutes les variables obligatoires sont remplies');
|
|
276
282
|
return true;
|
|
277
283
|
}, [currentGroup, currentInstance, responses, isVariableVisible, isVariableFilteredByMode, validateGroupForInstance, mode]);
|
|
278
284
|
// Mode consultation (pas de validation, navigation libre)
|
|
@@ -290,7 +296,6 @@ const FormRendererInner = () => {
|
|
|
290
296
|
}
|
|
291
297
|
// Valider les champs obligatoires
|
|
292
298
|
if (!canGoNext) {
|
|
293
|
-
console.log('❌ [Navigation] Champs obligatoires non remplis');
|
|
294
299
|
return;
|
|
295
300
|
}
|
|
296
301
|
// Valider les conditions de validation (ConditionEval) du groupe actuel
|
|
@@ -298,7 +303,6 @@ const FormRendererInner = () => {
|
|
|
298
303
|
const numeroMembre = currentGroup.estMultiple ? currentInstance?.numeroInstance : undefined;
|
|
299
304
|
const conditionErrors = validateConditionGroup(currentGroup, numeroMembre);
|
|
300
305
|
if (conditionErrors.length > 0) {
|
|
301
|
-
console.log('❌ [Navigation] Erreurs de validation ConditionEval:', conditionErrors);
|
|
302
306
|
// Les erreurs sont déjà affichées sous les variables via getConditionErrorForVariable
|
|
303
307
|
return;
|
|
304
308
|
}
|
|
@@ -333,7 +337,6 @@ const FormRendererInner = () => {
|
|
|
333
337
|
}
|
|
334
338
|
const conditionErrors = validateAllConditions(groupes, instancesMap);
|
|
335
339
|
if (conditionErrors.length > 0) {
|
|
336
|
-
console.log('❌ [Soumission] Erreurs de validation ConditionEval:', conditionErrors);
|
|
337
340
|
if (callbacks.onValidationError) {
|
|
338
341
|
// Convertir les erreurs de condition en erreurs de validation standard
|
|
339
342
|
callbacks.onValidationError(conditionErrors.map(e => ({
|
|
@@ -399,25 +402,13 @@ const FormRendererInner = () => {
|
|
|
399
402
|
: variable.code;
|
|
400
403
|
let currentValue = responses[responseKey]?.valeur;
|
|
401
404
|
// Fallback: si pas trouvé par clé directe, chercher par variableCode
|
|
402
|
-
//
|
|
403
|
-
if (currentValue === undefined) {
|
|
405
|
+
// UNIQUEMENT pour les groupes non-multiples (évite la fuite de données cross-instance)
|
|
406
|
+
if (currentValue === undefined && !currentGroup?.estMultiple) {
|
|
404
407
|
const fallbackResponse = responsesByVariableCode.get(variable.code);
|
|
405
408
|
if (fallbackResponse) {
|
|
406
409
|
currentValue = fallbackResponse.valeur;
|
|
407
410
|
}
|
|
408
411
|
}
|
|
409
|
-
// Debug log pour tracer le flux de données géographiques
|
|
410
|
-
if (['DISTRICT', 'REGION', 'DEPARTEMENT', 'SOUSPREFECTURE', 'QUARTIER'].includes(variable.typeCode)) {
|
|
411
|
-
console.log('[FormRenderer] Variable géographique:', {
|
|
412
|
-
variableCode: variable.code,
|
|
413
|
-
typeCode: variable.typeCode,
|
|
414
|
-
responseKey,
|
|
415
|
-
hasResponseKey: responseKey in responses,
|
|
416
|
-
responseObject: responses[responseKey],
|
|
417
|
-
currentValue,
|
|
418
|
-
currentValueType: typeof currentValue
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
412
|
// Extraire les variables roster si c'est un type ROSTERCHECK ou ROSTERLIST
|
|
422
413
|
const rosterVariables = (variable.typeCode === 'ROSTERCHECK' || variable.typeCode === 'ROSTERLIST')
|
|
423
414
|
? variable.rosterVariables
|
|
@@ -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":"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;AAG9E,MAAM,WAAW,sBAAsB;IACrC,oCAAoC;IACpC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,yBAAyB;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAClD,8CAA8C;IAC9C,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,IAAI,CAAC;IACxE,4CAA4C;IAC5C,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,yDAAyD;IACzD,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,CAAC;IAC9D,iBAAiB;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IAEpC,UAAU,EAAE,uBAAuB,CAAC;IAGpC,UAAU,EAAE,uBAAuB,CAAC;IAGpC,SAAS,EAAE,sBAAsB,CAAC;IAGlC,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,gCAAgC;IAChC,cAAc,EAAE,CACd,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,kBAAkB,EAC5B,YAAY,CAAC,EAAE,MAAM,KAClB,IAAI,CAAC;IACV,wCAAwC;IACxC,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IAGnF,gDAAgD;IAChD,iBAAiB,EAAE,OAAO,CAAC;IAC3B,0CAA0C;IAC1C,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,iCAAiC;IACjC,YAAY,EAAE,OAAO,CAAC;IACtB,mCAAmC;IACnC,eAAe,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/D,kCAAkC;IAClC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,gCAAgC;IAChC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,4BAA4B;IAC5B,UAAU,EAAE,OAAO,CAAC;IAGpB,iCAAiC;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,+BAA+B;IAC/B,QAAQ,EAAE,oBAAoB,CAAC;IAG/B,kCAAkC;IAClC,YAAY,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC3C,qCAAqC;IACrC,eAAe,EAAE,cAAc,GAAG,SAAS,CAAC;IAC5C,0CAA0C;IAC1C,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;IAGzC,+CAA+C;IAC/C,iBAAiB,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,CAAC;IAC7D,kDAAkD;IAClD,cAAc,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACxE,sCAAsC;IACtC,gBAAgB,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,aAAa,CAAC;CAClF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,iBAAiB,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,OAAO,GAAE,sBAAuD,GAC/D,qBAAqB,CAgQvB;AAED,eAAe,eAAe,CAAC"}
|
|
@@ -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,
|
|
@@ -111,27 +126,55 @@ export function useFormRenderer(formulaire, enquete, options = { config: { mode:
|
|
|
111
126
|
}
|
|
112
127
|
return updated;
|
|
113
128
|
});
|
|
114
|
-
// Mettre à jour l'instance si c'est un groupe multiple
|
|
129
|
+
// Mettre à jour l'instance si c'est un groupe multiple (immutable via setter)
|
|
115
130
|
if (currentGroup?.estMultiple && currentInstance) {
|
|
116
|
-
|
|
117
|
-
|
|
131
|
+
instances.setGroupesWithInstances(prev => prev.map(g => {
|
|
132
|
+
if (g.code !== currentGroup.code || !g.instances)
|
|
133
|
+
return g;
|
|
134
|
+
return {
|
|
135
|
+
...g,
|
|
136
|
+
instances: g.instances.map(inst => {
|
|
137
|
+
if (inst.numeroInstance !== currentInstance.numeroInstance)
|
|
138
|
+
return inst;
|
|
139
|
+
const updatedReponses = { ...inst.reponses, [variableCode]: newResponse };
|
|
140
|
+
return {
|
|
141
|
+
...inst,
|
|
142
|
+
reponses: updatedReponses,
|
|
143
|
+
estComplete: instances.isInstanceComplete({ ...inst, reponses: updatedReponses }, currentGroup)
|
|
144
|
+
};
|
|
145
|
+
})
|
|
146
|
+
};
|
|
147
|
+
}));
|
|
118
148
|
}
|
|
119
|
-
// Mettre à jour les propriétés des groupes affectés
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
149
|
+
// Mettre à jour les propriétés des groupes affectés (immutable via setter)
|
|
150
|
+
const affectedGroupCodes = instances.groupesWithInstances
|
|
151
|
+
.filter(g => g.codeVariable === variableCode)
|
|
152
|
+
.map(g => g.code);
|
|
153
|
+
if (affectedGroupCodes.length > 0) {
|
|
154
|
+
const updatedResponses = { ...responses, [responseKey]: newResponse };
|
|
155
|
+
instances.setGroupesWithInstances(prev => prev.map(g => {
|
|
156
|
+
if (!affectedGroupCodes.includes(g.code))
|
|
157
|
+
return g;
|
|
158
|
+
return {
|
|
159
|
+
...g,
|
|
160
|
+
maxInstances: GroupeInstanceManager.getMaxInstances(g, updatedResponses),
|
|
161
|
+
minInstances: GroupeInstanceManager.getMinInstances(g),
|
|
162
|
+
instancesCount: g.instances?.length ?? 0,
|
|
163
|
+
instances: g.instances?.map(inst => ({
|
|
164
|
+
...inst,
|
|
165
|
+
estComplete: GroupeInstanceManager.isInstanceComplete(inst, g)
|
|
166
|
+
}))
|
|
167
|
+
};
|
|
168
|
+
}));
|
|
128
169
|
}
|
|
129
170
|
setHasUnsavedChanges(true);
|
|
130
171
|
}, [
|
|
131
172
|
getResponseKey,
|
|
173
|
+
isControlVariable,
|
|
132
174
|
currentGroup,
|
|
133
175
|
currentInstance,
|
|
134
176
|
instances,
|
|
177
|
+
formulaire?.groupes,
|
|
135
178
|
responses,
|
|
136
179
|
onResponsesChange
|
|
137
180
|
]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groupeInstanceManager.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/groupeInstanceManager.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|