@rsuci/shared-form-components 1.0.123 → 1.0.124

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;AAuBlF;;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;AA0gCD;;;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;AAuBlF;;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;AAmkCD;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgBpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -285,8 +285,23 @@ const FormRendererInner = () => {
285
285
  };
286
286
  // Handler pour l'ajout d'instance
287
287
  const handleInstanceAdded = (instance, updatedGroupe) => {
288
- // Mettre à jour le groupe via l'état React (pas de mutation directe)
289
- instances.setGroupesWithInstances(prev => prev.map(g => g.code === updatedGroupe.code ? updatedGroupe : g));
288
+ // Cross-groupe : ajouter l'instance dans tous les groupes liés par la même codeVariable
289
+ const linkedGroups = GroupeInstanceManager.getLinkedGroups(updatedGroupe, instances.groupesWithInstances);
290
+ instances.setGroupesWithInstances(prev => prev.map(g => {
291
+ if (g.code === updatedGroupe.code)
292
+ return updatedGroupe;
293
+ // Ajouter l'instance dans les groupes liés
294
+ const isLinked = linkedGroups.some(lg => lg.code === g.code);
295
+ if (isLinked && g.estMultiple && g.instances) {
296
+ const alreadyExists = g.instances.some(i => i.numeroInstance === instance.numeroInstance);
297
+ if (!alreadyExists) {
298
+ const newInst = GroupeInstanceManager.createInstance(g, instance.numeroInstance);
299
+ const updatedInstances = [...g.instances, newInst];
300
+ return { ...g, instances: updatedInstances, instancesCount: updatedInstances.length };
301
+ }
302
+ }
303
+ return g;
304
+ }));
290
305
  navigation.updateTotalInstances(updatedGroupe.instances?.length || 1);
291
306
  // Naviguer vers la nouvelle instance
292
307
  if (updatedGroupe.instances) {
@@ -295,10 +310,37 @@ const FormRendererInner = () => {
295
310
  };
296
311
  // Handler pour la suppression d'instance
297
312
  const handleInstanceRemoved = (instanceNumber, updatedGroupe, updatedResponses) => {
313
+ // Cross-groupe : supprimer l'instance dans tous les groupes liés + renuméroter
314
+ const originalGroupe = instances.groupesWithInstances.find(g => g.code === updatedGroupe.code);
315
+ const linkedGroups = GroupeInstanceManager.getLinkedGroups((originalGroupe || updatedGroupe), instances.groupesWithInstances);
316
+ let currentResponses = { ...updatedResponses };
317
+ instances.setGroupesWithInstances(prev => prev.map(g => {
318
+ if (g.code === updatedGroupe.code)
319
+ return updatedGroupe;
320
+ // Appliquer la suppression + renumérotation aux groupes liés
321
+ const isLinked = linkedGroups.some(lg => lg.code === g.code);
322
+ if (!isLinked || !g.estMultiple || !g.instances)
323
+ return g;
324
+ const hasInstance = g.instances.some(i => i.numeroInstance === instanceNumber);
325
+ if (!hasInstance)
326
+ return g;
327
+ // Filtrer l'instance supprimée
328
+ const filtered = g.instances.filter(i => i.numeroInstance !== instanceNumber);
329
+ // Nettoyer les réponses de l'instance supprimée pour ce groupe
330
+ const cleaned = {};
331
+ Object.entries(currentResponses).forEach(([key, resp]) => {
332
+ const belongsToGroup = g.variables.some(v => v.code === resp.variableCode);
333
+ if (belongsToGroup && resp.numeroMembre === instanceNumber)
334
+ return;
335
+ cleaned[key] = resp;
336
+ });
337
+ // Renuméroter
338
+ const { reorderedInstances, reorderedResponses } = GroupeInstanceManager.reorderInstances(filtered, cleaned, g);
339
+ currentResponses = reorderedResponses;
340
+ return { ...g, instances: reorderedInstances, instancesCount: reorderedInstances.length };
341
+ }));
298
342
  // CRITIQUE : mettre à jour les réponses via l'état React
299
- context.setResponses(updatedResponses);
300
- // Mettre à jour le groupe via l'état React
301
- instances.setGroupesWithInstances(prev => prev.map(g => g.code === updatedGroupe.code ? updatedGroupe : g));
343
+ context.setResponses(currentResponses);
302
344
  navigation.updateTotalInstances(updatedGroupe.instances?.length || 1);
303
345
  // Revenir à la première instance
304
346
  navigation.changeInstance(0);
@@ -625,7 +667,7 @@ const FormRendererInner = () => {
625
667
  if (!formulaire || !formulaire.groupes || formulaire.groupes.length === 0) {
626
668
  return (_jsx("div", { className: "min-h-screen bg-gray-50 flex items-center justify-center", children: _jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-8 text-center max-w-md", children: [_jsx("div", { className: "text-red-500 mb-4", children: _jsx("svg", { className: "w-12 h-12 mx-auto", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 18.5c-.77.833.192 2.5 1.732 2.5z" }) }) }), _jsx("h3", { className: "text-lg font-medium text-gray-900 mb-2", children: "Formulaire non configur\u00E9" }), _jsx("p", { className: "text-gray-600 mb-4", children: "Ce formulaire ne contient aucun groupe de variables." }), _jsx("button", { onClick: callbacks.onCancel, className: "inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700", children: "Retour" })] }) }));
