@rsuci/shared-form-components 1.0.15 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,22 @@
1
1
  /**
2
2
  * Composant de rendu des groupes pour les formulaires d'enquête
3
3
  * RSU v2 - Moteur de Rendu des Formulaires d'Enquête
4
+ *
5
+ * Supporte deux modes:
6
+ * 1. Mode FormTree (nouveau): Utilise le FormTree pour la gestion centralisée de visibilité et jumps
7
+ * 2. Mode ConditionEngine (legacy): Utilise le ConditionEngine directement
4
8
  */
5
9
  import React from 'react';
6
10
  import { GroupeFormulaire, EnqueteReponse, VariableValue, ValidationError, AutoAction } from '../types/enquete';
7
11
  import { ConditionEngine } from '../lib/condition-engine';
12
+ import { FormTree } from '../lib/form-tree';
8
13
  interface GroupRendererProps {
9
14
  groupe: GroupeFormulaire;
10
15
  responses: Record<string, EnqueteReponse>;
11
- conditionEngine: ConditionEngine;
16
+ /** @deprecated Utiliser formTree à la place */
17
+ conditionEngine?: ConditionEngine;
18
+ /** Nouveau: FormTree pour gestion centralisée (recommandé) */
19
+ formTree?: FormTree;
12
20
  onChange: (variableCode: string, value: VariableValue, iteration?: number) => void;
13
21
  onValidation: (errors: ValidationError[]) => void;
14
22
  onAutoAction?: (actions: AutoAction[]) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"GroupRenderer.d.ts","sourceRoot":"","sources":["../../src/components/GroupRenderer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,eAAe,EACf,UAAU,EACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG1D,UAAU,kBAAkB;IAC1B,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,eAAe,EAAE,eAAe,CAAC;IACjC,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnF,YAAY,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,IAAI,CAAC;IAClD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAiJD,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAkS/C,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"GroupRenderer.d.ts","sourceRoot":"","sources":["../../src/components/GroupRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAuC,MAAM,OAAO,CAAC;AAE5D,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,eAAe,EACf,UAAU,EAEX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,UAAU,kBAAkB;IAC1B,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,+CAA+C;IAC/C,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnF,YAAY,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,IAAI,CAAC;IAClD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAsID,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAmU/C,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -1,10 +1,14 @@
1
1
  /**
2
2
  * Composant de rendu des groupes pour les formulaires d'enquête
3
3
  * RSU v2 - Moteur de Rendu des Formulaires d'Enquête
4
+ *
5
+ * Supporte deux modes:
6
+ * 1. Mode FormTree (nouveau): Utilise le FormTree pour la gestion centralisée de visibilité et jumps
7
+ * 2. Mode ConditionEngine (legacy): Utilise le ConditionEngine directement
4
8
  */
5
9
  'use client';
6
10
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
- import { useState, useEffect } from 'react';
11
+ import { useState, useEffect, useMemo } from 'react';
8
12
  import { Plus, Trash2, Users, AlertCircle } from 'lucide-react';
9
13
  import VariableRenderer from './VariableRenderer';
10
14
  // Composant pour gérer les groupes multiples
@@ -25,23 +29,22 @@ const MultipleGroupEntry = ({ groupe, iterationNumber, totalIterations, visibleV
25
29
  const rosterVariables = (variable.typeCode === 'ROSTERCHECK' || variable.typeCode === 'ROSTERLIST')
26
30
  ? variable.rosterVariables
27
31
  : undefined;
28
- // 🔍 LOG: Vérifier la transmission des rosterVariables dans MultipleGroupEntry
29
- if (variable.typeCode === 'ROSTERCHECK' || variable.typeCode === 'ROSTERLIST') {
30
- console.log(`🔍 [GroupRenderer/MultipleGroupEntry] Variable ${variable.code}:`, {
31
- typeCode: variable.typeCode,
32
- rosterVariables: rosterVariables,
33
- rosterVariablesType: typeof rosterVariables,
34
- isArray: Array.isArray(rosterVariables),
35
- length: Array.isArray(rosterVariables) ? rosterVariables.length : 'N/A'
36
- });
37
- }
38
32
  return (_jsx(VariableRenderer, { variable: variable, value: responses[responseKey]?.valeur, onChange: (value) => onChange(responseKey, value, iterationNumber), numeroMembre: iterationNumber, disabled: disabled, rosterVariables: rosterVariables }, responseKey));
39
33
  }) })] }));
40
34
  };
41
35
  // Composant principal GroupRenderer
