@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.
@@ -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;AA0xBD;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgBpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
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
- navigation.updateTotalInstances(currentGroup?.instances?.length || 1);
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 (currentGroup?.instances) {
213
- navigation.changeInstance(currentGroup.instances.length - 1);
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
- navigation.updateTotalInstances(currentGroup?.instances?.length || 1);
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
- // (gère les groupes non-multiples le backend stocke avec numero_membre)
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;IACpD,iBAAiB,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CA0RzD,CAAC;AAEF,eAAe,kBAAkB,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 === groupe.code) {
93
- return { ...groupe };
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 === groupe.code) {
122
- return { ...groupe };
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":"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"}
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,
@@ -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
- currentInstance.reponses[variableCode] = newResponse;
117
- currentInstance.estComplete = instances.isInstanceComplete(currentInstance, currentGroup);
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 affectedGroups = instances.groupesWithInstances.filter(g => g.codeVariable === variableCode);
121
- if (affectedGroups.length > 0) {
122
- affectedGroups.forEach(groupe => {
123
- instances.updateGroupeProperties(groupe, {
124
- ...responses,
125
- [responseKey]: newResponse
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,6 @@
1
+ /**
2
+ * Tests unitaires pour GroupeInstanceManager
3
+ * Couvre le comptage, l'ajout/suppression, la validation et les scénarios d'intégration
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=groupeInstanceManager.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groupeInstanceManager.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/groupeInstanceManager.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}