@rsuci/shared-form-components 1.0.28 → 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/components/inputs/NumberInput.js +1 -1
- 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,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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"}
|
|
@@ -0,0 +1,177 @@
|
|
|
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 { useState, useCallback, useEffect, useMemo } from 'react';
|
|
7
|
+
import { useFormNavigation } from './useFormNavigation';
|
|
8
|
+
import { useFormValidation } from './useFormValidation';
|
|
9
|
+
import { useFormInstances } from './useFormInstances';
|
|
10
|
+
/**
|
|
11
|
+
* Hook principal pour gérer un formulaire d'enquête
|
|
12
|
+
*
|
|
13
|
+
* @param formulaire - Le formulaire complet
|
|
14
|
+
* @param enquete - L'instance d'enquête (optionnel pour nouvelle enquête)
|
|
15
|
+
* @param options - Options de configuration
|
|
16
|
+
* @returns Objet contenant tout l'état et les fonctions nécessaires
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* const formRenderer = useFormRenderer(formulaire, enquete, {
|
|
21
|
+
* config: { mode: 'admin', features: { saveDraft: true } },
|
|
22
|
+
* onResponsesChange: (responses) => console.log('Changed:', responses)
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // Utilisation
|
|
26
|
+
* const { navigation, validation, responses, updateResponse } = formRenderer;
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function useFormRenderer(formulaire, enquete, options = { config: { mode: 'public' } }) {
|
|
30
|
+
const { config, initialResponses, onResponsesChange, onGroupChange, isVariableVisible: externalIsVariableVisible, debug = false } = options;
|
|
31
|
+
const mode = config.mode;
|
|
32
|
+
const features = config.features || {};
|
|
33
|
+
// === État de base ===
|
|
34
|
+
const [responses, setResponses] = useState(initialResponses || enquete?.reponses || {});
|
|
35
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
36
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
37
|
+
// === Calcul du mode lecture seule ===
|
|
38
|
+
const isReadOnly = useMemo(() => {
|
|
39
|
+
if (!enquete)
|
|
40
|
+
return false;
|
|
41
|
+
// En mode admin, certains statuts empêchent la modification
|
|
42
|
+
if (mode === 'admin') {
|
|
43
|
+
return ['VALIDE_ENQUETEUR', 'VALIDE_DR', 'FINALISE'].includes(enquete.statut);
|
|
44
|
+
}
|
|
45
|
+
// En mode public, on utilise le flag isReadOnly de l'enquête
|
|
46
|
+
return enquete.isReadOnly || false;
|
|
47
|
+
}, [enquete, mode]);
|
|
48
|
+
const isConsultationMode = enquete?.isReadOnly || false;
|
|
49
|
+
const effectiveDisabled = isReadOnly || isConsultationMode;
|
|
50
|
+
// === Utilitaires ===
|
|
51
|
+
const isControlVariable = useCallback((variable) => {
|
|
52
|
+
return formulaire?.groupes?.some(groupe => groupe.estMultiple && groupe.codeVariable === variable.code) || false;
|
|
53
|
+
}, [formulaire?.groupes]);
|
|
54
|
+
const getResponseKey = useCallback((variableCode, numeroMembre) => {
|
|
55
|
+
return numeroMembre ? `${variableCode}_${numeroMembre}` : variableCode;
|
|
56
|
+
}, []);
|
|
57
|
+
const getResponseValue = useCallback((variableCode, numeroMembre) => {
|
|
58
|
+
const key = getResponseKey(variableCode, numeroMembre);
|
|
59
|
+
return responses[key]?.valeur ?? null;
|
|
60
|
+
}, [responses, getResponseKey]);
|
|
61
|
+
// === Hook de gestion des instances ===
|
|
62
|
+
const instances = useFormInstances(formulaire?.groupes || [], responses, {
|
|
63
|
+
debug,
|
|
64
|
+
onGroupesUpdated: (groupes) => {
|
|
65
|
+
if (debug) {
|
|
66
|
+
console.log('[FormRenderer] Groupes mis à jour:', groupes.length);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
// === Hook de validation ===
|
|
71
|
+
const validation = useFormValidation({
|
|
72
|
+
isVariableVisible: externalIsVariableVisible || (() => true),
|
|
73
|
+
isControlVariable,
|
|
74
|
+
debug
|
|
75
|
+
});
|
|
76
|
+
// === Hook de navigation ===
|
|
77
|
+
const navigation = useFormNavigation(instances.groupesWithInstances.length > 0
|
|
78
|
+
? instances.groupesWithInstances
|
|
79
|
+
: formulaire?.groupes || [], {
|
|
80
|
+
onGroupChange,
|
|
81
|
+
validateBeforeNavigation: () => {
|
|
82
|
+
// Valider le groupe actuel avant de naviguer
|
|
83
|
+
return validation.validateCurrentGroupRequiredFields(navigation.currentGroup, responses);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// === Groupe et instance actuels ===
|
|
87
|
+
const currentGroup = useMemo(() => {
|
|
88
|
+
return instances.groupesWithInstances[navigation.navigationState.groupeIndex]
|
|
89
|
+
|| formulaire?.groupes?.[navigation.navigationState.groupeIndex];
|
|
90
|
+
}, [instances.groupesWithInstances, formulaire?.groupes, navigation.navigationState.groupeIndex]);
|
|
91
|
+
const currentInstance = useMemo(() => {
|
|
92
|
+
return currentGroup?.instances?.[navigation.navigationState.instanceIndex];
|
|
93
|
+
}, [currentGroup, navigation.navigationState.instanceIndex]);
|
|
94
|
+
// === Mise à jour des réponses ===
|
|
95
|
+
const updateResponse = useCallback((variableCode, value, variable, numeroMembre) => {
|
|
96
|
+
const responseKey = getResponseKey(variableCode, numeroMembre);
|
|
97
|
+
const newResponse = {
|
|
98
|
+
variableId: variable.id,
|
|
99
|
+
variableCode: variableCode,
|
|
100
|
+
valeur: value,
|
|
101
|
+
numeroMembre,
|
|
102
|
+
dateModification: new Date()
|
|
103
|
+
};
|
|
104
|
+
setResponses(prev => {
|
|
105
|
+
const updated = {
|
|
106
|
+
...prev,
|
|
107
|
+
[responseKey]: newResponse
|
|
108
|
+
};
|
|
109
|
+
if (onResponsesChange) {
|
|
110
|
+
onResponsesChange(updated);
|
|
111
|
+
}
|
|
112
|
+
return updated;
|
|
113
|
+
});
|
|
114
|
+
// Mettre à jour l'instance si c'est un groupe multiple
|
|
115
|
+
if (currentGroup?.estMultiple && currentInstance) {
|
|
116
|
+
currentInstance.reponses[variableCode] = newResponse;
|
|
117
|
+
currentInstance.estComplete = instances.isInstanceComplete(currentInstance, currentGroup);
|
|
118
|
+
}
|
|
119
|
+
// Mettre à jour les propriétés des groupes affectés
|
|
120
|
+
const affectedGroups = instances.groupesWithInstances.filter(g => g.codeVariable === variableCode);
|
|
121
|
+
if (affectedGroups.length > 0) {
|
|
122
|
+
affectedGroups.forEach(groupe => {
|
|
123
|
+
instances.updateGroupeProperties(groupe, {
|
|
124
|
+
...responses,
|
|
125
|
+
[responseKey]: newResponse
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
setHasUnsavedChanges(true);
|
|
130
|
+
}, [
|
|
131
|
+
getResponseKey,
|
|
132
|
+
currentGroup,
|
|
133
|
+
currentInstance,
|
|
134
|
+
instances,
|
|
135
|
+
responses,
|
|
136
|
+
onResponsesChange
|
|
137
|
+
]);
|
|
138
|
+
// === Synchronisation initiale ===
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (formulaire?.groupes?.length && Object.keys(responses).length > 0) {
|
|
141
|
+
const initialized = instances.initializeGroupesWithInstances(formulaire.groupes, responses);
|
|
142
|
+
instances.setGroupesWithInstances(initialized);
|
|
143
|
+
}
|
|
144
|
+
}, [formulaire?.groupes]);
|
|
145
|
+
return {
|
|
146
|
+
// Navigation
|
|
147
|
+
navigation,
|
|
148
|
+
// Validation
|
|
149
|
+
validation,
|
|
150
|
+
// Instances
|
|
151
|
+
instances,
|
|
152
|
+
// Réponses
|
|
153
|
+
responses,
|
|
154
|
+
updateResponse,
|
|
155
|
+
setResponses,
|
|
156
|
+
// État
|
|
157
|
+
hasUnsavedChanges,
|
|
158
|
+
setHasUnsavedChanges,
|
|
159
|
+
isSubmitting,
|
|
160
|
+
setIsSubmitting,
|
|
161
|
+
effectiveDisabled,
|
|
162
|
+
isConsultationMode,
|
|
163
|
+
isReadOnly,
|
|
164
|
+
// Configuration
|
|
165
|
+
mode,
|
|
166
|
+
features,
|
|
167
|
+
// Groupes
|
|
168
|
+
currentGroup,
|
|
169
|
+
currentInstance,
|
|
170
|
+
groupesWithInstances: instances.groupesWithInstances,
|
|
171
|
+
// Utilitaires
|
|
172
|
+
isControlVariable,
|
|
173
|
+
getResponseKey,
|
|
174
|
+
getResponseValue
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
export default useFormRenderer;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook de validation pour les formulaires d'enquête
|
|
3
|
+
* Gère la validation des variables, groupes et instances
|
|
4
|
+
* RSU v2 - Package Partagé
|
|
5
|
+
*/
|
|
6
|
+
import { GroupeFormulaire, VariableFormulaire, EnqueteReponse, ValidationError, ValidationResult } from '../types/enquete';
|
|
7
|
+
export interface UseFormValidationOptions {
|
|
8
|
+
/** Fonction pour vérifier si une variable est visible */
|
|
9
|
+
isVariableVisible?: (variable: VariableFormulaire) => boolean;
|
|
10
|
+
/** Fonction pour vérifier si une variable est de contrôle */
|
|
11
|
+
isControlVariable?: (variable: VariableFormulaire) => boolean;
|
|
12
|
+
/** Mode debug (affiche les logs de validation) */
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface UseFormValidationResult {
|
|
16
|
+
/** Valide une variable individuelle */
|
|
17
|
+
validateVariable: (variable: VariableFormulaire, allResponses: Record<string, EnqueteReponse>) => boolean;
|
|
18
|
+
/** Valide un groupe complet */
|
|
19
|
+
validateGroup: (groupe: GroupeFormulaire, allResponses: Record<string, EnqueteReponse>) => ValidationResult;
|
|
20
|
+
/** Valide les champs obligatoires du groupe actuel */
|
|
21
|
+
validateCurrentGroupRequiredFields: (currentGroup: GroupeFormulaire | undefined, responses: Record<string, EnqueteReponse>) => boolean;
|
|
22
|
+
/** Valide que toutes les instances d'un groupe multiple sont complètes */
|
|
23
|
+
validateAllInstancesComplete: (groupe: GroupeFormulaire) => boolean;
|
|
24
|
+
/** Valide tous les groupes du formulaire */
|
|
25
|
+
validateAllGroups: (groupes: GroupeFormulaire[], responses: Record<string, EnqueteReponse>) => ValidationResult;
|
|
26
|
+
/** Récupère les erreurs de validation pour un groupe */
|
|
27
|
+
getGroupValidationErrors: (groupe: GroupeFormulaire, responses: Record<string, EnqueteReponse>) => ValidationError[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Hook pour gérer la validation dans un formulaire d'enquête
|
|
31
|
+
*
|
|
32
|
+
* @param options - Options de configuration
|
|
33
|
+
* @returns Objet contenant les fonctions de validation
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* const {
|
|
38
|
+
* validateVariable,
|
|
39
|
+
* validateCurrentGroupRequiredFields,
|
|
40
|
+
* validateAllGroups
|
|
41
|
+
* } = useFormValidation({
|
|
42
|
+
* isVariableVisible: (v) => formTree.isVariableVisible(v.code),
|
|
43
|
+
* isControlVariable: (v) => groupes.some(g => g.codeVariable === v.code),
|
|
44
|
+
* debug: true
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function useFormValidation(options?: UseFormValidationOptions): UseFormValidationResult;
|
|
49
|
+
export default useFormValidation;
|
|
50
|
+
//# sourceMappingURL=useFormValidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFormValidation.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormValidation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,gBAAgB,EAEjB,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,wBAAwB;IACvC,yDAAyD;IACzD,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,CAAC;IAC9D,6DAA6D;IAC7D,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,CAAC;IAC9D,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,uCAAuC;IACvC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,OAAO,CAAC;IAC1G,+BAA+B;IAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,gBAAgB,CAAC;IAC5G,sDAAsD;IACtD,kCAAkC,EAAE,CAClC,YAAY,EAAE,gBAAgB,GAAG,SAAS,EAC1C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,KACtC,OAAO,CAAC;IACb,0EAA0E;IAC1E,4BAA4B,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,OAAO,CAAC;IACpE,4CAA4C;IAC5C,iBAAiB,EAAE,CACjB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,KACtC,gBAAgB,CAAC;IACtB,wDAAwD;IACxD,wBAAwB,EAAE,CACxB,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,KACtC,eAAe,EAAE,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,wBAA6B,GACrC,uBAAuB,CAyLzB;AAED,eAAe,iBAAiB,CAAC"}
|