42
- const GroupRenderer = ({ groupe, responses, conditionEngine, onChange, onValidation, onAutoAction, disabled = false }) => {
36
+ const GroupRenderer = ({ groupe, responses, conditionEngine, formTree, onChange, onValidation, onAutoAction, disabled = false }) => {
43
37
  const [multipleEntries, setMultipleEntries] = useState([1]);
44
38
  const [validationErrors, setValidationErrors] = useState([]);
39
+ // Déterminer le mode d'utilisation
40
+ const useFormTreeMode = !!formTree;
41
+ // Obtenir le ConditionEngine (depuis formTree ou directement)
42
+ const engine = useMemo(() => {
43
+ if (formTree) {
44
+ return formTree.getConditionEngine();
45
+ }
46
+ return conditionEngine;
47
+ }, [formTree, conditionEngine]);
45
48
  // Obtenir le nombre d'itérations depuis la variable de contrôle
46
49
  const getIterationCount = () => {
47
50
  if (!groupe.estMultiple || !groupe.configMultiple?.variableControle) {
@@ -53,26 +56,25 @@ const GroupRenderer = ({ groupe, responses, conditionEngine, onChange, onValidat
53
56
  }
54
57
  return Math.min(controlValue, groupe.configMultiple.maxIterations || 50);
55
58
  };
56
- // Filtrer les variables visibles selon les conditions (avec support des nouvelles fonctions)
57
- const getVisibleVariables = () => {
58
- console.log('📋 [GroupRenderer] Filtering visible variables for group:', groupe.designation);
59
+ // Filtrer les variables visibles selon les conditions
60
+ const visibleVariables = useMemo(() => {
61
+ // Mode FormTree: utiliser directement getVisibleVariables
62
+ if (useFormTreeMode && formTree) {
63
+ return formTree.getVisibleVariables(groupe.code);
64
+ }
65
+ // Mode Legacy: filtrage manuel avec ConditionEngine
66
+ if (!engine) {
67
+ return groupe.variables.filter(v => v.estVisible);
68
+ }
59
69
  return groupe.variables.filter(variable => {
60
- console.log(`📋 [GroupRenderer] Processing variable: ${variable.code} (${variable.designation})`);
61
- console.log(`📋 [GroupRenderer] - estVisible: ${variable.estVisible}`);
62
- console.log(`📋 [GroupRenderer] - conditionsAffichage: ${variable.conditionsAffichage}`);
63
70
  if (!variable.conditionsAffichage) {
64
- console.log(`📋 [GroupRenderer] - No condition, returning estVisible: ${variable.estVisible}`);
65
71
  return variable.estVisible;
66
72
  }
67
- // Évaluer la condition avec le moteur étendu
68
- const isVisible = conditionEngine.evaluate(variable.conditionsAffichage);
69
- console.log(`📋 [GroupRenderer] - Condition evaluation result: ${isVisible}`);
70
- const finalResult = variable.estVisible && isVisible;
71
- console.log(`📋 [GroupRenderer] - Final visibility: ${finalResult} (estVisible: ${variable.estVisible} && condition: ${isVisible})`);
72
- return finalResult;
73
+ // Évaluer la condition avec le moteur
74
+ const isVisible = engine.evaluate(variable.conditionsAffichage);
75
+ return variable.estVisible && isVisible;
73
76
  });
74
- };
75
- const visibleVariables = getVisibleVariables();
77
+ }, [useFormTreeMode, formTree, engine, groupe, responses]);
76
78
  // Mettre à jour le nombre d'itérations quand la variable de contrôle change
77
79
  useEffect(() => {
78
80
  if (groupe.estMultiple && groupe.configMultiple?.variableControle) {
@@ -95,6 +97,19 @@ const GroupRenderer = ({ groupe, responses, conditionEngine, onChange, onValidat
95
97
  }, [responses[groupe.configMultiple?.variableControle || '']?.valeur, groupe]);
96
98
  // Validation du groupe
97
99
  useEffect(() => {
100
+ // Mode FormTree: utiliser la validation du FormTree
101
+ if (useFormTreeMode && formTree) {
102
+ const { errors } = formTree.validateGroup(groupe.code);
103
+ const validationErrs = errors.map(msg => ({
104
+ variableCode: groupe.code,
105
+ message: msg,
106
+ type: 'required'
107
+ }));
108
+ setValidationErrors(validationErrs);
109
+ onValidation(validationErrs);
110
+ return;
111
+ }
112
+ // Mode Legacy: validation manuelle
98
113
  const errors = [];
99
114
  visibleVariables.forEach(variable => {
100
115
  if (variable.estObligatoire) {
@@ -129,10 +144,10 @@ const GroupRenderer = ({ groupe, responses, conditionEngine, onChange, onValidat
129
144
  });
130
145
  setValidationErrors(errors);
131
146
  onValidation(errors);
132
- }, [responses, visibleVariables, multipleEntries, groupe, onValidation]);
133
- // Exécuter les actions automatiques quand les réponses changent
147
+ }, [responses, visibleVariables, multipleEntries, groupe, onValidation, useFormTreeMode, formTree]);
148
+ // Exécuter les actions automatiques quand les réponses changent (mode legacy uniquement)
134
149
  useEffect(() => {
135
- if (!onAutoAction)
150
+ if (!onAutoAction || !engine || useFormTreeMode)
136
151
  return;
137
152
  const executeAutoActions = () => {
138
153
  const allActions = [];
@@ -140,21 +155,21 @@ const GroupRenderer = ({ groupe, responses, conditionEngine, onChange, onValidat
140
155
  visibleVariables.forEach(variable => {
141
156
  if (variable.conditionsAffichage && variable.conditionsAffichage.includes('setValeur(')) {
142
157
  // Évaluer la condition pour déclencher les actions automatiques
143
- conditionEngine.evaluate(variable.conditionsAffichage);
158
+ engine.evaluate(variable.conditionsAffichage);
144
159
  // Récupérer les actions générées
145
- const actions = conditionEngine.getAutoActions();
160
+ const actions = engine.getAutoActions();
146
161
  allActions.push(...actions);
147
162
  }
148
163
  });
149
164
  if (allActions.length > 0) {
150
165
  onAutoAction(allActions);
151
- conditionEngine.clearAutoActions(); // Nettoyer après exécution
166
+ engine.clearAutoActions(); // Nettoyer après exécution
152
167
  }
153
168
  };
154
169
  // Exécuter avec un délai pour éviter les boucles infinies
155
170
  const timeoutId = setTimeout(executeAutoActions, 100);
156
171
  return () => clearTimeout(timeoutId);
157
- }, [responses, visibleVariables, conditionEngine, onAutoAction]);
172
+ }, [responses, visibleVariables, engine, onAutoAction, useFormTreeMode]);
158
173
  // Gestion des entrées multiples
159
174
  const addEntry = () => {
160
175
  if (!groupe.configMultiple?.allowManualControl)
@@ -188,8 +203,19 @@ const GroupRenderer = ({ groupe, responses, conditionEngine, onChange, onValidat
188
203
  (Array.isArray(value) && value.length === 0);
189
204
  };
190
205
  // Vérifier si le groupe doit être affiché
191
- const shouldShowGroup = !groupe.conditionsAffichage ||
192
- conditionEngine.evaluate(groupe.conditionsAffichage);
206
+ const shouldShowGroup = useMemo(() => {
207
+ // Mode FormTree: vérifier l'état du groupe
208
+ if (useFormTreeMode && formTree) {
209
+ const groupState = formTree.getGroupState(groupe.code);
210
+ return groupState?.isVisible ?? true;
211
+ }
212
+ // Mode Legacy: évaluer la condition
213
+ if (!groupe.conditionsAffichage)
214
+ return true;
215
+ if (!engine)
216
+ return true;
217
+ return engine.evaluate(groupe.conditionsAffichage);
218
+ }, [useFormTreeMode, formTree, engine, groupe]);
193
219
  if (!shouldShowGroup) {
194
220
  return null;
195
221
  }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Hook React pour utiliser le FormTree
3
+ * RSU v2 - Gestion centralisée de l'état de visibilité et des jumps
4
+ */
5
+ import { GroupeFormulaire, EnqueteReponse, VariableFormulaire } from '../types/enquete';
6
+ import { FormTree } from '../lib/form-tree';
7
+ import { JumpError, JumpRange, VariableNodeState, GroupNodeState } from '../types/form-tree';
8
+ export interface UseFormTreeOptions {
9
+ /** Callback appelé en cas d'erreur de jump */
10
+ onJumpError?: (error: JumpError) => void;
11
+ /** Activer le logging de debug */
12
+ debug?: boolean;
13
+ }
14
+ export interface UseFormTreeReturn {
15
+ /** Instance du FormTree */
16
+ formTree: FormTree;
17
+ /** Retourne les variables visibles d'un groupe */
18
+ getVisibleVariables: (groupeCode: string) => VariableFormulaire[];
19
+ /** Retourne les codes des variables sautées dans un groupe */
20
+ getJumpedVariableCodes: (groupeCode: string) => Set<string>;
21
+ /** Valide un groupe et retourne les erreurs */
22
+ validateGroup: (groupeCode: string) => {
23
+ isValid: boolean;
24
+ errors: string[];
25
+ };
26
+ /** Retourne les codes des variables à effacer lors de la sauvegarde */
27
+ getVariablesToClearOnSave: () => string[];
28
+ /** Retourne l'état d'une variable */
29
+ getVariableState: (variableCode: string) => VariableNodeState | undefined;
30
+ /** Retourne l'état d'un groupe */
31
+ getGroupState: (groupeCode: string) => GroupNodeState | undefined;
32
+ /** Liste des erreurs de jump actuelles */
33
+ jumpErrors: JumpError[];
34
+ /** Liste des jumps actifs */
35
+ activeJumps: JumpRange[];
36
+ /** Force une réévaluation de toutes les conditions */
37
+ forceEvaluate: () => void;
38
+ /** Compteur de mise à jour (pour forcer le re-render) */
39
+ updateCount: number;
40
+ }
41
+ /**
42
+ * Hook pour utiliser le FormTree dans les composants React
43
+ *
44
+ * @param groupes Liste des groupes du formulaire
45
+ * @param responses Dictionnaire des réponses
46
+ * @param options Options de configuration
47
+ * @returns Objet avec les méthodes et états du FormTree
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * const {
52
+ * getVisibleVariables,
53
+ * validateGroup,
54
+ * jumpErrors,
55
+ * activeJumps
56
+ * } = useFormTree(formulaire.groupes, responses, {
57
+ * onJumpError: (error) => toast.error(error.message)
58
+ * });
59
+ *
60
+ * // Obtenir les variables visibles d'un groupe
61
+ * const visibleVars = getVisibleVariables(groupe.code);
62
+ *
63
+ * // Valider avant navigation
64
+ * const { isValid, errors } = validateGroup(groupe.code);
65
+ * ```
66
+ */
67
+ export declare function useFormTree(groupes: GroupeFormulaire[], responses: Record<string, EnqueteReponse>, options?: UseFormTreeOptions): UseFormTreeReturn;
68
+ export default useFormTree;
69
+ //# sourceMappingURL=useFormTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFormTree.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormTree.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE7F,MAAM,WAAW,kBAAkB;IACjC,8CAA8C;IAC9C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,kCAAkC;IAClC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,2BAA2B;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IAEnB,kDAAkD;IAClD,mBAAmB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,kBAAkB,EAAE,CAAC;IAElE,8DAA8D;IAC9D,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC;IAE5D,+CAA+C;IAC/C,aAAa,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAE9E,uEAAuE;IACvE,yBAAyB,EAAE,MAAM,MAAM,EAAE,CAAC;IAE1C,qCAAqC;IACrC,gBAAgB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,iBAAiB,GAAG,SAAS,CAAC;IAE1E,kCAAkC;IAClC,aAAa,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,cAAc,GAAG,SAAS,CAAC;IAElE,0CAA0C;IAC1C,UAAU,EAAE,SAAS,EAAE,CAAC;IAExB,6BAA6B;IAC7B,WAAW,EAAE,SAAS,EAAE,CAAC;IAEzB,sDAAsD;IACtD,aAAa,EAAE,MAAM,IAAI,CAAC;IAE1B,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EACzC,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAyFnB;AAED,eAAe,WAAW,CAAC"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Hook React pour utiliser le FormTree
3
+ * RSU v2 - Gestion centralisée de l'état de visibilité et des jumps
4
+ */
5
+ 'use client';
6
+ import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
7
+ import { FormTree } from '../lib/form-tree';
8
+ /**
9
+ * Hook pour utiliser le FormTree dans les composants React
10
+ *
11
+ * @param groupes Liste des groupes du formulaire
12
+ * @param responses Dictionnaire des réponses
13
+ * @param options Options de configuration
14
+ * @returns Objet avec les méthodes et états du FormTree
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * const {
19
+ * getVisibleVariables,
20
+ * validateGroup,
21
+ * jumpErrors,
22
+ * activeJumps
23
+ * } = useFormTree(formulaire.groupes, responses, {
24
+ * onJumpError: (error) => toast.error(error.message)
25
+ * });
26
+ *
27
+ * // Obtenir les variables visibles d'un groupe
28
+ * const visibleVars = getVisibleVariables(groupe.code);
29
+ *
30
+ * // Valider avant navigation
31
+ * const { isValid, errors } = validateGroup(groupe.code);
32
+ * ```
33
+ */
34
+ export function useFormTree(groupes, responses, options = {}) {
35
+ // Compteur pour forcer les re-renders
36
+ const [updateCount, setUpdateCount] = useState(0);
37
+ // Référence stable pour les options
38
+ const optionsRef = useRef(options);
39
+ optionsRef.current = options;
40
+ // Créer l'instance FormTree (stable tant que groupes ne changent pas)
41
+ const formTree = useMemo(() => {
42
+ const tree = new FormTree(responses, {
43
+ debug: optionsRef.current.debug,
44
+ onJumpError: optionsRef.current.onJumpError
45
+ });
46
+ tree.buildFromFormulaire(groupes);
47
+ return tree;
48
+ }, [groupes]); // Reconstruire seulement si groupes changent
49
+ // Mettre à jour le callback d'erreur quand il change
50
+ useEffect(() => {
51
+ formTree.onJumpError = options.onJumpError;
52
+ }, [formTree, options.onJumpError]);
53
+ // Mettre à jour quand les réponses changent
54
+ useEffect(() => {
55
+ formTree.updateResponses(responses);
56
+ setUpdateCount(c => c + 1);
57
+ }, [formTree, responses]);
58
+ // Fonctions de requête memoizées
59
+ const getVisibleVariables = useCallback((groupeCode) => formTree.getVisibleVariables(groupeCode), [formTree, updateCount]);
60
+ const getJumpedVariableCodes = useCallback((groupeCode) => formTree.getJumpedVariableCodes(groupeCode), [formTree, updateCount]);
61
+ const validateGroup = useCallback((groupeCode) => formTree.validateGroup(groupeCode), [formTree, updateCount]);
62
+ const getVariablesToClearOnSave = useCallback(() => formTree.getVariablesToClearOnSave(), [formTree, updateCount]);
63
+ const getVariableState = useCallback((variableCode) => formTree.getVariableState(variableCode), [formTree, updateCount]);
64
+ const getGroupState = useCallback((groupeCode) => formTree.getGroupState(groupeCode), [formTree, updateCount]);
65
+ const forceEvaluate = useCallback(() => {
66
+ formTree.evaluateAll();
67
+ setUpdateCount(c => c + 1);
68
+ }, [formTree]);
69
+ // Valeurs dérivées
70
+ const jumpErrors = useMemo(() => formTree.getJumpErrors(), [formTree, updateCount]);
71
+ const activeJumps = useMemo(() => formTree.getActiveJumps(), [formTree, updateCount]);
72
+ return {
73
+ formTree,
74
+ getVisibleVariables,
75
+ getJumpedVariableCodes,
76
+ validateGroup,
77
+ getVariablesToClearOnSave,
78
+ getVariableState,
79
+ getGroupState,
80
+ jumpErrors,
81
+ activeJumps,
82
+ forceEvaluate,
83
+ updateCount
84
+ };
85
+ }
86
+ export default useFormTree;
package/dist/index.d.ts CHANGED
@@ -34,8 +34,11 @@ export { default as EnqueteInput } from './components/selectors/EnqueteInput';
34
34
  export type { EnqueteReponse, VariableFormulaire, GroupeFormulaire, FormulaireComplet, EnqueteInstance, GroupeInstance, VariableValue, VariableType, ValidationError, AutoAction, NavigationState } from './types/enquete';
35
35
  export type { FormRendererServices, GeographicComponents, DistrictSelectorProps, RegionSelectorProps, DepartementSelectorProps, SousPrefectureSelectorProps, QuartierSelectorProps, VillageSelectorProps } from './types/services';
36
36
  export type { FormRendererProps, GroupRendererProps, VariableRendererProps } from './types/form-renderer';
37
+ export type { VariableNodeState, GroupNodeState, JumpRange, JumpError, IFormTree, FormTreeOptions, JumpEvaluationResult } from './types/form-tree';
37
38
  export { VariableValueConverter } from './lib/utils/variableValueConverter';
38
39
  export { interpolateVariableLabel } from './lib/utils/interpolateVariableLabel';
39
40
  export { ConditionEngine } from './lib/condition-engine';
41
+ export { FormTree } from './lib/form-tree';
42
+ export { useFormTree, type UseFormTreeOptions, type UseFormTreeReturn } from './hooks/useFormTree';
40
43
  export { cn } from './lib/utils/cn';
41
44
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -58,5 +58,12 @@ export { VariableValueConverter } from './lib/utils/variableValueConverter';
58
58
  export { interpolateVariableLabel } from './lib/utils/interpolateVariableLabel';
59
59
  // Moteur de conditions
60
60
  export { ConditionEngine } from './lib/condition-engine';
61
+ // FormTree (arbre virtuel du formulaire)
62
+ export { FormTree } from './lib/form-tree';
63
+ // ========================================
64
+ // HOOKS
65
+ // ========================================
66
+ // Hook FormTree
67
+ export { useFormTree } from './hooks/useFormTree';
61
68
  // Utilitaire CSS
62
69
  export { cn } from './lib/utils/cn';
@@ -0,0 +1,76 @@
1
+ /**
2
+ * FormTree - Arbre virtuel du formulaire
3
+ * RSU v2 - Gestion centralisée de l'état de visibilité et des jumps
4
+ *
5
+ * L'arbre virtuel (FormTree) est une représentation en mémoire de l'état du formulaire qui:
6
+ * 1. Connaît l'ordre de toutes les variables
7
+ * 2. Centralise l'évaluation des conditions
8
+ * 3. Stocke l'état de visibilité calculé
9
+ * 4. Gère nativement les jumps comme des "plages masquées"
10
+ */
11
+ import { VariableFormulaire, GroupeFormulaire, EnqueteReponse, VariableValue } from '../types/enquete';
12
+ import { IFormTree, VariableNodeState, GroupNodeState, JumpRange, JumpError, FormTreeOptions } from '../types/form-tree';
13
+ import { ConditionEngine } from './condition-engine';
14
+ export declare class FormTree implements IFormTree {
15
+ private groupNodes;
16
+ private variableNodes;
17
+ private orderedVariables;
18
+ private jumpRanges;
19
+ private jumpErrors;
20
+ private conditionEngine;
21
+ private responses;
22
+ private debug;
23
+ onJumpError?: (error: JumpError) => void;
24
+ constructor(responses?: Record<string, EnqueteReponse>, options?: FormTreeOptions);
25
+ private log;
26
+ buildFromFormulaire(groupes: GroupeFormulaire[]): void;
27
+ updateResponses(responses: Record<string, EnqueteReponse>): void;
28
+ evaluateAll(): void;
29
+ private evaluateGroupConditions;
30
+ private containsJump;
31
+ /**
32
+ * Évalue la partie non-jump d'une condition mixte
33
+ * Ex: "showMe(${A} == '1') || jump(${B} == '2', ${Q10})" -> évalue seulement showMe(${A} == '1')
34
+ */
35
+ private evaluateNonJumpPart;
36
+ private processJumpCondition;
37
+ private evaluateJump;
38
+ private applyJumpRanges;
39
+ private computeFinalVisibility;
40
+ getVisibleVariables(groupeCode: string): VariableFormulaire[];
41
+ getGroupState(groupeCode: string): GroupNodeState | undefined;
42
+ getVariableState(variableCode: string): VariableNodeState | undefined;
43
+ getJumpedVariableCodes(groupeCode: string): Set<string>;
44
+ getActiveJumps(): JumpRange[];
45
+ getJumpErrors(): JumpError[];
46
+ validateGroup(groupeCode: string): {
47
+ isValid: boolean;
48
+ errors: string[];
49
+ };
50
+ private isValueEmpty;
51
+ getVariablesToClearOnSave(): string[];
52
+ /**
53
+ * Expose le ConditionEngine pour les fonctionnalités existantes
54
+ * (showMe, hideMe, setValeur, etc.)
55
+ */
56
+ getConditionEngine(): ConditionEngine;
57
+ /**
58
+ * Retourne un snapshot de l'état complet pour debug
59
+ */
60
+ getDebugSnapshot(): {
61
+ groups: Array<{
62
+ code: string;
63
+ isVisible: boolean;
64
+ variableCount: number;
65
+ }>;
66
+ variables: Array<{
67
+ code: string;
68
+ isVisible: boolean;
69
+ isJumpedOver: boolean;
70
+ value: VariableValue;
71
+ }>;
72
+ activeJumps: JumpRange[];
73
+ errors: JumpError[];
74
+ };
75
+ }
76
+ //# sourceMappingURL=form-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-tree.d.ts","sourceRoot":"","sources":["../../src/lib/form-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,SAAS,EACT,eAAe,EAEhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,qBAAa,QAAS,YAAW,SAAS;IACxC,OAAO,CAAC,UAAU,CAA0C;IAC5D,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,gBAAgB,CAA4B;IACpD,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,KAAK,CAAU;IAEhB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;gBAG9C,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAM,EAC9C,OAAO,GAAE,eAAoB;IAU/B,OAAO,CAAC,GAAG;IAQX,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,IAAI;IAmDtD,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI;IAchE,WAAW,IAAI,IAAI;IA+BnB,OAAO,CAAC,uBAAuB;IAkC/B,OAAO,CAAC,YAAY;IAIpB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAiB3B,OAAO,CAAC,oBAAoB;IA6C5B,OAAO,CAAC,YAAY;IA+DpB,OAAO,CAAC,eAAe;IAsBvB,OAAO,CAAC,sBAAsB;IAY9B,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAS7D,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI7D,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIrE,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAYvD,cAAc,IAAI,SAAS,EAAE;IAI7B,aAAa,IAAI,SAAS,EAAE;IAM5B,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IA+BzE,OAAO,CAAC,YAAY;IAOpB,yBAAyB,IAAI,MAAM,EAAE;IAcrC;;;OAGG;IACH,kBAAkB,IAAI,eAAe;IAMrC;;OAEG;IACH,gBAAgB,IAAI;QAClB,MAAM,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,OAAO,CAAC;YAAC,aAAa,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC3E,SAAS,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,OAAO,CAAC;YAAC,YAAY,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,aAAa,CAAA;SAAE,CAAC,CAAC;QACpG,WAAW,EAAE,SAAS,EAAE,CAAC;QACzB,MAAM,EAAE,SAAS,EAAE,CAAC;KACrB;CAiBF"}
@@ -0,0 +1,371 @@
1
+ /**
2
+ * FormTree - Arbre virtuel du formulaire
3
+ * RSU v2 - Gestion centralisée de l'état de visibilité et des jumps
4
+ *
5
+ * L'arbre virtuel (FormTree) est une représentation en mémoire de l'état du formulaire qui:
6
+ * 1. Connaît l'ordre de toutes les variables
7
+ * 2. Centralise l'évaluation des conditions
8
+ * 3. Stocke l'état de visibilité calculé
9
+ * 4. Gère nativement les jumps comme des "plages masquées"
10
+ */
11
+ import { ConditionEngine } from './condition-engine';
12
+ export class FormTree {
13
+ constructor(responses = {}, options = {}) {
14
+ this.groupNodes = new Map();
15
+ this.variableNodes = new Map();
16
+ this.orderedVariables = [];
17
+ this.jumpRanges = [];
18
+ this.jumpErrors = [];
19
+ this.responses = responses;
20
+ this.conditionEngine = new ConditionEngine(responses);
21
+ this.onJumpError = options.onJumpError;
22
+ this.debug = options.debug ?? false;
23
+ }
24
+ // ============ LOGGING ============
25
+ log(message, ...args) {
26
+ if (this.debug) {
27
+ console.log(`🌳 [FormTree] ${message}`, ...args);
28
+ }
29
+ }
30
+ // ============ CONSTRUCTION ============
31
+ buildFromFormulaire(groupes) {
32
+ this.log('Building tree from', groupes.length, 'groups');
33
+ this.groupNodes.clear();
34
+ this.variableNodes.clear();
35
+ this.orderedVariables = [];
36
+ // Trier les groupes par ordre
37
+ const sortedGroupes = [...groupes].sort((a, b) => a.ordre - b.ordre);
38
+ for (const groupe of sortedGroupes) {
39
+ // Trier les variables par ordre dans le groupe
40
+ const sortedVariables = [...groupe.variables].sort((a, b) => a.ordre - b.ordre);
41
+ const variableStates = sortedVariables.map(variable => {
42
+ const state = {
43
+ variable,
44
+ isVisible: variable.estVisible,
45
+ isJumpedOver: false,
46
+ isConditionMet: true,
47
+ isValid: true,
48
+ skipValidation: false,
49
+ currentValue: this.responses[variable.code]?.valeur ?? null,
50
+ shouldClearOnSave: false
51
+ };
52
+ this.variableNodes.set(variable.code, state);
53
+ this.orderedVariables.push(variable);
54
+ return state;
55
+ });
56
+ const groupState = {
57
+ groupe,
58
+ variables: variableStates,
59
+ isVisible: true,
60
+ isComplete: false,
61
+ validationErrors: [],
62
+ iterations: new Map()
63
+ };
64
+ this.groupNodes.set(groupe.code, groupState);
65
+ this.log(`Group ${groupe.code} built with ${variableStates.length} variables`);
66
+ }
67
+ // Évaluer toutes les conditions initiales
68
+ this.evaluateAll();
69
+ }
70
+ // ============ MISE À JOUR ============
71
+ updateResponses(responses) {
72
+ this.log('Updating responses');
73
+ this.responses = responses;
74
+ this.conditionEngine.updateContext(responses);
75
+ // Mettre à jour les valeurs dans les noeuds
76
+ for (const [code, state] of this.variableNodes) {
77
+ state.currentValue = responses[code]?.valeur ?? null;
78
+ }
79
+ // Réévaluer toutes les conditions
80
+ this.evaluateAll();
81
+ }
82
+ evaluateAll() {
83
+ this.log('Evaluating all conditions');
84
+ // Reset des états
85
+ this.jumpRanges = [];
86
+ this.jumpErrors = [];
87
+ for (const state of this.variableNodes.values()) {
88
+ state.isJumpedOver = false;
89
+ state.isConditionMet = true;
90
+ state.skipValidation = false;
91
+ state.shouldClearOnSave = false;
92
+ }
93
+ // Premier passage: évaluer les conditions et détecter les jumps
94
+ for (const groupNode of this.groupNodes.values()) {
95
+ this.evaluateGroupConditions(groupNode);
96
+ }
97
+ // Deuxième passage: appliquer les jumps
98
+ this.applyJumpRanges();
99
+ // Troisième passage: calculer la visibilité finale
100
+ this.computeFinalVisibility();
101
+ this.log('Evaluation complete:', {
102
+ activeJumps: this.jumpRanges.filter(j => j.isActive).length,
103
+ jumpErrors: this.jumpErrors.length
104
+ });
105
+ }
106
+ evaluateGroupConditions(groupNode) {
107
+ const { groupe } = groupNode;
108
+ this.log(`Evaluating group ${groupe.code}`);
109
+ // Évaluer la condition du groupe
110
+ if (groupe.conditionsAffichage) {
111
+ groupNode.isVisible = this.conditionEngine.evaluate(groupe.conditionsAffichage);
112
+ this.log(`Group ${groupe.code} visibility:`, groupNode.isVisible);
113
+ }
114
+ if (!groupNode.isVisible)
115
+ return;
116
+ // Évaluer les conditions de chaque variable
117
+ for (const varState of groupNode.variables) {
118
+ const condition = varState.variable.conditionsAffichage;
119
+ if (!condition) {
120
+ varState.isConditionMet = true;
121
+ continue;
122
+ }
123
+ // Détecter et traiter les jumps
124
+ if (this.containsJump(condition)) {
125
+ this.processJumpCondition(varState, groupe.code, condition);
126
+ // La variable source du jump reste visible (sa condition propre est évaluée séparément)
127
+ varState.isConditionMet = this.evaluateNonJumpPart(condition);
128
+ }
129
+ else {
130
+ varState.isConditionMet = this.conditionEngine.evaluate(condition);
131
+ }
132
+ this.log(`Variable ${varState.variable.code} conditionMet:`, varState.isConditionMet);
133
+ }
134
+ }
135
+ containsJump(condition) {
136
+ return /jump\s*\(/.test(condition);
137
+ }
138
+ /**
139
+ * Évalue la partie non-jump d'une condition mixte
140
+ * Ex: "showMe(${A} == '1') || jump(${B} == '2', ${Q10})" -> évalue seulement showMe(${A} == '1')
141
+ */
142
+ evaluateNonJumpPart(condition) {
143
+ // Retirer les appels jump() de la condition
144
+ const withoutJumps = condition
145
+ .replace(/jump\s*\([^)]*\)/g, 'true')
146
+ .replace(/\|\|\s*true/g, '')
147
+ .replace(/true\s*\|\|/g, '')
148
+ .replace(/&&\s*true/g, '')
149
+ .replace(/true\s*&&/g, '')
150
+ .trim();
151
+ if (!withoutJumps || withoutJumps === 'true') {
152
+ return true;
153
+ }
154
+ return this.conditionEngine.evaluate(withoutJumps);
155
+ }
156
+ processJumpCondition(sourceState, groupeCode, condition) {
157
+ this.log(`Processing jump condition for ${sourceState.variable.code}:`, condition);
158
+ // Parser tous les jumps dans la condition
159
+ // Pattern: jump(condition, ${VARIABLE_CIBLE})
160
+ const jumpPattern = /jump\s*\(\s*(.+?)\s*,\s*\$\{([A-Z_][A-Z0-9_]*)\}\s*\)/g;
161
+ let match;
162
+ while ((match = jumpPattern.exec(condition)) !== null) {
163
+ const innerCondition = match[1].trim();
164
+ const targetCode = match[2];
165
+ this.log(`Found jump: condition="${innerCondition}", target="${targetCode}"`);
166
+ const result = this.evaluateJump(sourceState, groupeCode, innerCondition, targetCode);
167
+ if (result.error) {
168
+ this.jumpErrors.push(result.error);
169
+ this.onJumpError?.(result.error);
170
+ continue;
171
+ }
172
+ if (result.canExecute && result.targetCode) {
173
+ const targetState = this.variableNodes.get(result.targetCode);
174
+ const jumpRange = {
175
+ sourceCode: sourceState.variable.code,
176
+ sourceOrdre: sourceState.variable.ordre,
177
+ targetCode: result.targetCode,
178
+ targetOrdre: targetState.variable.ordre,
179
+ groupeCode,
180
+ condition: innerCondition,
181
+ isActive: result.shouldActivate
182
+ };
183
+ this.jumpRanges.push(jumpRange);
184
+ this.log(`Jump range created:`, jumpRange);
185
+ }
186
+ }
187
+ }
188
+ evaluateJump(sourceState, groupeCode, innerCondition, targetCode) {
189
+ const result = {
190
+ isValidSyntax: true,
191
+ canExecute: false,
192
+ shouldActivate: false,
193
+ targetCode: null
194
+ };
195
+ // Validation: cible existe?
196
+ const targetState = this.variableNodes.get(targetCode);
197
+ if (!targetState) {
198
+ result.error = {
199
+ type: 'invalid_target',
200
+ sourceVariable: sourceState.variable.code,
201
+ targetVariable: targetCode,
202
+ message: `Variable cible "${targetCode}" non trouvée pour le jump depuis "${sourceState.variable.code}"`
203
+ };
204
+ return result;
205
+ }
206
+ // Validation: même groupe?
207
+ if (targetState.variable.groupeCode !== groupeCode) {
208
+ result.error = {
209
+ type: 'cross_group_jump',
210
+ sourceVariable: sourceState.variable.code,
211
+ targetVariable: targetCode,
212
+ message: `Jump inter-groupe non autorisé: "${sourceState.variable.code}" (${groupeCode}) → "${targetCode}" (${targetState.variable.groupeCode})`
213
+ };
214
+ return result;
215
+ }
216
+ // Validation: direction avant?
217
+ if (targetState.variable.ordre <= sourceState.variable.ordre) {
218
+ result.error = {
219
+ type: 'backward_jump',
220
+ sourceVariable: sourceState.variable.code,
221
+ targetVariable: targetCode,
222
+ message: `Jump arrière non autorisé: "${sourceState.variable.code}" (ordre ${sourceState.variable.ordre}) → "${targetCode}" (ordre ${targetState.variable.ordre})`
223
+ };
224
+ return result;
225
+ }
226
+ // Le jump est valide
227
+ result.canExecute = true;
228
+ result.targetCode = targetCode;
229
+ // Évaluer la condition du jump
230
+ try {
231
+ result.shouldActivate = this.conditionEngine.evaluate(innerCondition);
232
+ this.log(`Jump condition "${innerCondition}" evaluated to:`, result.shouldActivate);
233
+ }
234
+ catch (e) {
235
+ this.log(`Error evaluating jump condition:`, e);
236
+ result.shouldActivate = false;
237
+ }
238
+ return result;
239
+ }
240
+ applyJumpRanges() {
241
+ for (const jump of this.jumpRanges) {
242
+ if (!jump.isActive)
243
+ continue;
244
+ this.log(`Applying jump from ${jump.sourceCode} to ${jump.targetCode}`);
245
+ // Marquer toutes les variables entre source et cible comme "jumped over"
246
+ for (const [code, state] of this.variableNodes) {
247
+ if (state.variable.groupeCode !== jump.groupeCode)
248
+ continue;
249
+ const ordre = state.variable.ordre;
250
+ // Variables strictement entre source et cible (exclusif aux deux bornes)
251
+ if (ordre > jump.sourceOrdre && ordre < jump.targetOrdre) {
252
+ state.isJumpedOver = true;
253
+ state.skipValidation = true;
254
+ state.shouldClearOnSave = true;
255
+ this.log(`Variable ${code} marked as jumped over`);
256
+ }
257
+ }
258
+ }
259
+ }
260
+ computeFinalVisibility() {
261
+ for (const state of this.variableNodes.values()) {
262
+ // Visible = base visible ET condition satisfaite ET pas sauté
263
+ state.isVisible =
264
+ state.variable.estVisible &&
265
+ state.isConditionMet &&
266
+ !state.isJumpedOver;
267
+ }
268
+ }
269
+ // ============ REQUÊTES ============
270
+ getVisibleVariables(groupeCode) {
271
+ const groupNode = this.groupNodes.get(groupeCode);
272
+ if (!groupNode || !groupNode.isVisible)
273
+ return [];
274
+ return groupNode.variables
275
+ .filter(state => state.isVisible)
276
+ .map(state => state.variable);
277
+ }
278
+ getGroupState(groupeCode) {
279
+ return this.groupNodes.get(groupeCode);
280
+ }
281
+ getVariableState(variableCode) {
282
+ return this.variableNodes.get(variableCode);
283
+ }
284
+ getJumpedVariableCodes(groupeCode) {
285
+ const codes = new Set();
286
+ for (const [code, state] of this.variableNodes) {
287
+ if (state.variable.groupeCode === groupeCode && state.isJumpedOver) {
288
+ codes.add(code);
289
+ }
290
+ }
291
+ return codes;
292
+ }
293
+ getActiveJumps() {
294
+ return this.jumpRanges.filter(j => j.isActive);
295
+ }
296
+ getJumpErrors() {
297
+ return [...this.jumpErrors];
298
+ }
299
+ // ============ VALIDATION ============
300
+ validateGroup(groupeCode) {
301
+ const groupNode = this.groupNodes.get(groupeCode);
302
+ if (!groupNode)
303
+ return { isValid: true, errors: [] };
304
+ const errors = [];
305
+ for (const state of groupNode.variables) {
306
+ // Ignorer les variables masquées ou sautées
307
+ if (!state.isVisible || state.skipValidation)
308
+ continue;
309
+ // Vérifier les champs obligatoires
310
+ if (state.variable.estObligatoire) {
311
+ const isEmpty = this.isValueEmpty(state.currentValue);
312
+ if (isEmpty) {
313
+ errors.push(`${state.variable.designation} est obligatoire`);
314
+ state.isValid = false;
315
+ state.validationError = 'Champ obligatoire';
316
+ }
317
+ else {
318
+ state.isValid = true;
319
+ state.validationError = undefined;
320
+ }
321
+ }
322
+ }
323
+ groupNode.validationErrors = errors;
324
+ groupNode.isComplete = errors.length === 0;
325
+ return { isValid: errors.length === 0, errors };
326
+ }
327
+ isValueEmpty(value) {
328
+ return value === null ||
329
+ value === undefined ||
330
+ value === '' ||
331
+ (Array.isArray(value) && value.length === 0);
332
+ }
333
+ getVariablesToClearOnSave() {
334
+ const codes = [];
335
+ for (const [code, state] of this.variableNodes) {
336
+ if (state.shouldClearOnSave && !this.isValueEmpty(state.currentValue)) {
337
+ codes.push(code);
338
+ }
339
+ }
340
+ return codes;
341
+ }
342
+ // ============ ACCÈS AU CONDITION ENGINE ============
343
+ /**
344
+ * Expose le ConditionEngine pour les fonctionnalités existantes
345
+ * (showMe, hideMe, setValeur, etc.)
346
+ */
347
+ getConditionEngine() {
348
+ return this.conditionEngine;
349
+ }
350
+ // ============ DEBUG ============
351
+ /**
352
+ * Retourne un snapshot de l'état complet pour debug
353
+ */
354
+ getDebugSnapshot() {
355
+ return {
356
+ groups: Array.from(this.groupNodes.values()).map(g => ({
357
+ code: g.groupe.code,
358
+ isVisible: g.isVisible,
359
+ variableCount: g.variables.length
360
+ })),
361
+ variables: Array.from(this.variableNodes.values()).map(v => ({
362
+ code: v.variable.code,
363
+ isVisible: v.isVisible,
364
+ isJumpedOver: v.isJumpedOver,
365
+ value: v.currentValue
366
+ })),
367
+ activeJumps: this.getActiveJumps(),
368
+ errors: this.jumpErrors
369
+ };
370
+ }
371
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Types et interfaces pour le FormTree (arbre virtuel du formulaire)
3
+ * RSU v2 - Gestion centralisée de l'état de visibilité et des jumps
4
+ */
5
+ import { VariableFormulaire, GroupeFormulaire, EnqueteReponse, VariableValue } from './enquete';
6
+ /**
7
+ * État d'un noeud variable dans l'arbre
8
+ */
9
+ export interface VariableNodeState {
10
+ /** Référence à la variable du formulaire */
11
+ variable: VariableFormulaire;
12
+ /** Résultat final de visibilité (estVisible && conditionMet && !jumpedOver) */
13
+ isVisible: boolean;
14
+ /** Masqué par un jump actif */
15
+ isJumpedOver: boolean;
16
+ /** Condition d'affichage satisfaite */
17
+ isConditionMet: boolean;
18
+ /** Passe la validation */
19
+ isValid: boolean;
20
+ /** Message d'erreur si invalide */
21
+ validationError?: string;
22
+ /** Ignorer validation (jumped over) */
23
+ skipValidation: boolean;
24
+ /** Valeur actuelle de la variable */
25
+ currentValue: VariableValue;
26
+ /** Effacer à la sauvegarde (jumped) */
27
+ shouldClearOnSave: boolean;
28
+ }
29
+ /**
30
+ * État d'un noeud groupe dans l'arbre
31
+ */
32
+ export interface GroupNodeState {
33
+ /** Référence au groupe du formulaire */
34
+ groupe: GroupeFormulaire;
35
+ /** États des variables de ce groupe */
36
+ variables: VariableNodeState[];
37
+ /** Le groupe est visible */
38
+ isVisible: boolean;
39
+ /** Toutes les variables obligatoires sont remplies */
40
+ isComplete: boolean;
41
+ /** Erreurs de validation du groupe */
42
+ validationErrors: string[];
43
+ /** Map des itérations avec leurs états de variables */
44
+ iterations: Map<number, VariableNodeState[]>;
45
+ }
46
+ /**
47
+ * Plage de jump actif
48
+ * Représente un saut conditionnel entre deux variables
49
+ */
50
+ export interface JumpRange {
51
+ /** Code de la variable source du jump */
52
+ sourceCode: string;
53
+ /** Ordre de la variable source */
54
+ sourceOrdre: number;
55
+ /** Code de la variable cible du jump */
56
+ targetCode: string;
57
+ /** Ordre de la variable cible */
58
+ targetOrdre: number;
59
+ /** Code du groupe contenant le jump */
60
+ groupeCode: string;
61
+ /** Condition d'activation du jump */
62
+ condition: string;
63
+ /** Le jump est actuellement actif */
64
+ isActive: boolean;
65
+ }
66
+ /**
67
+ * Erreur de jump pour notification
68
+ */
69
+ export interface JumpError {
70
+ /** Type d'erreur */
71
+ type: 'backward_jump' | 'invalid_target' | 'circular_jump' | 'cross_group_jump';
72
+ /** Code de la variable source */
73
+ sourceVariable: string;
74
+ /** Code de la variable cible */
75
+ targetVariable: string;
76
+ /** Message d'erreur descriptif */
77
+ message: string;
78
+ }
79
+ /**
80
+ * Interface principale du FormTree
81
+ */
82
+ export interface IFormTree {
83
+ /**
84
+ * Construit l'arbre à partir des groupes du formulaire
85
+ * @param groupes Liste des groupes du formulaire
86
+ */
87
+ buildFromFormulaire(groupes: GroupeFormulaire[]): void;
88
+ /**
89
+ * Met à jour les réponses et réévalue toutes les conditions
90
+ * @param responses Dictionnaire des réponses
91
+ */
92
+ updateResponses(responses: Record<string, EnqueteReponse>): void;
93
+ /**
94
+ * Évalue toutes les conditions de visibilité et jumps
95
+ */
96
+ evaluateAll(): void;
97
+ /**
98
+ * Retourne les variables visibles d'un groupe
99
+ * @param groupeCode Code du groupe
100
+ */
101
+ getVisibleVariables(groupeCode: string): VariableFormulaire[];
102
+ /**
103
+ * Retourne l'état d'un groupe
104
+ * @param groupeCode Code du groupe
105
+ */
106
+ getGroupState(groupeCode: string): GroupNodeState | undefined;
107
+ /**
108
+ * Retourne l'état d'une variable
109
+ * @param variableCode Code de la variable
110
+ */
111
+ getVariableState(variableCode: string): VariableNodeState | undefined;
112
+ /**
113
+ * Retourne les codes des variables sautées dans un groupe
114
+ * @param groupeCode Code du groupe
115
+ */
116
+ getJumpedVariableCodes(groupeCode: string): Set<string>;
117
+ /**
118
+ * Retourne tous les jumps actifs
119
+ */
120
+ getActiveJumps(): JumpRange[];
121
+ /**
122
+ * Retourne toutes les erreurs de jump détectées
123
+ */
124
+ getJumpErrors(): JumpError[];
125
+ /**
126
+ * Valide un groupe et retourne les erreurs
127
+ * @param groupeCode Code du groupe
128
+ */
129
+ validateGroup(groupeCode: string): {
130
+ isValid: boolean;
131
+ errors: string[];
132
+ };
133
+ /**
134
+ * Retourne les codes des variables à effacer lors de la sauvegarde
135
+ */
136
+ getVariablesToClearOnSave(): string[];
137
+ /** Callback appelé en cas d'erreur de jump */
138
+ onJumpError?: (error: JumpError) => void;
139
+ }
140
+ /**
141
+ * Options pour la création d'un FormTree
142
+ */
143
+ export interface FormTreeOptions {
144
+ /** Callback en cas d'erreur de jump */
145
+ onJumpError?: (error: JumpError) => void;
146
+ /** Activer le logging de debug */
147
+ debug?: boolean;
148
+ }
149
+ /**
150
+ * Résultat de l'évaluation d'une condition de jump
151
+ */
152
+ export interface JumpEvaluationResult {
153
+ /** La condition est valide syntaxiquement */
154
+ isValidSyntax: boolean;
155
+ /** Le jump peut être exécuté (cible valide, direction correcte) */
156
+ canExecute: boolean;
157
+ /** Le jump doit être activé (condition satisfaite) */
158
+ shouldActivate: boolean;
159
+ /** Code de la variable cible */
160
+ targetCode: string | null;
161
+ /** Erreur éventuelle */
162
+ error?: JumpError;
163
+ }
164
+ //# sourceMappingURL=form-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-tree.d.ts","sourceRoot":"","sources":["../../src/types/form-tree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Types et interfaces pour le FormTree (arbre virtuel du formulaire)
3
+ * RSU v2 - Gestion centralisée de l'état de visibilité et des jumps
4
+ */
5
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsuci/shared-form-components",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "Composants partagés de rendu de formulaires RSU v2 - Package local pour frontend Admin et Public",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",