@rsuci/shared-form-components 1.0.29 → 1.0.32
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/ConfirmationModal.d.ts +20 -0
- package/dist/components/form-renderer/ConfirmationModal.d.ts.map +1 -0
- package/dist/components/form-renderer/ConfirmationModal.js +79 -0
- package/dist/components/form-renderer/FormActions.d.ts +21 -0
- package/dist/components/form-renderer/FormActions.d.ts.map +1 -0
- package/dist/components/form-renderer/FormActions.js +81 -0
- package/dist/components/form-renderer/FormNavigationButtons.d.ts +23 -0
- package/dist/components/form-renderer/FormNavigationButtons.d.ts.map +1 -0
- package/dist/components/form-renderer/FormNavigationButtons.js +35 -0
- package/dist/components/form-renderer/FormProgress.d.ts +19 -0
- package/dist/components/form-renderer/FormProgress.d.ts.map +1 -0
- package/dist/components/form-renderer/FormProgress.js +26 -0
- package/dist/components/form-renderer/FormRenderer.d.ts +39 -0
- package/dist/components/form-renderer/FormRenderer.d.ts.map +1 -0
- package/dist/components/form-renderer/FormRenderer.js +113 -0
- package/dist/components/form-renderer/FormRendererContext.d.ts +109 -0
- package/dist/components/form-renderer/FormRendererContext.d.ts.map +1 -0
- package/dist/components/form-renderer/FormRendererContext.js +114 -0
- package/dist/components/form-renderer/GroupeInstanceTabs.d.ts +18 -0
- package/dist/components/form-renderer/GroupeInstanceTabs.d.ts.map +1 -0
- package/dist/components/form-renderer/GroupeInstanceTabs.js +174 -0
- package/dist/components/form-renderer/index.d.ts +17 -0
- package/dist/components/form-renderer/index.d.ts.map +1 -0
- package/dist/components/form-renderer/index.js +23 -0
- package/dist/hooks/useFormInstances.d.ts +87 -0
- package/dist/hooks/useFormInstances.d.ts.map +1 -0
- package/dist/hooks/useFormInstances.js +197 -0
- package/dist/hooks/useFormNavigation.d.ts +72 -0
- package/dist/hooks/useFormNavigation.d.ts.map +1 -0
- package/dist/hooks/useFormNavigation.js +147 -0
- package/dist/hooks/useFormRenderer.d.ts +87 -0
- package/dist/hooks/useFormRenderer.d.ts.map +1 -0
- package/dist/hooks/useFormRenderer.js +177 -0
- package/dist/hooks/useFormValidation.d.ts +50 -0
- package/dist/hooks/useFormValidation.d.ts.map +1 -0
- package/dist/hooks/useFormValidation.js +175 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -0
- package/dist/lib/__tests__/date-functions.test.d.ts +5 -0
- package/dist/lib/__tests__/date-functions.test.d.ts.map +1 -0
- package/dist/lib/__tests__/date-functions.test.js +184 -0
- package/dist/lib/utils/groupeInstanceManager.d.ts +88 -0
- package/dist/lib/utils/groupeInstanceManager.d.ts.map +1 -0
- package/dist/lib/utils/groupeInstanceManager.js +606 -0
- package/dist/types/form-renderer.d.ts +115 -1
- package/dist/types/form-renderer.d.ts.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contexte React pour le FormRenderer
|
|
3
|
+
* Partage l'état et les fonctions entre tous les sous-composants
|
|
4
|
+
* RSU v2 - Package Partagé
|
|
5
|
+
*/
|
|
6
|
+
'use client';
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
9
|
+
import { useFormRenderer } from '../../hooks/useFormRenderer';
|
|
10
|
+
// Création du contexte avec valeur par défaut null
|
|
11
|
+
const FormRendererContext = createContext(null);
|
|
12
|
+
/**
|
|
13
|
+
* Provider pour le contexte FormRenderer
|
|
14
|
+
* Encapsule useFormRenderer et fournit l'état à tous les enfants
|
|
15
|
+
*/
|
|
16
|
+
export const FormRendererProvider = ({ children, formulaire, enquete, initialResponses, config, callbacks, services, geographicComponents, disabled, isSubmitting: externalIsSubmitting, isVariableVisible, debug }) => {
|
|
17
|
+
// Utiliser le hook principal
|
|
18
|
+
const formRenderer = useFormRenderer(formulaire, enquete, {
|
|
19
|
+
config,
|
|
20
|
+
initialResponses: initialResponses || enquete?.reponses,
|
|
21
|
+
onResponsesChange: callbacks.onResponsesChange,
|
|
22
|
+
onGroupChange: callbacks.onGroupChange,
|
|
23
|
+
isVariableVisible,
|
|
24
|
+
debug
|
|
25
|
+
});
|
|
26
|
+
// Construire la valeur du contexte
|
|
27
|
+
const contextValue = useMemo(() => ({
|
|
28
|
+
...formRenderer,
|
|
29
|
+
// Données initiales
|
|
30
|
+
formulaire,
|
|
31
|
+
enquete,
|
|
32
|
+
// Configuration
|
|
33
|
+
config,
|
|
34
|
+
// Callbacks
|
|
35
|
+
callbacks,
|
|
36
|
+
// Services
|
|
37
|
+
services,
|
|
38
|
+
geographicComponents,
|
|
39
|
+
// Surcharges externes
|
|
40
|
+
isSubmitting: externalIsSubmitting ?? formRenderer.isSubmitting,
|
|
41
|
+
effectiveDisabled: disabled ?? formRenderer.effectiveDisabled
|
|
42
|
+
}), [
|
|
43
|
+
formRenderer,
|
|
44
|
+
formulaire,
|
|
45
|
+
enquete,
|
|
46
|
+
config,
|
|
47
|
+
callbacks,
|
|
48
|
+
services,
|
|
49
|
+
geographicComponents,
|
|
50
|
+
externalIsSubmitting,
|
|
51
|
+
disabled
|
|
52
|
+
]);
|
|
53
|
+
return (_jsx(FormRendererContext.Provider, { value: contextValue, children: children }));
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Hook pour accéder au contexte FormRenderer
|
|
57
|
+
* Doit être utilisé à l'intérieur d'un FormRendererProvider
|
|
58
|
+
*/
|
|
59
|
+
export function useFormRendererContext() {
|
|
60
|
+
const context = useContext(FormRendererContext);
|
|
61
|
+
if (!context) {
|
|
62
|
+
throw new Error('useFormRendererContext doit être utilisé à l\'intérieur d\'un FormRendererProvider');
|
|
63
|
+
}
|
|
64
|
+
return context;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Hook pour accéder uniquement à la navigation
|
|
68
|
+
*/
|
|
69
|
+
export function useFormRendererNavigation() {
|
|
70
|
+
const { navigation } = useFormRendererContext();
|
|
71
|
+
return navigation;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Hook pour accéder uniquement à la validation
|
|
75
|
+
*/
|
|
76
|
+
export function useFormRendererValidation() {
|
|
77
|
+
const { validation } = useFormRendererContext();
|
|
78
|
+
return validation;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Hook pour accéder uniquement aux instances
|
|
82
|
+
*/
|
|
83
|
+
export function useFormRendererInstances() {
|
|
84
|
+
const { instances } = useFormRendererContext();
|
|
85
|
+
return instances;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Hook pour accéder à la configuration
|
|
89
|
+
*/
|
|
90
|
+
export function useFormRendererConfig() {
|
|
91
|
+
const { mode, features, config } = useFormRendererContext();
|
|
92
|
+
return { mode, features, config };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Hook pour accéder aux réponses
|
|
96
|
+
*/
|
|
97
|
+
export function useFormRendererResponses() {
|
|
98
|
+
const { responses, updateResponse, getResponseValue } = useFormRendererContext();
|
|
99
|
+
return { responses, updateResponse, getResponseValue };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Hook pour accéder à l'état global
|
|
103
|
+
*/
|
|
104
|
+
export function useFormRendererState() {
|
|
105
|
+
const { hasUnsavedChanges, isSubmitting, effectiveDisabled, isReadOnly, isConsultationMode } = useFormRendererContext();
|
|
106
|
+
return {
|
|
107
|
+
hasUnsavedChanges,
|
|
108
|
+
isSubmitting,
|
|
109
|
+
effectiveDisabled,
|
|
110
|
+
isReadOnly,
|
|
111
|
+
isConsultationMode
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export default FormRendererContext;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composant d'interface à onglets pour les instances multiples de groupes
|
|
3
|
+
* RSU v2 - Moteur de Rendu des Formulaires d'Enquête
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { GroupeFormulaire, EnqueteReponse, GroupeInstance } from '../../types/enquete';
|
|
7
|
+
export interface GroupeInstanceTabsProps {
|
|
8
|
+
groupe: GroupeFormulaire;
|
|
9
|
+
currentInstanceIndex: number;
|
|
10
|
+
responses: Record<string, EnqueteReponse>;
|
|
11
|
+
onInstanceChange: (instanceIndex: number) => void;
|
|
12
|
+
onInstanceAdded: (instance: GroupeInstance) => void;
|
|
13
|
+
onInstanceRemoved: (instanceNumber: number) => void;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
declare const GroupeInstanceTabs: React.FC<GroupeInstanceTabsProps>;
|
|
17
|
+
export default GroupeInstanceTabs;
|
|
18
|
+
//# sourceMappingURL=GroupeInstanceTabs.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composant d'interface à onglets pour les instances multiples de groupes
|
|
3
|
+
* RSU v2 - Moteur de Rendu des Formulaires d'Enquête
|
|
4
|
+
*/
|
|
5
|
+
'use client';
|
|
6
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { useState, useEffect } from 'react';
|
|
8
|
+
import { Plus, Minus, AlertTriangle, Check, AlertCircle, AlertTriangle as ExclamationIcon } from 'lucide-react';
|
|
9
|
+
import { GroupeInstanceManager } from '../../lib/utils/groupeInstanceManager';
|
|
10
|
+
import ConfirmationModal from './ConfirmationModal';
|
|
11
|
+
const GroupeInstanceTabs = ({ groupe, currentInstanceIndex, responses, onInstanceChange, onInstanceAdded, onInstanceRemoved, disabled = false }) => {
|
|
12
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
13
|
+
const [error, setError] = useState(null);
|
|
14
|
+
const [forceUpdate, setForceUpdate] = useState(0);
|
|
15
|
+
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
|
|
16
|
+
// Ne pas afficher pour les groupes non multiples
|
|
17
|
+
if (!groupe.estMultiple || !groupe.instances) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
// Calculer les informations sur les instances
|
|
21
|
+
const currentCount = GroupeInstanceManager.getInstancesCount(groupe, responses);
|
|
22
|
+
const maxInstances = GroupeInstanceManager.getMaxInstances(groupe, responses);
|
|
23
|
+
const canAdd = GroupeInstanceManager.canAddInstance(groupe, responses);
|
|
24
|
+
const canRemove = GroupeInstanceManager.canRemoveInstance(groupe, responses);
|
|
25
|
+
// CORRECTION: Détecter les changements de maxInstances et forcer le re-rendu
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const newMaxInstances = GroupeInstanceManager.getMaxInstances(groupe, responses);
|
|
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
|
+
setForceUpdate(prev => prev + 1);
|
|
36
|
+
}
|
|
37
|
+
}, [responses, groupe.codeVariable, groupe.maxInstances]);
|
|
38
|
+
// PROTECTION DES DONNÉES: Afficher toutes les instances existantes, même si la variable de contrôle est plus petite
|
|
39
|
+
// Le nombre d'onglets affichés = Math.max(maxInstances, instances existantes)
|
|
40
|
+
const minDisplayCount = Math.max(maxInstances, groupe.instances.length);
|
|
41
|
+
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
|
+
// Fonction pour déterminer l'état d'une instance
|
|
55
|
+
const getInstanceState = (instance) => {
|
|
56
|
+
const responseCount = Object.keys(instance.reponses).length;
|
|
57
|
+
if (responseCount === 0) {
|
|
58
|
+
return 'empty';
|
|
59
|
+
}
|
|
60
|
+
if (GroupeInstanceManager.isInstanceComplete(instance, groupe)) {
|
|
61
|
+
return 'complete';
|
|
62
|
+
}
|
|
63
|
+
return 'incomplete';
|
|
64
|
+
};
|
|
65
|
+
// Fonction pour obtenir l'icône et la couleur selon l'état
|
|
66
|
+
const getInstanceDisplay = (instance, isActive) => {
|
|
67
|
+
const state = getInstanceState(instance);
|
|
68
|
+
const baseClasses = `
|
|
69
|
+
flex items-center justify-center min-w-[3rem] h-10 px-3 rounded-lg font-medium text-sm
|
|
70
|
+
transition-all duration-200 cursor-pointer border-2
|
|
71
|
+
`;
|
|
72
|
+
let stateClasses = '';
|
|
73
|
+
let icon = null;
|
|
74
|
+
switch (state) {
|
|
75
|
+
case 'complete':
|
|
76
|
+
stateClasses = isActive
|
|
77
|
+
? 'bg-blue-600 text-white border-blue-600 shadow-lg'
|
|
78
|
+
: 'bg-green-100 text-green-700 border-green-300 hover:bg-green-200';
|
|
79
|
+
icon = _jsx(Check, { className: "h-4 w-4 ml-1" });
|
|
80
|
+
break;
|
|
81
|
+
case 'incomplete':
|
|
82
|
+
stateClasses = isActive
|
|
83
|
+
? 'bg-blue-600 text-white border-blue-600 shadow-lg'
|
|
84
|
+
: 'bg-green-100 text-orange-700 border-green-300 hover:bg-green-200';
|
|
85
|
+
icon = _jsx(AlertCircle, { className: "h-4 w-4 ml-1" });
|
|
86
|
+
break;
|
|
87
|
+
case 'empty':
|
|
88
|
+
stateClasses = isActive
|
|
89
|
+
? 'bg-blue-600 text-white border-blue-600 shadow-lg'
|
|
90
|
+
: 'bg-gray-600 text-white border-gray-600 hover:bg-gray-700';
|
|
91
|
+
icon = _jsx(ExclamationIcon, { className: "h-4 w-4 ml-1" });
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
className: `${baseClasses} ${stateClasses}`,
|
|
96
|
+
icon
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
const handleInstanceClick = (instanceIndex) => {
|
|
100
|
+
// En mode consultation, permettre la navigation entre onglets mais pas les modifications
|
|
101
|
+
if (isProcessing)
|
|
102
|
+
return;
|
|
103
|
+
onInstanceChange(instanceIndex);
|
|
104
|
+
};
|
|
105
|
+
const handleAddInstance = async () => {
|
|
106
|
+
if (disabled || isProcessing)
|
|
107
|
+
return;
|
|
108
|
+
setIsProcessing(true);
|
|
109
|
+
setError(null);
|
|
110
|
+
try {
|
|
111
|
+
const result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
112
|
+
if (result.success && result.instance) {
|
|
113
|
+
onInstanceAdded(result.instance);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
setError(result.error || 'Erreur lors de l\'ajout de l\'instance');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
setError('Erreur inattendue lors de l\'ajout de l\'instance');
|
|
121
|
+
console.error('Erreur ajout instance:', err);
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
setIsProcessing(false);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
const handleRemoveInstanceClick = () => {
|
|
128
|
+
if (disabled || isProcessing || !groupe.instances)
|
|
129
|
+
return;
|
|
130
|
+
setShowDeleteConfirmation(true);
|
|
131
|
+
};
|
|
132
|
+
const handleConfirmRemoveInstance = async () => {
|
|
133
|
+
if (disabled || isProcessing || !groupe.instances)
|
|
134
|
+
return;
|
|
135
|
+
setShowDeleteConfirmation(false);
|
|
136
|
+
setIsProcessing(true);
|
|
137
|
+
setError(null);
|
|
138
|
+
try {
|
|
139
|
+
// Supprimer l'instance actuellement affichée (correction du bug)
|
|
140
|
+
const currentInstance = groupe.instances[currentInstanceIndex];
|
|
141
|
+
if (!currentInstance) {
|
|
142
|
+
setError('Aucune instance sélectionnée à supprimer');
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const result = GroupeInstanceManager.removeInstance(groupe, currentInstance.numeroInstance, responses);
|
|
146
|
+
if (result.success) {
|
|
147
|
+
onInstanceRemoved(currentInstance.numeroInstance);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
setError(result.error || 'Erreur lors de la suppression de l\'instance');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
setError('Erreur inattendue lors de la suppression de l\'instance');
|
|
155
|
+
console.error('Erreur suppression instance:', err);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
setIsProcessing(false);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const handleCancelRemoveInstance = () => {
|
|
162
|
+
setShowDeleteConfirmation(false);
|
|
163
|
+
};
|
|
164
|
+
return (_jsxs("div", { className: "w-full", children: [_jsxs("div", { className: "flex items-center space-x-2 overflow-x-auto pb-2 relative", children: [_jsx("div", { className: "flex space-x-2 flex-shrink-0", children: displayedInstances.map((instance, index) => {
|
|
165
|
+
const isActive = index === currentInstanceIndex;
|
|
166
|
+
const display = getInstanceDisplay(instance, isActive);
|
|
167
|
+
return (_jsxs("button", { onClick: () => handleInstanceClick(index), disabled: isProcessing, className: display.className, title: `Instance ${instance.numeroInstance} - ${getInstanceState(instance) === 'complete' ? 'Complète' : getInstanceState(instance) === 'incomplete' ? 'Incomplète' : 'Vide'}`, children: [_jsx("span", { className: "font-semibold", children: instance.numeroInstance }), display.icon] }, instance.numeroInstance));
|
|
168
|
+
}) }), _jsx("div", { className: "h-6 w-px bg-gray-300 flex-shrink-0" }), _jsxs("div", { className: "flex items-center space-x-2 flex-shrink-0", children: [_jsx("button", { type: "button", onClick: handleAddInstance, disabled: disabled || isProcessing || !canAdd.canProceed, className: `p-2 rounded-lg transition-colors flex-shrink-0 ${disabled || isProcessing || !canAdd.canProceed
|
|
169
|
+
? 'bg-gray-100 text-gray-400 cursor-not-allowed'
|
|
170
|
+
: '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
|
|
171
|
+
? 'bg-gray-100 text-gray-400 cursor-not-allowed'
|
|
172
|
+
: '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" }))] }));
|
|
173
|
+
};
|
|
174
|
+
export default GroupeInstanceTabs;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exports du module form-renderer
|
|
3
|
+
* RSU v2 - Composants partagés de rendu de formulaires
|
|
4
|
+
*/
|
|
5
|
+
export { default as ConfirmationModal } from './ConfirmationModal';
|
|
6
|
+
export type { ConfirmationModalProps, ConfirmationModalType } from './ConfirmationModal';
|
|
7
|
+
export { default as GroupeInstanceTabs } from './GroupeInstanceTabs';
|
|
8
|
+
export type { GroupeInstanceTabsProps } from './GroupeInstanceTabs';
|
|
9
|
+
export { FormRendererProvider, useFormRendererContext, useFormRendererNavigation, useFormRendererValidation, useFormRendererInstances, useFormRendererConfig, useFormRendererResponses, useFormRendererState, type FormRendererContextValue, type FormRendererProviderProps } from './FormRendererContext';
|
|
10
|
+
export { default as FormProgress } from './FormProgress';
|
|
11
|
+
export type { FormProgressProps } from './FormProgress';
|
|
12
|
+
export { default as FormNavigationButtons } from './FormNavigationButtons';
|
|
13
|
+
export type { FormNavigationButtonsProps } from './FormNavigationButtons';
|
|
14
|
+
export { default as FormActions } from './FormActions';
|
|
15
|
+
export type { FormActionsProps } from './FormActions';
|
|
16
|
+
export { default as FormRenderer, type FormRendererProps } from './FormRenderer';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/form-renderer/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACnE,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEzF,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAMpE,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC/B,MAAM,uBAAuB,CAAC;AAM/B,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzD,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAMtD,OAAO,EACL,OAAO,IAAI,YAAY,EACvB,KAAK,iBAAiB,EACvB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exports du module form-renderer
|
|
3
|
+
* RSU v2 - Composants partagés de rendu de formulaires
|
|
4
|
+
*/
|
|
5
|
+
// ========================================
|
|
6
|
+
// COMPOSANTS UI DE BASE
|
|
7
|
+
// ========================================
|
|
8
|
+
export { default as ConfirmationModal } from './ConfirmationModal';
|
|
9
|
+
export { default as GroupeInstanceTabs } from './GroupeInstanceTabs';
|
|
10
|
+
// ========================================
|
|
11
|
+
// CONTEXTE ET PROVIDER
|
|
12
|
+
// ========================================
|
|
13
|
+
export { FormRendererProvider, useFormRendererContext, useFormRendererNavigation, useFormRendererValidation, useFormRendererInstances, useFormRendererConfig, useFormRendererResponses, useFormRendererState } from './FormRendererContext';
|
|
14
|
+
// ========================================
|
|
15
|
+
// SOUS-COMPOSANTS DU FORM RENDERER
|
|
16
|
+
// ========================================
|
|
17
|
+
export { default as FormProgress } from './FormProgress';
|
|
18
|
+
export { default as FormNavigationButtons } from './FormNavigationButtons';
|
|
19
|
+
export { default as FormActions } from './FormActions';
|
|
20
|
+
// ========================================
|
|
21
|
+
// COMPOSANT PRINCIPAL FORM RENDERER
|
|
22
|
+
// ========================================
|
|
23
|
+
export { default as FormRenderer } from './FormRenderer';
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook de gestion des instances multiples pour les formulaires d'enquête
|
|
3
|
+
* Gère l'ajout, la suppression et la synchronisation des instances de groupes
|
|
4
|
+
* RSU v2 - Package Partagé
|
|
5
|
+
*/
|
|
6
|
+
import { GroupeFormulaire, GroupeInstance, EnqueteReponse } from '../types/enquete';
|
|
7
|
+
export interface UseFormInstancesOptions {
|
|
8
|
+
/** Callback appelé lors de l'ajout d'une instance */
|
|
9
|
+
onInstanceAdded?: (instance: GroupeInstance) => void;
|
|
10
|
+
/** Callback appelé lors de la suppression d'une instance */
|
|
11
|
+
onInstanceRemoved?: (instanceNumber: number) => void;
|
|
12
|
+
/** Callback appelé lors du changement d'instance active */
|
|
13
|
+
onInstanceChange?: (instanceIndex: number) => void;
|
|
14
|
+
/** Callback appelé lors de la mise à jour des groupes */
|
|
15
|
+
onGroupesUpdated?: (groupes: GroupeFormulaire[]) => void;
|
|
16
|
+
/** Mode debug */
|
|
17
|
+
debug?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface UseFormInstancesResult {
|
|
20
|
+
/** Groupes avec leurs instances initialisées */
|
|
21
|
+
groupesWithInstances: GroupeFormulaire[];
|
|
22
|
+
/** Index de l'instance actuellement active */
|
|
23
|
+
currentInstanceIndex: number;
|
|
24
|
+
/** Instance actuellement active */
|
|
25
|
+
currentInstance: GroupeInstance | undefined;
|
|
26
|
+
/** Ajouter une instance au groupe actuel */
|
|
27
|
+
addInstance: (groupe: GroupeFormulaire, responses: Record<string, EnqueteReponse>) => {
|
|
28
|
+
success: boolean;
|
|
29
|
+
instance?: GroupeInstance;
|
|
30
|
+
error?: string;
|
|
31
|
+
};
|
|
32
|
+
/** Supprimer une instance du groupe actuel */
|
|
33
|
+
removeInstance: (groupe: GroupeFormulaire, instanceNumber: number, responses: Record<string, EnqueteReponse>) => {
|
|
34
|
+
success: boolean;
|
|
35
|
+
error?: string;
|
|
36
|
+
};
|
|
37
|
+
/** Changer d'instance active */
|
|
38
|
+
changeInstance: (instanceIndex: number) => void;
|
|
39
|
+
/** Synchroniser les réponses avec l'instance */
|
|
40
|
+
syncInstanceResponses: (instance: GroupeInstance, groupe: GroupeFormulaire, allResponses: Record<string, EnqueteReponse>) => void;
|
|
41
|
+
/** Mettre à jour les propriétés d'un groupe */
|
|
42
|
+
updateGroupeProperties: (groupe: GroupeFormulaire, responses: Record<string, EnqueteReponse>) => void;
|
|
43
|
+
/** Initialiser les groupes avec instances */
|
|
44
|
+
initializeGroupesWithInstances: (groupes: GroupeFormulaire[], responses: Record<string, EnqueteReponse>) => GroupeFormulaire[];
|
|
45
|
+
/** Vérifier si une instance est complète */
|
|
46
|
+
isInstanceComplete: (instance: GroupeInstance, groupe: GroupeFormulaire) => boolean;
|
|
47
|
+
/** Peut-on ajouter une instance ? */
|
|
48
|
+
canAddInstance: (groupe: GroupeFormulaire, responses: Record<string, EnqueteReponse>) => {
|
|
49
|
+
canProceed: boolean;
|
|
50
|
+
constraints: Array<{
|
|
51
|
+
message: string;
|
|
52
|
+
}>;
|
|
53
|
+
};
|
|
54
|
+
/** Peut-on supprimer une instance ? */
|
|
55
|
+
canRemoveInstance: (groupe: GroupeFormulaire, responses: Record<string, EnqueteReponse>) => {
|
|
56
|
+
canProceed: boolean;
|
|
57
|
+
constraints: Array<{
|
|
58
|
+
message: string;
|
|
59
|
+
}>;
|
|
60
|
+
};
|
|
61
|
+
/** Mettre à jour les groupes avec instances */
|
|
62
|
+
setGroupesWithInstances: React.Dispatch<React.SetStateAction<GroupeFormulaire[]>>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Hook pour gérer les instances multiples dans un formulaire d'enquête
|
|
66
|
+
*
|
|
67
|
+
* @param initialGroupes - Liste initiale des groupes du formulaire
|
|
68
|
+
* @param initialResponses - Réponses initiales
|
|
69
|
+
* @param options - Options de configuration
|
|
70
|
+
* @returns Objet contenant l'état et les fonctions de gestion des instances
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```tsx
|
|
74
|
+
* const {
|
|
75
|
+
* groupesWithInstances,
|
|
76
|
+
* addInstance,
|
|
77
|
+
* removeInstance,
|
|
78
|
+
* currentInstance
|
|
79
|
+
* } = useFormInstances(formulaire.groupes, enquete.reponses, {
|
|
80
|
+
* onInstanceAdded: (instance) => console.log('Added:', instance),
|
|
81
|
+
* onInstanceRemoved: (num) => console.log('Removed:', num)
|
|
82
|
+
* });
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare function useFormInstances(initialGroupes: GroupeFormulaire[], initialResponses: Record<string, EnqueteReponse>, options?: UseFormInstancesOptions): UseFormInstancesResult;
|
|
86
|
+
export default useFormInstances;
|
|
87
|
+
//# sourceMappingURL=useFormInstances.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFormInstances.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormInstances.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook de gestion des instances multiples pour les formulaires d'enquête
|
|
3
|
+
* Gère l'ajout, la suppression et la synchronisation des instances de groupes
|
|
4
|
+
* RSU v2 - Package Partagé
|
|
5
|
+
*/
|
|
6
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
7
|
+
import { GroupeInstanceManager } from '../lib/utils/groupeInstanceManager';
|
|
8
|
+
/**
|
|
9
|
+
* Hook pour gérer les instances multiples dans un formulaire d'enquête
|
|
10
|
+
*
|
|
11
|
+
* @param initialGroupes - Liste initiale des groupes du formulaire
|
|
12
|
+
* @param initialResponses - Réponses initiales
|
|
13
|
+
* @param options - Options de configuration
|
|
14
|
+
* @returns Objet contenant l'état et les fonctions de gestion des instances
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* const {
|
|
19
|
+
* groupesWithInstances,
|
|
20
|
+
* addInstance,
|
|
21
|
+
* removeInstance,
|
|
22
|
+
* currentInstance
|
|
23
|
+
* } = useFormInstances(formulaire.groupes, enquete.reponses, {
|
|
24
|
+
* onInstanceAdded: (instance) => console.log('Added:', instance),
|
|
25
|
+
* onInstanceRemoved: (num) => console.log('Removed:', num)
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function useFormInstances(initialGroupes, initialResponses, options = {}) {
|
|
30
|
+
const { onInstanceAdded, onInstanceRemoved, onInstanceChange, onGroupesUpdated, debug = false } = options;
|
|
31
|
+
// État des groupes avec instances
|
|
32
|
+
const [groupesWithInstances, setGroupesWithInstances] = useState([]);
|
|
33
|
+
// Index de l'instance active
|
|
34
|
+
const [currentInstanceIndex, setCurrentInstanceIndex] = useState(0);
|
|
35
|
+
// Fonction utilitaire pour logger en mode debug
|
|
36
|
+
const log = useCallback((message, data) => {
|
|
37
|
+
if (debug) {
|
|
38
|
+
console.log(`[FormInstances] ${message}`, data || '');
|
|
39
|
+
}
|
|
40
|
+
}, [debug]);
|
|
41
|
+
/**
|
|
42
|
+
* Initialiser les groupes avec leurs instances
|
|
43
|
+
*/
|
|
44
|
+
const initializeGroupesWithInstances = useCallback((groupes, responses) => {
|
|
45
|
+
if (!groupes)
|
|
46
|
+
return [];
|
|
47
|
+
log('Initialisation des groupes multiples:', {
|
|
48
|
+
groupesCount: groupes.length,
|
|
49
|
+
responsesCount: Object.keys(responses).length
|
|
50
|
+
});
|
|
51
|
+
return groupes.map(groupe => {
|
|
52
|
+
if (groupe.estMultiple) {
|
|
53
|
+
log('Traitement groupe multiple:', {
|
|
54
|
+
groupeCode: groupe.code,
|
|
55
|
+
codeVariable: groupe.codeVariable,
|
|
56
|
+
designation: groupe.designation
|
|
57
|
+
});
|
|
58
|
+
// Initialiser les propriétés pour les groupes multiples
|
|
59
|
+
GroupeInstanceManager.updateGroupeProperties(groupe, responses);
|
|
60
|
+
// S'assurer qu'il y a toujours au moins une instance
|
|
61
|
+
if (!groupe.instances || groupe.instances.length === 0) {
|
|
62
|
+
groupe.instances = [GroupeInstanceManager.createInstance(groupe, 1)];
|
|
63
|
+
}
|
|
64
|
+
// Synchroniser les réponses avec chaque instance
|
|
65
|
+
groupe.instances.forEach((instance, index) => {
|
|
66
|
+
GroupeInstanceManager.syncInstanceResponses(instance, groupe, responses);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return groupe;
|
|
70
|
+
});
|
|
71
|
+
}, [log]);
|
|
72
|
+
// Initialisation au montage
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (initialGroupes?.length) {
|
|
75
|
+
const initialized = initializeGroupesWithInstances(initialGroupes, initialResponses);
|
|
76
|
+
setGroupesWithInstances(initialized);
|
|
77
|
+
}
|
|
78
|
+
}, []);
|
|
79
|
+
/**
|
|
80
|
+
* Ajouter une instance à un groupe
|
|
81
|
+
*/
|
|
82
|
+
const addInstance = useCallback((groupe, responses) => {
|
|
83
|
+
const result = GroupeInstanceManager.addInstance(groupe, responses);
|
|
84
|
+
if (result.success && result.instance) {
|
|
85
|
+
log('Instance ajoutée:', {
|
|
86
|
+
groupeCode: groupe.code,
|
|
87
|
+
instanceNumber: result.instance.numeroInstance
|
|
88
|
+
});
|
|
89
|
+
// Mettre à jour les groupes
|
|
90
|
+
setGroupesWithInstances(prev => {
|
|
91
|
+
const updated = prev.map(g => {
|
|
92
|
+
if (g.code === groupe.code) {
|
|
93
|
+
return { ...groupe };
|
|
94
|
+
}
|
|
95
|
+
return g;
|
|
96
|
+
});
|
|
97
|
+
if (onGroupesUpdated) {
|
|
98
|
+
onGroupesUpdated(updated);
|
|
99
|
+
}
|
|
100
|
+
return updated;
|
|
101
|
+
});
|
|
102
|
+
if (onInstanceAdded) {
|
|
103
|
+
onInstanceAdded(result.instance);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}, [log, onInstanceAdded, onGroupesUpdated]);
|
|
108
|
+
/**
|
|
109
|
+
* Supprimer une instance d'un groupe
|
|
110
|
+
*/
|
|
111
|
+
const removeInstance = useCallback((groupe, instanceNumber, responses) => {
|
|
112
|
+
const result = GroupeInstanceManager.removeInstance(groupe, instanceNumber, responses);
|
|
113
|
+
if (result.success) {
|
|
114
|
+
log('Instance supprimée:', {
|
|
115
|
+
groupeCode: groupe.code,
|
|
116
|
+
instanceNumber
|
|
117
|
+
});
|
|
118
|
+
// Mettre à jour les groupes
|
|
119
|
+
setGroupesWithInstances(prev => {
|
|
120
|
+
const updated = prev.map(g => {
|
|
121
|
+
if (g.code === groupe.code) {
|
|
122
|
+
return { ...groupe };
|
|
123
|
+
}
|
|
124
|
+
return g;
|
|
125
|
+
});
|
|
126
|
+
if (onGroupesUpdated) {
|
|
127
|
+
onGroupesUpdated(updated);
|
|
128
|
+
}
|
|
129
|
+
return updated;
|
|
130
|
+
});
|
|
131
|
+
if (onInstanceRemoved) {
|
|
132
|
+
onInstanceRemoved(instanceNumber);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}, [log, onInstanceRemoved, onGroupesUpdated]);
|
|
137
|
+
/**
|
|
138
|
+
* Changer d'instance active
|
|
139
|
+
*/
|
|
140
|
+
const changeInstance = useCallback((instanceIndex) => {
|
|
141
|
+
setCurrentInstanceIndex(instanceIndex);
|
|
142
|
+
if (onInstanceChange) {
|
|
143
|
+
onInstanceChange(instanceIndex);
|
|
144
|
+
}
|
|
145
|
+
}, [onInstanceChange]);
|
|
146
|
+
/**
|
|
147
|
+
* Synchroniser les réponses avec l'instance
|
|
148
|
+
*/
|
|
149
|
+
const syncInstanceResponses = useCallback((instance, groupe, allResponses) => {
|
|
150
|
+
GroupeInstanceManager.syncInstanceResponses(instance, groupe, allResponses);
|
|
151
|
+
}, []);
|
|
152
|
+
/**
|
|
153
|
+
* Mettre à jour les propriétés d'un groupe
|
|
154
|
+
*/
|
|
155
|
+
const updateGroupeProperties = useCallback((groupe, responses) => {
|
|
156
|
+
GroupeInstanceManager.updateGroupeProperties(groupe, responses);
|
|
157
|
+
}, []);
|
|
158
|
+
/**
|
|
159
|
+
* Vérifier si une instance est complète
|
|
160
|
+
*/
|
|
161
|
+
const isInstanceComplete = useCallback((instance, groupe) => {
|
|
162
|
+
return GroupeInstanceManager.isInstanceComplete(instance, groupe);
|
|
163
|
+
}, []);
|
|
164
|
+
/**
|
|
165
|
+
* Peut-on ajouter une instance ?
|
|
166
|
+
*/
|
|
167
|
+
const canAddInstance = useCallback((groupe, responses) => {
|
|
168
|
+
return GroupeInstanceManager.canAddInstance(groupe, responses);
|
|
169
|
+
}, []);
|
|
170
|
+
/**
|
|
171
|
+
* Peut-on supprimer une instance ?
|
|
172
|
+
*/
|
|
173
|
+
const canRemoveInstance = useCallback((groupe, responses) => {
|
|
174
|
+
return GroupeInstanceManager.canRemoveInstance(groupe, responses);
|
|
175
|
+
}, []);
|
|
176
|
+
/**
|
|
177
|
+
* Instance actuellement active
|
|
178
|
+
*/
|
|
179
|
+
const currentInstance = groupesWithInstances.find(g => g.estMultiple)
|
|
180
|
+
?.instances?.[currentInstanceIndex];
|
|
181
|
+
return {
|
|
182
|
+
groupesWithInstances,
|
|
183
|
+
currentInstanceIndex,
|
|
184
|
+
currentInstance,
|
|
185
|
+
addInstance,
|
|
186
|
+
removeInstance,
|
|
187
|
+
changeInstance,
|
|
188
|
+
syncInstanceResponses,
|
|
189
|
+
updateGroupeProperties,
|
|
190
|
+
initializeGroupesWithInstances,
|
|
191
|
+
isInstanceComplete,
|
|
192
|
+
canAddInstance,
|
|
193
|
+
canRemoveInstance,
|
|
194
|
+
setGroupesWithInstances
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
export default useFormInstances;
|