627
669
  }
628
- return (_jsxs("div", { className: "min-h-screen bg-gray-50", children: [_jsxs("div", { className: "max-w-4xl mx-auto px-4 py-6", children: [_jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-4 mb-6", children: [_jsx("h1", { className: "text-xl font-semibold text-gray-900 mb-4", children: formulaire.designation }), _jsx(FormProgress, { showPercentage: true })] }), _jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 mb-6", children: [_jsx("div", { className: "bg-green-600 px-6 py-4", children: _jsx("h2", { className: "text-xl font-bold text-white uppercase tracking-wide", children: currentGroup?.designation }) }), _jsxs("div", { className: "p-6", children: [currentGroup?.estMultiple && currentGroup.instances && (_jsx("div", { className: "mb-6", children: _jsx(GroupeInstanceTabs, { groupe: currentGroup, currentInstanceIndex: navigation.navigationState.instanceIndex, responses: responses, onInstanceChange: handleInstanceChange, onInstanceAdded: handleInstanceAdded, onInstanceRemoved: handleInstanceRemoved, disabled: effectiveDisabled }) })), _jsx("div", { className: "space-y-6", children: visibleVariables.map(variable => {
670
+ return (_jsxs("div", { className: "min-h-screen bg-gray-50", children: [_jsxs("div", { className: "max-w-4xl mx-auto px-4 py-6", children: [_jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-4 mb-6", children: [_jsx("h1", { className: "text-xl font-semibold text-gray-900 mb-4", children: formulaire.designation }), _jsx(FormProgress, { showPercentage: true })] }), _jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 mb-6", children: [_jsx("div", { className: "bg-green-600 px-6 py-4", children: _jsx("h2", { className: "text-xl font-bold text-white uppercase tracking-wide", children: currentGroup?.designation }) }), _jsxs("div", { className: "p-6", children: [currentGroup?.estMultiple && currentGroup.instances && (_jsx("div", { className: "mb-6", children: _jsx(GroupeInstanceTabs, { groupe: currentGroup, currentInstanceIndex: navigation.navigationState.instanceIndex, responses: responses, onInstanceChange: handleInstanceChange, onInstanceAdded: handleInstanceAdded, onInstanceRemoved: handleInstanceRemoved, disabled: effectiveDisabled, linkedGroups: GroupeInstanceManager.getLinkedGroups(currentGroup, groupesWithInstances) }) })), _jsx("div", { className: "space-y-6", children: visibleVariables.map(variable => {
629
671
  const responseKey = currentGroup?.estMultiple && currentInstance?.numeroInstance
630
672
  ? `${variable.code}_${currentInstance.numeroInstance}`
631
673
  : variable.code;
@@ -647,7 +689,7 @@ const FormRendererInner = () => {
647
689
  : responsesByVariableCode.get(variable.code)?.numeroMembre, conditionValidationError: getConditionErrorForVariable(variable.code, currentGroup?.estMultiple
648
690
  ? currentInstance?.numeroInstance
649
691
  : responsesByVariableCode.get(variable.code)?.numeroMembre), services: services, geographicComponents: geographicComponents, formulaireVariables: formulaire.variables, formulaireGroupes: groupesWithInstances.length > 0 ? groupesWithInstances : formulaire?.groupes, allResponses: responses, reponses: responses, interpolateVariableLabel: interpolateVariableLabel, rosterVariables: rosterVariables, RosterCheckInput: RosterCheckInput, RosterListInput: RosterListInput, onFillFormFromEnquete: handleFillFormFromEnquete }, variable.id));
650
- }) }), currentGroup?.estMultiple && currentGroup.instances && (_jsx("div", { className: "mt-6 pt-6 border-t", children: _jsx(GroupeInstanceTabs, { groupe: currentGroup, currentInstanceIndex: navigation.navigationState.instanceIndex, responses: responses, onInstanceChange: handleInstanceChange, onInstanceAdded: handleInstanceAdded, onInstanceRemoved: handleInstanceRemoved, disabled: effectiveDisabled }) }))] })] }), _jsx("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-4", children: _jsxs("div", { className: "flex flex-wrap justify-between items-center gap-2", children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs("button", { type: "button", onClick: handlePrevious, disabled: navigation.isFirstGroup || (!isConsultationMode && effectiveDisabled), className: `flex items-center px-4 py-2 rounded-lg font-medium transition-colors ${!navigation.isFirstGroup && (isConsultationMode || !effectiveDisabled)
692
+ }) }), currentGroup?.estMultiple && currentGroup.instances && (_jsx("div", { className: "mt-6 pt-6 border-t", children: _jsx(GroupeInstanceTabs, { groupe: currentGroup, currentInstanceIndex: navigation.navigationState.instanceIndex, responses: responses, onInstanceChange: handleInstanceChange, onInstanceAdded: handleInstanceAdded, onInstanceRemoved: handleInstanceRemoved, disabled: effectiveDisabled, linkedGroups: GroupeInstanceManager.getLinkedGroups(currentGroup, groupesWithInstances) }) }))] })] }), _jsx("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-4", children: _jsxs("div", { className: "flex flex-wrap justify-between items-center gap-2", children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs("button", { type: "button", onClick: handlePrevious, disabled: navigation.isFirstGroup || (!isConsultationMode && effectiveDisabled), className: `flex items-center px-4 py-2 rounded-lg font-medium transition-colors ${!navigation.isFirstGroup && (isConsultationMode || !effectiveDisabled)
651
693
  ? 'bg-orange-500 text-white hover:bg-orange-600'
652
694
  : 'bg-gray-100 text-gray-400 cursor-not-allowed'}`, children: [_jsx("svg", { className: "h-4 w-4 mr-2", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }), _jsx("span", { className: "hidden sm:inline", children: labels.previousButton || 'Précédent' }), _jsx("span", { className: "sm:hidden", children: "Pr\u00E9c." })] }), mode === 'admin' && features?.saveDraft && callbacks.onSaveDraft && (_jsx("button", { type: "button", onClick: handleSaveDraft, disabled: effectiveDisabled || isSavingDraft || isSubmitting || isSubmittingForm, className: `flex items-center px-4 py-2 rounded-lg font-medium transition-colors ${!effectiveDisabled && !isSavingDraft && !isSubmitting && !isSubmittingForm
653
695
  ? 'bg-yellow-500 text-white hover:bg-yellow-600'
@@ -12,6 +12,8 @@ export interface GroupeInstanceTabsProps {
12
12
  onInstanceAdded: (instance: GroupeInstance, updatedGroupe: GroupeFormulaire) => void;
13
13
  onInstanceRemoved: (instanceNumber: number, updatedGroupe: GroupeFormulaire, updatedResponses: Record<string, EnqueteReponse>) => void;
14
14
  disabled?: boolean;
15
+ /** Groupes liés par la même variable de contrôle (pour warning cross-groupe) */
16
+ linkedGroups?: GroupeFormulaire[];
15
17
  }
16
18
  declare const GroupeInstanceTabs: React.FC<GroupeInstanceTabsProps>;
17
19
  export default GroupeInstanceTabs;
@@ -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,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"}
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;IACnB,gFAAgF;IAChF,YAAY,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAED,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAkRzD,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -8,7 +8,7 @@ import { useState, useEffect } from 'react';
8
8
  import { Plus, Minus, AlertTriangle, Check, AlertCircle, AlertTriangle as ExclamationIcon } from 'lucide-react';
9
9
  import { GroupeInstanceManager } from '../../lib/utils/groupeInstanceManager';
10
10
  import ConfirmationModal from './ConfirmationModal';
11
- const GroupeInstanceTabs = ({ groupe, currentInstanceIndex, responses, onInstanceChange, onInstanceAdded, onInstanceRemoved, disabled = false }) => {
11
+ const GroupeInstanceTabs = ({ groupe, currentInstanceIndex, responses, onInstanceChange, onInstanceAdded, onInstanceRemoved, disabled = false, linkedGroups }) => {
12
12
  const [isProcessing, setIsProcessing] = useState(false);
13
13
  const [error, setError] = useState(null);
14
14
  const [forceUpdate, setForceUpdate] = useState(0);
@@ -151,6 +151,15 @@ const GroupeInstanceTabs = ({ groupe, currentInstanceIndex, responses, onInstanc
151
151
  ? 'bg-gray-100 text-gray-400 cursor-not-allowed'
152
152
  : 'bg-green-500 text-white hover:bg-green-600 focus:ring-2 focus:ring-green-500 focus:ring-offset-2'}`, title: canAdd.canProceed ? 'Ajouter une instance' : canAdd.constraints[0]?.message, children: _jsx(Plus, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: handleRemoveInstanceClick, disabled: disabled || isProcessing || !canRemove.canProceed, className: `p-2 rounded-lg transition-colors flex-shrink-0 ${disabled || isProcessing || !canRemove.canProceed
153
153
  ? 'bg-gray-100 text-gray-400 cursor-not-allowed'
154
- : 'bg-red-500 text-white hover:bg-red-600 focus:ring-2 focus:ring-red-500 focus:ring-offset-2'}`, title: canRemove.canProceed ? 'Supprimer l\'instance courante' : canRemove.constraints[0]?.message, children: _jsx(Minus, { className: "h-4 w-4" }) }), isProcessing && (_jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600 flex-shrink-0" }))] })] }), error && (_jsxs("div", { className: "mt-2 p-2 bg-red-50 border border-red-200 rounded-lg shadow-sm", children: [_jsxs("div", { className: "flex items-start space-x-2", children: [_jsx(AlertTriangle, { className: "h-4 w-4 text-red-500 flex-shrink-0 mt-0.5" }), _jsx("div", { className: "text-sm text-red-700", children: error })] }), _jsx("button", { onClick: () => setError(null), className: "absolute top-1 right-1 text-red-400 hover:text-red-600", children: "\u00D7" })] })), showDeleteConfirmation && (_jsx(ConfirmationModal, { isOpen: showDeleteConfirmation, title: "Confirmer la suppression", message: `Êtes-vous sûr de vouloir supprimer l'instance ${groupe.instances?.[currentInstanceIndex]?.numeroInstance || currentInstanceIndex + 1} ? Cette action est irréversible.`, confirmText: "Supprimer", cancelText: "Annuler", onConfirm: handleConfirmRemoveInstance, onCancel: handleCancelRemoveInstance, type: "danger" }))] }));
154
+ : 'bg-red-500 text-white hover:bg-red-600 focus:ring-2 focus:ring-red-500 focus:ring-offset-2'}`, title: canRemove.canProceed ? 'Supprimer l\'instance courante' : canRemove.constraints[0]?.message, children: _jsx(Minus, { className: "h-4 w-4" }) }), isProcessing && (_jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600 flex-shrink-0" }))] })] }), error && (_jsxs("div", { className: "mt-2 p-2 bg-red-50 border border-red-200 rounded-lg shadow-sm", children: [_jsxs("div", { className: "flex items-start space-x-2", children: [_jsx(AlertTriangle, { className: "h-4 w-4 text-red-500 flex-shrink-0 mt-0.5" }), _jsx("div", { className: "text-sm text-red-700", children: error })] }), _jsx("button", { onClick: () => setError(null), className: "absolute top-1 right-1 text-red-400 hover:text-red-600", children: "\u00D7" })] })), showDeleteConfirmation && (() => {
155
+ const instanceNum = groupe.instances?.[currentInstanceIndex]?.numeroInstance || currentInstanceIndex + 1;
156
+ const otherLinkedGroups = linkedGroups?.filter(g => g.code !== groupe.code) || [];
157
+ const hasLinkedGroups = otherLinkedGroups.length > 0;
158
+ const linkedGroupNames = otherLinkedGroups.map(g => g.designation).join(', ');
159
+ const message = hasLinkedGroups
160
+ ? `La suppression de l'instance ${instanceNum} va également la supprimer dans les groupes suivants : ${linkedGroupNames}. Les instances restantes seront renumérotées. Cette action est irréversible.`
161
+ : `Êtes-vous sûr de vouloir supprimer l'instance ${instanceNum} ? Cette action est irréversible.`;
162
+ return (_jsx(ConfirmationModal, { isOpen: showDeleteConfirmation, title: "Confirmer la suppression", message: message, confirmText: "Supprimer", cancelText: "Annuler", onConfirm: handleConfirmRemoveInstance, onCancel: handleCancelRemoveInstance, type: "danger" }));
163
+ })()] }));
155
164
  };
156
165
  export default GroupeInstanceTabs;
@@ -1 +1 @@
1
- {"version":3,"file":"useFormInstances.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormInstances.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useFormInstances.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormInstances.ts"],"names":[],"mappings":"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"}
@@ -33,7 +33,7 @@ export function useFormInstances(initialGroupes, initialResponses, options = {})
33
33
  const [groupesWithInstances, setGroupesWithInstances] = useState(() => {
34
34
  if (!initialGroupes?.length)
35
35
  return [];
36
- return initialGroupes.map(groupe => {
36
+ const initialized = initialGroupes.map(groupe => {
37
37
  if (groupe.estMultiple) {
38
38
  GroupeInstanceManager.updateGroupeProperties(groupe, initialResponses);
39
39
  if (!groupe.instances || groupe.instances.length === 0) {
@@ -45,6 +45,8 @@ export function useFormInstances(initialGroupes, initialResponses, options = {})
45
45
  }
46
46
  return groupe;
47
47
  });
48
+ // Synchroniser le nombre d'instances entre groupes liés (même codeVariable)
49
+ return GroupeInstanceManager.syncLinkedGroupInstances(initialized, initialResponses);
48
50
  });
49
51
  // Index de l'instance active
50
52
  const [currentInstanceIndex, setCurrentInstanceIndex] = useState(0);
@@ -90,60 +92,71 @@ export function useFormInstances(initialGroupes, initialResponses, options = {})
90
92
  * Ajouter une instance à un groupe
91
93
  */
92
94
  const addInstance = useCallback((groupe, responses) => {
93
- const result = GroupeInstanceManager.addInstance(groupe, responses);
94
- if (result.success && result.instance && result.updatedGroupe) {
95
- log('Instance ajoutée:', {
95
+ // Utiliser la version cross-groupe pour synchroniser tous les groupes liés
96
+ const crossResult = GroupeInstanceManager.addInstanceToLinkedGroups(groupe, groupesWithInstances, responses);
97
+ if (crossResult.success && crossResult.updatedGroupes.length > 0) {
98
+ log('Instance ajoutée (cross-groupe):', {
96
99
  groupeCode: groupe.code,
97
- instanceNumber: result.instance.numeroInstance
100
+ newInstanceNumber: crossResult.newInstanceNumber,
101
+ linkedGroupsCount: crossResult.updatedGroupes.filter(g => g.estMultiple && g.codeVariable === groupe.codeVariable).length
98
102
  });
99
- // Mettre à jour les groupes avec le nouveau groupe immutable
100
- setGroupesWithInstances(prev => {
101
- const updated = prev.map(g => {
102
- if (g.code === result.updatedGroupe.code) {
103
- return result.updatedGroupe;
104
- }
105
- return g;
106
- });
103
+ setGroupesWithInstances(() => {
107
104
  if (onGroupesUpdated) {
108
- onGroupesUpdated(updated);
105
+ onGroupesUpdated(crossResult.updatedGroupes);
109
106
  }
110
- return updated;
107
+ return crossResult.updatedGroupes;
111
108
  });
112
- if (onInstanceAdded) {
113
- onInstanceAdded(result.instance);
109
+ // Trouver la nouvelle instance dans le groupe source pour le callback
110
+ const updatedSourceGroupe = crossResult.updatedGroupes.find(g => g.code === groupe.code);
111
+ const newInstance = updatedSourceGroupe?.instances?.find(i => i.numeroInstance === crossResult.newInstanceNumber);
112
+ if (onInstanceAdded && newInstance) {
113
+ onInstanceAdded(newInstance);
114
114
  }
115
+ return {
116
+ success: true,
117
+ instance: newInstance,
118
+ updatedGroupe: updatedSourceGroupe
119
+ };
115
120
  }
116
- return result;
117
- }, [log, onInstanceAdded, onGroupesUpdated]);
121
+ return {
122
+ success: false,
123
+ error: crossResult.error || 'Impossible d\'ajouter une instance'
124
+ };
125
+ }, [log, onInstanceAdded, onGroupesUpdated, groupesWithInstances]);
118
126
  /**
119
127
  * Supprimer une instance d'un groupe
120
128
  */
121
129
  const removeInstance = useCallback((groupe, instanceNumber, responses) => {
122
- const result = GroupeInstanceManager.removeInstance(groupe, instanceNumber, responses);
123
- if (result.success && result.updatedGroupe) {
124
- log('Instance supprimée:', {
130
+ // Utiliser la version cross-groupe pour synchroniser tous les groupes liés
131
+ const crossResult = GroupeInstanceManager.removeInstanceFromLinkedGroups(groupe, instanceNumber, groupesWithInstances, responses);
132
+ if (crossResult.success) {
133
+ log('Instance supprimée (cross-groupe):', {
125
134
  groupeCode: groupe.code,
126
- instanceNumber
135
+ instanceNumber,
136
+ linkedGroupsCount: GroupeInstanceManager.getLinkedGroups(groupe, groupesWithInstances).length
127
137
  });
128
- // Mettre à jour les groupes avec le nouveau groupe immutable
129
- setGroupesWithInstances(prev => {
130
- const updated = prev.map(g => {
131
- if (g.code === result.updatedGroupe.code) {
132
- return result.updatedGroupe;
133
- }
134
- return g;
135
- });
138
+ setGroupesWithInstances(() => {
136
139
  if (onGroupesUpdated) {
137
- onGroupesUpdated(updated);
140
+ onGroupesUpdated(crossResult.updatedGroupes);
138
141
  }
139
- return updated;
142
+ return crossResult.updatedGroupes;
140
143
  });
141
144
  if (onInstanceRemoved) {
142
145
  onInstanceRemoved(instanceNumber);
143
146
  }
147
+ // Retourner les réponses mises à jour pour que le parent puisse les appliquer
148
+ const updatedSourceGroupe = crossResult.updatedGroupes.find(g => g.code === groupe.code);
149
+ return {
150
+ success: true,
151
+ updatedGroupe: updatedSourceGroupe,
152
+ updatedResponses: crossResult.updatedResponses
153
+ };
144
154
  }
145
- return result;
146
- }, [log, onInstanceRemoved, onGroupesUpdated]);
155
+ return {
156
+ success: false,
157
+ error: crossResult.error || 'Impossible de supprimer cette instance'
158
+ };
159
+ }, [log, onInstanceRemoved, onGroupesUpdated, groupesWithInstances]);
147
160
  /**
148
161
  * Changer d'instance active
149
162
  */
@@ -95,5 +95,34 @@ export declare class GroupeInstanceManager {
95
95
  reorderedInstances: GroupeInstance[];
96
96
  reorderedResponses: Record<string, EnqueteReponse>;
97
97
  };
98
+ /**
99
+ * Trouve tous les groupes multiples liés par la même codeVariable
100
+ */
101
+ static getLinkedGroups(groupe: GroupeFormulaire, allGroupes: GroupeFormulaire[]): GroupeFormulaire[];
102
+ /**
103
+ * Ajoute une instance dans TOUS les groupes liés par la même codeVariable
104
+ * Garantit la cohérence du nombre d'instances entre groupes liés
105
+ */
106
+ static addInstanceToLinkedGroups(groupe: GroupeFormulaire, allGroupes: GroupeFormulaire[], responses: Record<string, EnqueteReponse>): {
107
+ success: boolean;
108
+ updatedGroupes: GroupeFormulaire[];
109
+ newInstanceNumber?: number;
110
+ error?: string;
111
+ };
112
+ /**
113
+ * Supprime une instance dans TOUS les groupes liés + renumérote
114
+ * Garantit la cohérence de la numérotation entre groupes liés
115
+ */
116
+ static removeInstanceFromLinkedGroups(groupe: GroupeFormulaire, instanceNumber: number, allGroupes: GroupeFormulaire[], responses: Record<string, EnqueteReponse>): {
117
+ success: boolean;
118
+ updatedGroupes: GroupeFormulaire[];
119
+ updatedResponses: Record<string, EnqueteReponse>;
120
+ error?: string;
121
+ };
122
+ /**
123
+ * Synchronise le nombre d'instances entre groupes liés à l'initialisation
124
+ * Les groupes avec moins d'instances reçoivent les instances manquantes
125
+ */
126
+ static syncLinkedGroupInstances(allGroupes: GroupeFormulaire[], responses: Record<string, EnqueteReponse>): GroupeFormulaire[];
98
127
  }
99
128
  //# sourceMappingURL=groupeInstanceManager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"groupeInstanceManager.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/groupeInstanceManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"groupeInstanceManager.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/groupeInstanceManager.ts"],"names":[],"mappings":"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"}
@@ -498,4 +498,157 @@ export class GroupeInstanceManager {
498
498
  });
499
499
  return { reorderedInstances, reorderedResponses };
500
500
  }
501
+ // ============ MÉTHODES CROSS-GROUPE ============
502
+ /**
503
+ * Trouve tous les groupes multiples liés par la même codeVariable
504
+ */
505
+ static getLinkedGroups(groupe, allGroupes) {
506
+ if (!groupe.estMultiple || !groupe.codeVariable)
507
+ return [groupe];
508
+ return allGroupes.filter(g => g.estMultiple && g.codeVariable === groupe.codeVariable);
509
+ }
510
+ /**
511
+ * Ajoute une instance dans TOUS les groupes liés par la même codeVariable
512
+ * Garantit la cohérence du nombre d'instances entre groupes liés
513
+ */
514
+ static addInstanceToLinkedGroups(groupe, allGroupes, responses) {
515
+ const linkedGroups = this.getLinkedGroups(groupe, allGroupes);
516
+ // Valider que le groupe source peut ajouter
517
+ const validation = this.canAddInstance(groupe, responses);
518
+ if (!validation.canProceed) {
519
+ return {
520
+ success: false,
521
+ updatedGroupes: [],
522
+ error: validation.constraints[0]?.message || 'Impossible d\'ajouter une instance'
523
+ };
524
+ }
525
+ // Calculer le prochain numéro d'instance (max de tous les groupes liés + 1)
526
+ let maxInstanceNumber = 0;
527
+ for (const g of linkedGroups) {
528
+ const instances = g.instances || [];
529
+ for (const inst of instances) {
530
+ if (inst.numeroInstance > maxInstanceNumber) {
531
+ maxInstanceNumber = inst.numeroInstance;
532
+ }
533
+ }
534
+ }
535
+ const newInstanceNumber = maxInstanceNumber + 1;
536
+ // Ajouter l'instance dans chaque groupe lié
537
+ const updatedGroupes = allGroupes.map(g => {
538
+ const isLinked = linkedGroups.some(lg => lg.code === g.code);
539
+ if (!isLinked)
540
+ return g;
541
+ const currentInstances = g.instances || [];
542
+ const newInstance = this.createInstance(g, newInstanceNumber);
543
+ const updatedInstances = [...currentInstances, newInstance];
544
+ return {
545
+ ...g,
546
+ instances: updatedInstances,
547
+ instancesCount: updatedInstances.length
548
+ };
549
+ });
550
+ return {
551
+ success: true,
552
+ updatedGroupes,
553
+ newInstanceNumber
554
+ };
555
+ }
556
+ /**
557
+ * Supprime une instance dans TOUS les groupes liés + renumérote
558
+ * Garantit la cohérence de la numérotation entre groupes liés
559
+ */
560
+ static removeInstanceFromLinkedGroups(groupe, instanceNumber, allGroupes, responses) {
561
+ const linkedGroups = this.getLinkedGroups(groupe, allGroupes);
562
+ // Vérifier qu'au moins une instance reste après suppression
563
+ // (vérifier sur le groupe qui a le moins d'instances)
564
+ const minInstances = Math.min(...linkedGroups.map(g => g.instances?.length || 0));
565
+ if (minInstances <= 1) {
566
+ return {
567
+ success: false,
568
+ updatedGroupes: [],
569
+ updatedResponses: responses,
570
+ error: 'Au moins une instance doit être conservée'
571
+ };
572
+ }
573
+ // Appliquer la suppression + renumérotation pour chaque groupe lié
574
+ let currentResponses = { ...responses };
575
+ const updatedGroupes = allGroupes.map(g => {
576
+ const isLinked = linkedGroups.some(lg => lg.code === g.code);
577
+ if (!isLinked)
578
+ return g;
579
+ // Si cette instance n'existe pas dans ce groupe, ne rien faire
580
+ const hasInstance = g.instances?.some(i => i.numeroInstance === instanceNumber);
581
+ if (!hasInstance)
582
+ return g;
583
+ // Filtrer l'instance supprimée
584
+ const filteredInstances = (g.instances || []).filter(i => i.numeroInstance !== instanceNumber);
585
+ // Supprimer les réponses de l'instance supprimée pour ce groupe
586
+ const cleanedResponses = {};
587
+ Object.entries(currentResponses).forEach(([key, response]) => {
588
+ const belongsToGroup = g.variables.some(v => v.code === response.variableCode);
589
+ if (belongsToGroup && response.numeroMembre === instanceNumber) {
590
+ return; // Exclure
591
+ }
592
+ cleanedResponses[key] = response;
593
+ });
594
+ // Renuméroter les instances et réponses de ce groupe
595
+ const { reorderedInstances, reorderedResponses } = this.reorderInstances(filteredInstances, cleanedResponses, g);
596
+ // Mettre à jour les réponses courantes pour le prochain groupe
597
+ currentResponses = reorderedResponses;
598
+ return {
599
+ ...g,
600
+ instances: reorderedInstances,
601
+ instancesCount: reorderedInstances.length
602
+ };
603
+ });
604
+ return {
605
+ success: true,
606
+ updatedGroupes,
607
+ updatedResponses: currentResponses
608
+ };
609
+ }
610
+ /**
611
+ * Synchronise le nombre d'instances entre groupes liés à l'initialisation
612
+ * Les groupes avec moins d'instances reçoivent les instances manquantes
613
+ */
614
+ static syncLinkedGroupInstances(allGroupes, responses) {
615
+ // Grouper par codeVariable
616
+ const groupsByControlVar = new Map();
617
+ for (const g of allGroupes) {
618
+ if (g.estMultiple && g.codeVariable) {
619
+ const existing = groupsByControlVar.get(g.codeVariable) || [];
620
+ existing.push(g);
621
+ groupsByControlVar.set(g.codeVariable, existing);
622
+ }
623
+ }
624
+ // Pour chaque ensemble de groupes liés, synchroniser le nombre d'instances
625
+ const updatedGroupes = [...allGroupes];
626
+ for (const [, linkedGroups] of groupsByControlVar) {
627
+ if (linkedGroups.length <= 1)
628
+ continue;
629
+ // Trouver le max d'instances parmi les groupes liés
630
+ const maxCount = Math.max(...linkedGroups.map(g => g.instances?.length || 0));
631
+ if (maxCount <= 0)
632
+ continue;
633
+ // Ajouter les instances manquantes dans chaque groupe
634
+ for (const g of linkedGroups) {
635
+ const currentCount = g.instances?.length || 0;
636
+ if (currentCount < maxCount) {
637
+ const idx = updatedGroupes.findIndex(ug => ug.code === g.code);
638
+ if (idx === -1)
639
+ continue;
640
+ const instances = [...(updatedGroupes[idx].instances || [])];
641
+ for (let i = currentCount + 1; i <= maxCount; i++) {
642
+ instances.push(this.createInstance(g, i));
643
+ }
644
+ updatedGroupes[idx] = {
645
+ ...updatedGroupes[idx],
646
+ instances,
647
+ instancesCount: instances.length
648
+ };
649
+ }
650
+ }
651
+ }
652
+ return updatedGroupes;
653
+ }
501
654
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsuci/shared-form-components",
3
- "version": "1.0.123",
3
+ "version": "1.0.124",
4
4
  "description": "Composants partagés de rendu de formulaires RSU v2 - Package local pour frontend Admin et Public",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",