@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.
Files changed (49) hide show
  1. package/dist/components/form-renderer/ConfirmationModal.d.ts +20 -0
  2. package/dist/components/form-renderer/ConfirmationModal.d.ts.map +1 -0
  3. package/dist/components/form-renderer/ConfirmationModal.js +79 -0
  4. package/dist/components/form-renderer/FormActions.d.ts +21 -0
  5. package/dist/components/form-renderer/FormActions.d.ts.map +1 -0
  6. package/dist/components/form-renderer/FormActions.js +81 -0
  7. package/dist/components/form-renderer/FormNavigationButtons.d.ts +23 -0
  8. package/dist/components/form-renderer/FormNavigationButtons.d.ts.map +1 -0
  9. package/dist/components/form-renderer/FormNavigationButtons.js +35 -0
  10. package/dist/components/form-renderer/FormProgress.d.ts +19 -0
  11. package/dist/components/form-renderer/FormProgress.d.ts.map +1 -0
  12. package/dist/components/form-renderer/FormProgress.js +26 -0
  13. package/dist/components/form-renderer/FormRenderer.d.ts +39 -0
  14. package/dist/components/form-renderer/FormRenderer.d.ts.map +1 -0
  15. package/dist/components/form-renderer/FormRenderer.js +113 -0
  16. package/dist/components/form-renderer/FormRendererContext.d.ts +109 -0
  17. package/dist/components/form-renderer/FormRendererContext.d.ts.map +1 -0
  18. package/dist/components/form-renderer/FormRendererContext.js +114 -0
  19. package/dist/components/form-renderer/GroupeInstanceTabs.d.ts +18 -0
  20. package/dist/components/form-renderer/GroupeInstanceTabs.d.ts.map +1 -0
  21. package/dist/components/form-renderer/GroupeInstanceTabs.js +174 -0
  22. package/dist/components/form-renderer/index.d.ts +17 -0
  23. package/dist/components/form-renderer/index.d.ts.map +1 -0
  24. package/dist/components/form-renderer/index.js +23 -0
  25. package/dist/components/inputs/NumberInput.js +1 -1
  26. package/dist/hooks/useFormInstances.d.ts +87 -0
  27. package/dist/hooks/useFormInstances.d.ts.map +1 -0
  28. package/dist/hooks/useFormInstances.js +197 -0
  29. package/dist/hooks/useFormNavigation.d.ts +72 -0
  30. package/dist/hooks/useFormNavigation.d.ts.map +1 -0
  31. package/dist/hooks/useFormNavigation.js +147 -0
  32. package/dist/hooks/useFormRenderer.d.ts +87 -0
  33. package/dist/hooks/useFormRenderer.d.ts.map +1 -0
  34. package/dist/hooks/useFormRenderer.js +177 -0
  35. package/dist/hooks/useFormValidation.d.ts +50 -0
  36. package/dist/hooks/useFormValidation.d.ts.map +1 -0
  37. package/dist/hooks/useFormValidation.js +175 -0
  38. package/dist/index.d.ts +13 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +20 -0
  41. package/dist/lib/__tests__/date-functions.test.d.ts +5 -0
  42. package/dist/lib/__tests__/date-functions.test.d.ts.map +1 -0
  43. package/dist/lib/__tests__/date-functions.test.js +184 -0
  44. package/dist/lib/utils/groupeInstanceManager.d.ts +88 -0
  45. package/dist/lib/utils/groupeInstanceManager.d.ts.map +1 -0
  46. package/dist/lib/utils/groupeInstanceManager.js +606 -0
  47. package/dist/types/form-renderer.d.ts +115 -1
  48. package/dist/types/form-renderer.d.ts.map +1 -1
  49. 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"}