@rsuci/shared-form-components 1.0.29 → 1.0.33
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 +23 -0
- package/dist/components/form-renderer/FormActions.d.ts.map +1 -0
- package/dist/components/form-renderer/FormActions.js +116 -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/ValidationModal.d.ts +25 -0
- package/dist/components/form-renderer/ValidationModal.d.ts.map +1 -0
- package/dist/components/form-renderer/ValidationModal.js +37 -0
- package/dist/components/form-renderer/index.d.ts +19 -0
- package/dist/components/form-renderer/index.d.ts.map +1 -0
- package/dist/components/form-renderer/index.js +24 -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 +14 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -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,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;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook de navigation pour les formulaires d'enquête
|
|
3
|
+
* Gère la navigation entre groupes et instances
|
|
4
|
+
* RSU v2 - Package Partagé
|
|
5
|
+
*/
|
|
6
|
+
import { GroupeFormulaire, NavigationState } from '../types/enquete';
|
|
7
|
+
export interface UseFormNavigationOptions {
|
|
8
|
+
/** Index initial du groupe */
|
|
9
|
+
initialGroupIndex?: number;
|
|
10
|
+
/** Index initial de l'instance */
|
|
11
|
+
initialInstanceIndex?: number;
|
|
12
|
+
/** Callback appelé lors d'un changement de groupe */
|
|
13
|
+
onGroupChange?: (groupIndex: number, groupCode: string) => void;
|
|
14
|
+
/** Callback appelé lors d'un changement d'instance */
|
|
15
|
+
onInstanceChange?: (instanceIndex: number) => void;
|
|
16
|
+
/** Validation avant navigation (doit retourner true pour permettre la navigation) */
|
|
17
|
+
validateBeforeNavigation?: () => boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface UseFormNavigationResult {
|
|
20
|
+
/** État de navigation actuel */
|
|
21
|
+
navigationState: NavigationState;
|
|
22
|
+
/** Groupe actuellement affiché */
|
|
23
|
+
currentGroup: GroupeFormulaire | undefined;
|
|
24
|
+
/** Instance actuellement affichée (pour groupes multiples) */
|
|
25
|
+
currentInstance: any | undefined;
|
|
26
|
+
/** Est-ce le premier groupe? */
|
|
27
|
+
isFirstGroup: boolean;
|
|
28
|
+
/** Est-ce le dernier groupe? */
|
|
29
|
+
isLastGroup: boolean;
|
|
30
|
+
/** Peut-on aller au groupe suivant? */
|
|
31
|
+
canGoNext: boolean;
|
|
32
|
+
/** Peut-on aller au groupe précédent? */
|
|
33
|
+
canGoPrevious: boolean;
|
|
34
|
+
/** Aller au groupe suivant */
|
|
35
|
+
goToNextGroup: () => void;
|
|
36
|
+
/** Aller au groupe précédent */
|
|
37
|
+
goToPreviousGroup: () => void;
|
|
38
|
+
/** Aller à un groupe spécifique */
|
|
39
|
+
goToGroup: (groupIndex: number) => void;
|
|
40
|
+
/** Changer d'instance (pour groupes multiples) */
|
|
41
|
+
changeInstance: (instanceIndex: number) => void;
|
|
42
|
+
/** Mettre à jour le nombre total d'instances */
|
|
43
|
+
updateTotalInstances: (count: number) => void;
|
|
44
|
+
/** Réinitialiser la navigation */
|
|
45
|
+
resetNavigation: () => void;
|
|
46
|
+
/** Pourcentage de progression */
|
|
47
|
+
progressPercent: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Hook pour gérer la navigation dans un formulaire d'enquête
|
|
51
|
+
*
|
|
52
|
+
* @param groupes - Liste des groupes du formulaire
|
|
53
|
+
* @param options - Options de configuration
|
|
54
|
+
* @returns Objet contenant l'état et les fonctions de navigation
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```tsx
|
|
58
|
+
* const {
|
|
59
|
+
* navigationState,
|
|
60
|
+
* currentGroup,
|
|
61
|
+
* isLastGroup,
|
|
62
|
+
* goToNextGroup,
|
|
63
|
+
* goToPreviousGroup
|
|
64
|
+
* } = useFormNavigation(formulaire.groupes, {
|
|
65
|
+
* onGroupChange: (index, code) => console.log(`Navigated to ${code}`),
|
|
66
|
+
* validateBeforeNavigation: () => validateCurrentGroup()
|
|
67
|
+
* });
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function useFormNavigation(groupes: GroupeFormulaire[], options?: UseFormNavigationOptions): UseFormNavigationResult;
|
|
71
|
+
export default useFormNavigation;
|
|
72
|
+
//# sourceMappingURL=useFormNavigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFormNavigation.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormNavigation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAkB,MAAM,kBAAkB,CAAC;AAErF,MAAM,WAAW,wBAAwB;IACvC,8BAA8B;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kCAAkC;IAClC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qDAAqD;IACrD,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,qFAAqF;IACrF,wBAAwB,CAAC,EAAE,MAAM,OAAO,CAAC;CAC1C;AAED,MAAM,WAAW,uBAAuB;IACtC,gCAAgC;IAChC,eAAe,EAAE,eAAe,CAAC;IACjC,kCAAkC;IAClC,YAAY,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC3C,8DAA8D;IAC9D,eAAe,EAAE,GAAG,GAAG,SAAS,CAAC;IACjC,gCAAgC;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,gCAAgC;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,yCAAyC;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,8BAA8B;IAC9B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,gCAAgC;IAChC,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,mCAAmC;IACnC,SAAS,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kDAAkD;IAClD,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,gDAAgD;IAChD,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,kCAAkC;IAClC,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,iCAAiC;IACjC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,gBAAgB,EAAE,EAC3B,OAAO,GAAE,wBAA6B,GACrC,uBAAuB,CAqJzB;AAED,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook de navigation pour les formulaires d'enquête
|
|
3
|
+
* Gère la navigation entre groupes et instances
|
|
4
|
+
* RSU v2 - Package Partagé
|
|
5
|
+
*/
|
|
6
|
+
import { useState, useCallback, useMemo } from 'react';
|
|
7
|
+
/**
|
|
8
|
+
* Hook pour gérer la navigation dans un formulaire d'enquête
|
|
9
|
+
*
|
|
10
|
+
* @param groupes - Liste des groupes du formulaire
|
|
11
|
+
* @param options - Options de configuration
|
|
12
|
+
* @returns Objet contenant l'état et les fonctions de navigation
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const {
|
|
17
|
+
* navigationState,
|
|
18
|
+
* currentGroup,
|
|
19
|
+
* isLastGroup,
|
|
20
|
+
* goToNextGroup,
|
|
21
|
+
* goToPreviousGroup
|
|
22
|
+
* } = useFormNavigation(formulaire.groupes, {
|
|
23
|
+
* onGroupChange: (index, code) => console.log(`Navigated to ${code}`),
|
|
24
|
+
* validateBeforeNavigation: () => validateCurrentGroup()
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function useFormNavigation(groupes, options = {}) {
|
|
29
|
+
const { initialGroupIndex = 0, initialInstanceIndex = 0, onGroupChange, onInstanceChange, validateBeforeNavigation } = options;
|
|
30
|
+
// État de navigation
|
|
31
|
+
const [navigationState, setNavigationState] = useState({
|
|
32
|
+
groupeIndex: initialGroupIndex,
|
|
33
|
+
instanceIndex: initialInstanceIndex,
|
|
34
|
+
totalGroupes: groupes?.length || 0,
|
|
35
|
+
totalInstances: groupes?.[initialGroupIndex]?.instances?.length || 1
|
|
36
|
+
});
|
|
37
|
+
// Groupe et instance actuels
|
|
38
|
+
const currentGroup = useMemo(() => groupes?.[navigationState.groupeIndex], [groupes, navigationState.groupeIndex]);
|
|
39
|
+
const currentInstance = useMemo(() => currentGroup?.instances?.[navigationState.instanceIndex], [currentGroup, navigationState.instanceIndex]);
|
|
40
|
+
// Calculs de position
|
|
41
|
+
const isFirstGroup = navigationState.groupeIndex === 0;
|
|
42
|
+
const isLastGroup = navigationState.groupeIndex === (groupes?.length || 0) - 1;
|
|
43
|
+
const canGoNext = !isLastGroup;
|
|
44
|
+
const canGoPrevious = !isFirstGroup;
|
|
45
|
+
// Pourcentage de progression
|
|
46
|
+
const progressPercent = useMemo(() => {
|
|
47
|
+
if (!groupes?.length)
|
|
48
|
+
return 0;
|
|
49
|
+
return Math.round(((navigationState.groupeIndex + 1) / groupes.length) * 100);
|
|
50
|
+
}, [groupes?.length, navigationState.groupeIndex]);
|
|
51
|
+
// Aller au groupe suivant
|
|
52
|
+
const goToNextGroup = useCallback(() => {
|
|
53
|
+
if (isLastGroup)
|
|
54
|
+
return;
|
|
55
|
+
// Validation optionnelle avant navigation
|
|
56
|
+
if (validateBeforeNavigation && !validateBeforeNavigation()) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const nextGroupIndex = navigationState.groupeIndex + 1;
|
|
60
|
+
const nextGroup = groupes?.[nextGroupIndex];
|
|
61
|
+
setNavigationState(prev => ({
|
|
62
|
+
...prev,
|
|
63
|
+
groupeIndex: nextGroupIndex,
|
|
64
|
+
instanceIndex: 0,
|
|
65
|
+
totalInstances: nextGroup?.instances?.length || 1
|
|
66
|
+
}));
|
|
67
|
+
if (onGroupChange && nextGroup) {
|
|
68
|
+
onGroupChange(nextGroupIndex, nextGroup.code);
|
|
69
|
+
}
|
|
70
|
+
}, [isLastGroup, navigationState.groupeIndex, groupes, validateBeforeNavigation, onGroupChange]);
|
|
71
|
+
// Aller au groupe précédent
|
|
72
|
+
const goToPreviousGroup = useCallback(() => {
|
|
73
|
+
if (isFirstGroup)
|
|
74
|
+
return;
|
|
75
|
+
const prevGroupIndex = navigationState.groupeIndex - 1;
|
|
76
|
+
const prevGroup = groupes?.[prevGroupIndex];
|
|
77
|
+
setNavigationState(prev => ({
|
|
78
|
+
...prev,
|
|
79
|
+
groupeIndex: prevGroupIndex,
|
|
80
|
+
instanceIndex: 0,
|
|
81
|
+
totalInstances: prevGroup?.instances?.length || 1
|
|
82
|
+
}));
|
|
83
|
+
if (onGroupChange && prevGroup) {
|
|
84
|
+
onGroupChange(prevGroupIndex, prevGroup.code);
|
|
85
|
+
}
|
|
86
|
+
}, [isFirstGroup, navigationState.groupeIndex, groupes, onGroupChange]);
|
|
87
|
+
// Aller à un groupe spécifique
|
|
88
|
+
const goToGroup = useCallback((groupIndex) => {
|
|
89
|
+
if (groupIndex < 0 || groupIndex >= (groupes?.length || 0))
|
|
90
|
+
return;
|
|
91
|
+
const targetGroup = groupes?.[groupIndex];
|
|
92
|
+
setNavigationState(prev => ({
|
|
93
|
+
...prev,
|
|
94
|
+
groupeIndex: groupIndex,
|
|
95
|
+
instanceIndex: 0,
|
|
96
|
+
totalInstances: targetGroup?.instances?.length || 1
|
|
97
|
+
}));
|
|
98
|
+
if (onGroupChange && targetGroup) {
|
|
99
|
+
onGroupChange(groupIndex, targetGroup.code);
|
|
100
|
+
}
|
|
101
|
+
}, [groupes, onGroupChange]);
|
|
102
|
+
// Changer d'instance
|
|
103
|
+
const changeInstance = useCallback((instanceIndex) => {
|
|
104
|
+
if (instanceIndex < 0)
|
|
105
|
+
return;
|
|
106
|
+
setNavigationState(prev => ({
|
|
107
|
+
...prev,
|
|
108
|
+
instanceIndex
|
|
109
|
+
}));
|
|
110
|
+
if (onInstanceChange) {
|
|
111
|
+
onInstanceChange(instanceIndex);
|
|
112
|
+
}
|
|
113
|
+
}, [onInstanceChange]);
|
|
114
|
+
// Mettre à jour le nombre total d'instances
|
|
115
|
+
const updateTotalInstances = useCallback((count) => {
|
|
116
|
+
setNavigationState(prev => ({
|
|
117
|
+
...prev,
|
|
118
|
+
totalInstances: count
|
|
119
|
+
}));
|
|
120
|
+
}, []);
|
|
121
|
+
// Réinitialiser la navigation
|
|
122
|
+
const resetNavigation = useCallback(() => {
|
|
123
|
+
setNavigationState({
|
|
124
|
+
groupeIndex: initialGroupIndex,
|
|
125
|
+
instanceIndex: initialInstanceIndex,
|
|
126
|
+
totalGroupes: groupes?.length || 0,
|
|
127
|
+
totalInstances: groupes?.[initialGroupIndex]?.instances?.length || 1
|
|
128
|
+
});
|
|
129
|
+
}, [initialGroupIndex, initialInstanceIndex, groupes]);
|
|
130
|
+
return {
|
|
131
|
+
navigationState,
|
|
132
|
+
currentGroup,
|
|
133
|
+
currentInstance,
|
|
134
|
+
isFirstGroup,
|
|
135
|
+
isLastGroup,
|
|
136
|
+
canGoNext,
|
|
137
|
+
canGoPrevious,
|
|
138
|
+
goToNextGroup,
|
|
139
|
+
goToPreviousGroup,
|
|
140
|
+
goToGroup,
|
|
141
|
+
changeInstance,
|
|
142
|
+
updateTotalInstances,
|
|
143
|
+
resetNavigation,
|
|
144
|
+
progressPercent
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
export default useFormNavigation;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook principal pour le rendu des formulaires d'enquête
|
|
3
|
+
* Combine navigation, validation et gestion des instances
|
|
4
|
+
* RSU v2 - Package Partagé
|
|
5
|
+
*/
|
|
6
|
+
import { FormulaireComplet, EnqueteInstance, EnqueteReponse, GroupeFormulaire, GroupeInstance, VariableFormulaire, VariableValue } from '../types/enquete';
|
|
7
|
+
import { FormRendererConfig, FormRendererMode, FormRendererFeatures } from '../types/form-renderer';
|
|
8
|
+
import { UseFormNavigationResult } from './useFormNavigation';
|
|
9
|
+
import { UseFormValidationResult } from './useFormValidation';
|
|
10
|
+
import { UseFormInstancesResult } from './useFormInstances';
|
|
11
|
+
export interface UseFormRendererOptions {
|
|
12
|
+
/** Configuration du FormRenderer */
|
|
13
|
+
config: FormRendererConfig;
|
|
14
|
+
/** Réponses initiales */
|
|
15
|
+
initialResponses?: Record<string, EnqueteReponse>;
|
|
16
|
+
/** Callback lors de changement de réponses */
|
|
17
|
+
onResponsesChange?: (responses: Record<string, EnqueteReponse>) => void;
|
|
18
|
+
/** Callback lors de changement de groupe */
|
|
19
|
+
onGroupChange?: (groupIndex: number, groupCode: string) => void;
|
|
20
|
+
/** Fonction pour vérifier si une variable est visible */
|
|
21
|
+
isVariableVisible?: (variable: VariableFormulaire) => boolean;
|
|
22
|
+
/** Mode debug */
|
|
23
|
+
debug?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface UseFormRendererResult {
|
|
26
|
+
navigation: UseFormNavigationResult;
|
|
27
|
+
validation: UseFormValidationResult;
|
|
28
|
+
instances: UseFormInstancesResult;
|
|
29
|
+
/** Toutes les réponses */
|
|
30
|
+
responses: Record<string, EnqueteReponse>;
|
|
31
|
+
/** Mettre à jour une réponse */
|
|
32
|
+
updateResponse: (variableCode: string, value: VariableValue, variable: VariableFormulaire, numeroMembre?: number) => void;
|
|
33
|
+
/** Mettre à jour toutes les réponses */
|
|
34
|
+
setResponses: React.Dispatch<React.SetStateAction<Record<string, EnqueteReponse>>>;
|
|
35
|
+
/** Y a-t-il des changements non sauvegardés? */
|
|
36
|
+
hasUnsavedChanges: boolean;
|
|
37
|
+
/** Marquer comme ayant des changements */
|
|
38
|
+
setHasUnsavedChanges: React.Dispatch<React.SetStateAction<boolean>>;
|
|
39
|
+
/** Est en train de soumettre? */
|
|
40
|
+
isSubmitting: boolean;
|
|
41
|
+
/** Définir l'état de soumission */
|
|
42
|
+
setIsSubmitting: React.Dispatch<React.SetStateAction<boolean>>;
|
|
43
|
+
/** Mode lecture seule effectif */
|
|
44
|
+
effectiveDisabled: boolean;
|
|
45
|
+
/** Est en mode consultation? */
|
|
46
|
+
isConsultationMode: boolean;
|
|
47
|
+
/** Est en lecture seule? */
|
|
48
|
+
isReadOnly: boolean;
|
|
49
|
+
/** Mode actuel (admin/public) */
|
|
50
|
+
mode: FormRendererMode;
|
|
51
|
+
/** Fonctionnalités activées */
|
|
52
|
+
features: FormRendererFeatures;
|
|
53
|
+
/** Groupe actuellement affiché */
|
|
54
|
+
currentGroup: GroupeFormulaire | undefined;
|
|
55
|
+
/** Instance actuellement affichée */
|
|
56
|
+
currentInstance: GroupeInstance | undefined;
|
|
57
|
+
/** Groupes avec instances initialisées */
|
|
58
|
+
groupesWithInstances: GroupeFormulaire[];
|
|
59
|
+
/** Vérifier si une variable est de contrôle */
|
|
60
|
+
isControlVariable: (variable: VariableFormulaire) => boolean;
|
|
61
|
+
/** Obtenir la clé de réponse pour une variable */
|
|
62
|
+
getResponseKey: (variableCode: string, numeroMembre?: number) => string;
|
|
63
|
+
/** Obtenir la valeur d'une réponse */
|
|
64
|
+
getResponseValue: (variableCode: string, numeroMembre?: number) => VariableValue;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Hook principal pour gérer un formulaire d'enquête
|
|
68
|
+
*
|
|
69
|
+
* @param formulaire - Le formulaire complet
|
|
70
|
+
* @param enquete - L'instance d'enquête (optionnel pour nouvelle enquête)
|
|
71
|
+
* @param options - Options de configuration
|
|
72
|
+
* @returns Objet contenant tout l'état et les fonctions nécessaires
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```tsx
|
|
76
|
+
* const formRenderer = useFormRenderer(formulaire, enquete, {
|
|
77
|
+
* config: { mode: 'admin', features: { saveDraft: true } },
|
|
78
|
+
* onResponsesChange: (responses) => console.log('Changed:', responses)
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* // Utilisation
|
|
82
|
+
* const { navigation, validation, responses, updateResponse } = formRenderer;
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare function useFormRenderer(formulaire: FormulaireComplet, enquete?: EnqueteInstance, options?: UseFormRendererOptions): UseFormRendererResult;
|
|
86
|
+
export default useFormRenderer;
|
|
87
|
+
//# sourceMappingURL=useFormRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFormRenderer.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormRenderer.ts"],"names":[],"mappings":"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"}
|