@rsuci/shared-form-components 1.0.16 → 1.0.18
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/roster/RosterPanel.d.ts +1 -0
- package/dist/components/roster/RosterPanel.d.ts.map +1 -1
- package/dist/components/roster/RosterPanel.js +22 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lib/roster-condition-engine.d.ts +75 -0
- package/dist/lib/roster-condition-engine.d.ts.map +1 -0
- package/dist/lib/roster-condition-engine.js +243 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RosterPanel.d.ts","sourceRoot":"","sources":["../../../src/components/roster/RosterPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,
|
|
1
|
+
{"version":3,"file":"RosterPanel.d.ts","sourceRoot":"","sources":["../../../src/components/roster/RosterPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAkB,MAAM,OAAO,CAAC;AAOvC,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAGD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAGD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IACrD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC5C;AAED,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA+J3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -7,12 +7,33 @@
|
|
|
7
7
|
*/
|
|
8
8
|
'use client';
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { useMemo } from 'react';
|
|
10
11
|
import VariableRenderer from '../VariableRenderer';
|
|
11
12
|
import { cn } from '../../lib/utils/cn';
|
|
13
|
+
import { RosterConditionEngine } from '../../lib/roster-condition-engine';
|
|
12
14
|
const RosterPanel = ({ option, rosterVariables, value, onChange, disabled, showCheckbox = false, isChecked = true, onCheckChange }) => {
|
|
13
15
|
// Trier les variables par ordre
|
|
14
16
|
const sortedVariables = [...rosterVariables].sort((a, b) => a.ordre - b.ordre);
|
|
15
|
-
|
|
17
|
+
// Calculer les variables masquées par les jumps
|
|
18
|
+
const jumpedVariables = useMemo(() => {
|
|
19
|
+
return RosterConditionEngine.computeJumpedVariables(sortedVariables.map(v => ({ code: v.code, ordre: v.ordre, conditionsAffichage: v.conditionsAffichage })), value);
|
|
20
|
+
}, [sortedVariables, value]);
|
|
21
|
+
// Filtrer les variables visibles (conditions + jumps)
|
|
22
|
+
const visibleVariables = useMemo(() => {
|
|
23
|
+
return sortedVariables.filter(rosterVar => {
|
|
24
|
+
// Variable masquée par un jump actif
|
|
25
|
+
if (jumpedVariables.has(rosterVar.code)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
// Pas de condition = visible
|
|
29
|
+
if (!rosterVar.conditionsAffichage) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
// Évaluer la condition avec les valeurs de cette ligne
|
|
33
|
+
return RosterConditionEngine.evaluate(rosterVar.conditionsAffichage, value);
|
|
34
|
+
});
|
|
35
|
+
}, [sortedVariables, jumpedVariables, value]);
|
|
36
|
+
return (_jsxs("div", { className: cn('border border-gray-200 rounded-lg p-4 bg-white', 'shadow-sm hover:shadow-md transition-shadow', disabled && 'opacity-60'), children: [_jsx("div", { className: "border-b border-gray-100 pb-3 mb-4", children: _jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { className: "flex items-center flex-1", children: [showCheckbox && (_jsx("input", { type: "checkbox", checked: isChecked, onChange: (e) => onCheckChange?.(e.target.checked), disabled: disabled, className: cn('w-5 h-5 mr-3 text-green-600 border-gray-300 rounded focus:ring-green-500', disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer') })), _jsxs("div", { children: [_jsxs("h3", { className: "font-semibold text-lg text-gray-800 flex items-center", children: [_jsx("span", { className: "mr-2", children: "\uD83D\uDCCC" }), option.designation] }), _jsxs("p", { className: "text-xs text-blue-600 font-mono mt-1", children: ["(", option.code, ")"] })] })] }) }) }), !showCheckbox || isChecked ? (_jsx("div", { className: "space-y-4", children: visibleVariables.map(rosterVar => {
|
|
16
37
|
// Créer un objet VariableFormulaire compatible avec VariableRenderer
|
|
17
38
|
// Rendre le code unique par panel pour éviter les conflits de radios/checkboxes
|
|
18
39
|
const uniqueCode = `${option.code}_${rosterVar.code}`;
|
package/dist/index.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export type { VariableNodeState, GroupNodeState, JumpRange, JumpError, IFormTree
|
|
|
38
38
|
export { VariableValueConverter } from './lib/utils/variableValueConverter';
|
|
39
39
|
export { interpolateVariableLabel } from './lib/utils/interpolateVariableLabel';
|
|
40
40
|
export { ConditionEngine } from './lib/condition-engine';
|
|
41
|
+
export { RosterConditionEngine, type RosterConditionError, type RosterConditionErrorType, type RosterConditionValidationResult, type RosterVariableRef } from './lib/roster-condition-engine';
|
|
41
42
|
export { FormTree } from './lib/form-tree';
|
|
42
43
|
export { useFormTree, type UseFormTreeOptions, type UseFormTreeReturn } from './hooks/useFormTree';
|
|
43
44
|
export { cn } from './lib/utils/cn';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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":"AAAA;;;GAGG;AAOH,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAG5E,OAAO,EACL,OAAO,IAAI,gBAAgB,EAC3B,KAAK,qBAAqB,EAC3B,MAAM,sCAAsC,CAAC;AAE9C,OAAO,EACL,OAAO,IAAI,eAAe,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EACL,OAAO,IAAI,WAAW,EACtB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACpB,MAAM,iCAAiC,CAAC;AAOzC,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAGrE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAG3E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAM5E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACvF,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AAC7F,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAM/E,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAO9E,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,YAAY,EACZ,eAAe,EACf,UAAU,EACV,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAGzB,YAAY,EACV,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,wBAAwB,EACxB,2BAA2B,EAC3B,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACtB,MAAM,uBAAuB,CAAC;AAG/B,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,SAAS,EACT,SAAS,EACT,eAAe,EACf,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAO3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAG5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAGhF,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,EACL,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,+BAA+B,EACpC,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAO3C,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGnG,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -58,6 +58,8 @@ 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
|
+
// Moteur de conditions scopées pour les ROSTER
|
|
62
|
+
export { RosterConditionEngine } from './lib/roster-condition-engine';
|
|
61
63
|
// FormTree (arbre virtuel du formulaire)
|
|
62
64
|
export { FormTree } from './lib/form-tree';
|
|
63
65
|
// ========================================
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moteur d'évaluation des conditions scopées pour les variables ROSTER
|
|
3
|
+
* RSU v2 - Moteur de Rendu des Formulaires d'Enquête
|
|
4
|
+
*
|
|
5
|
+
* Ce moteur est ISOLÉ du ConditionEngine principal.
|
|
6
|
+
* Les conditions sont évaluées PAR LIGNE (par option de roster) indépendamment.
|
|
7
|
+
* Le scope est limité aux variables du même ROSTER uniquement.
|
|
8
|
+
*/
|
|
9
|
+
export type RosterConditionErrorType = 'variable_not_in_scope' | 'jump_out_of_scope' | 'backward_jump' | 'self_reference' | 'syntax_error';
|
|
10
|
+
export interface RosterConditionError {
|
|
11
|
+
type: RosterConditionErrorType;
|
|
12
|
+
variableCode: string;
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
export interface RosterConditionValidationResult {
|
|
16
|
+
isValid: boolean;
|
|
17
|
+
errors: RosterConditionError[];
|
|
18
|
+
}
|
|
19
|
+
export interface RosterVariableRef {
|
|
20
|
+
code: string;
|
|
21
|
+
ordre: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Moteur de conditions scopées pour les ROSTER
|
|
25
|
+
* Toutes les méthodes sont statiques pour une utilisation sans état
|
|
26
|
+
*/
|
|
27
|
+
export declare class RosterConditionEngine {
|
|
28
|
+
/**
|
|
29
|
+
* Valide une condition au moment du design (admin)
|
|
30
|
+
* Vérifie que les références sont dans le scope et que les jumps sont valides
|
|
31
|
+
*/
|
|
32
|
+
static validateCondition(condition: string, availableVariables: RosterVariableRef[], currentVariableCode: string, currentVariableOrdre: number): RosterConditionValidationResult;
|
|
33
|
+
/**
|
|
34
|
+
* Évalue une condition au runtime (rendu)
|
|
35
|
+
* Utilise les valeurs de la ligne courante du roster
|
|
36
|
+
*/
|
|
37
|
+
static evaluate(condition: string, lineValues: Record<string, any>): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Extrait toutes les références de variables d'une condition
|
|
40
|
+
* Format: ${VAR_CODE}
|
|
41
|
+
*/
|
|
42
|
+
static extractVariableReferences(condition: string): string[];
|
|
43
|
+
/**
|
|
44
|
+
* Extrait la cible d'un jump s'il existe dans la condition (premier match uniquement)
|
|
45
|
+
* Format: jump(condition, ${TARGET})
|
|
46
|
+
* @deprecated Utiliser extractAllJumps pour supporter les jumps multiples
|
|
47
|
+
*/
|
|
48
|
+
static extractJumpTarget(condition: string): string | null;
|
|
49
|
+
/**
|
|
50
|
+
* Extrait TOUS les jumps d'une condition
|
|
51
|
+
* Supporte: jump(cond1, ${VAR1}) || jump(cond2, ${VAR2})
|
|
52
|
+
* Retourne un tableau de { fullExpression, innerCondition, target }
|
|
53
|
+
*/
|
|
54
|
+
static extractAllJumps(condition: string): Array<{
|
|
55
|
+
fullExpression: string;
|
|
56
|
+
innerCondition: string;
|
|
57
|
+
target: string;
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* Calcule les variables qui doivent être masquées à cause des jumps actifs
|
|
61
|
+
* Supporte les jumps multiples avec court-circuit:
|
|
62
|
+
* - jump(cond1, ${VAR1}) || jump(cond2, ${VAR2})
|
|
63
|
+
* - Si cond1 est vraie, on saute à VAR1 et cond2 n'est pas évaluée
|
|
64
|
+
*/
|
|
65
|
+
static computeJumpedVariables(sortedVariables: RosterVariableRef[], lineValues: Record<string, any>): Set<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Évalue une condition interne (sans le wrapper jump())
|
|
68
|
+
*/
|
|
69
|
+
private static evaluateInnerCondition;
|
|
70
|
+
/**
|
|
71
|
+
* Valide la syntaxe de base d'une condition
|
|
72
|
+
*/
|
|
73
|
+
private static validateSyntax;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=roster-condition-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roster-condition-engine.d.ts","sourceRoot":"","sources":["../../src/lib/roster-condition-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,wBAAwB,GAChC,uBAAuB,GACvB,mBAAmB,GACnB,eAAe,GACf,gBAAgB,GAChB,cAAc,CAAC;AAEnB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,wBAAwB,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,qBAAa,qBAAqB;IAEhC;;;OAGG;IACH,MAAM,CAAC,iBAAiB,CACtB,SAAS,EAAE,MAAM,EACjB,kBAAkB,EAAE,iBAAiB,EAAE,EACvC,mBAAmB,EAAE,MAAM,EAC3B,oBAAoB,EAAE,MAAM,GAC3B,+BAA+B;IAgElC;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO;IAyB5E;;;OAGG;IACH,MAAM,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAO7D;;;;OAIG;IACH,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAO1D;;;;OAIG;IACH,MAAM,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC;QAC/C,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,MAAM,CAAC;QACvB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IAoBF;;;;;OAKG;IACH,MAAM,CAAC,sBAAsB,CAC3B,eAAe,EAAE,iBAAiB,EAAE,EACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC9B,GAAG,CAAC,MAAM,CAAC;IAmCd;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,sBAAsB;IA6BrC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;CA0B9B"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moteur d'évaluation des conditions scopées pour les variables ROSTER
|
|
3
|
+
* RSU v2 - Moteur de Rendu des Formulaires d'Enquête
|
|
4
|
+
*
|
|
5
|
+
* Ce moteur est ISOLÉ du ConditionEngine principal.
|
|
6
|
+
* Les conditions sont évaluées PAR LIGNE (par option de roster) indépendamment.
|
|
7
|
+
* Le scope est limité aux variables du même ROSTER uniquement.
|
|
8
|
+
*/
|
|
9
|
+
import { ConditionEngine } from './condition-engine';
|
|
10
|
+
/**
|
|
11
|
+
* Moteur de conditions scopées pour les ROSTER
|
|
12
|
+
* Toutes les méthodes sont statiques pour une utilisation sans état
|
|
13
|
+
*/
|
|
14
|
+
export class RosterConditionEngine {
|
|
15
|
+
/**
|
|
16
|
+
* Valide une condition au moment du design (admin)
|
|
17
|
+
* Vérifie que les références sont dans le scope et que les jumps sont valides
|
|
18
|
+
*/
|
|
19
|
+
static validateCondition(condition, availableVariables, currentVariableCode, currentVariableOrdre) {
|
|
20
|
+
const errors = [];
|
|
21
|
+
if (!condition?.trim()) {
|
|
22
|
+
return { isValid: true, errors: [] };
|
|
23
|
+
}
|
|
24
|
+
const availableCodes = new Set(availableVariables.map(v => v.code));
|
|
25
|
+
// 1. Vérifier les références de variables
|
|
26
|
+
const refs = this.extractVariableReferences(condition);
|
|
27
|
+
for (const ref of refs) {
|
|
28
|
+
if (ref === currentVariableCode) {
|
|
29
|
+
errors.push({
|
|
30
|
+
type: 'self_reference',
|
|
31
|
+
variableCode: ref,
|
|
32
|
+
message: 'Une variable ne peut pas se référencer elle-même'
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else if (!availableCodes.has(ref)) {
|
|
36
|
+
errors.push({
|
|
37
|
+
type: 'variable_not_in_scope',
|
|
38
|
+
variableCode: ref,
|
|
39
|
+
message: `La variable \${${ref}} n'existe pas dans ce Roster`
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// 2. Vérifier TOUS les jumps (supporte jump() || jump() || ...)
|
|
44
|
+
const allJumps = this.extractAllJumps(condition);
|
|
45
|
+
for (const jump of allJumps) {
|
|
46
|
+
if (!availableCodes.has(jump.target)) {
|
|
47
|
+
errors.push({
|
|
48
|
+
type: 'jump_out_of_scope',
|
|
49
|
+
variableCode: jump.target,
|
|
50
|
+
message: `La variable cible \${${jump.target}} n'existe pas dans ce Roster`
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const targetVar = availableVariables.find(v => v.code === jump.target);
|
|
55
|
+
if (targetVar && targetVar.ordre <= currentVariableOrdre) {
|
|
56
|
+
errors.push({
|
|
57
|
+
type: 'backward_jump',
|
|
58
|
+
variableCode: jump.target,
|
|
59
|
+
message: `Le jump vers \${${jump.target}} ne peut pas cibler une variable précédente`
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// 3. Valider la syntaxe générale
|
|
65
|
+
const syntaxError = this.validateSyntax(condition);
|
|
66
|
+
if (syntaxError) {
|
|
67
|
+
errors.push({
|
|
68
|
+
type: 'syntax_error',
|
|
69
|
+
variableCode: '',
|
|
70
|
+
message: syntaxError
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
isValid: errors.length === 0,
|
|
75
|
+
errors
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Évalue une condition au runtime (rendu)
|
|
80
|
+
* Utilise les valeurs de la ligne courante du roster
|
|
81
|
+
*/
|
|
82
|
+
static evaluate(condition, lineValues) {
|
|
83
|
+
if (!condition?.trim()) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
// Construire un contexte isolé pour cette ligne du roster
|
|
88
|
+
const responses = {};
|
|
89
|
+
for (const [code, value] of Object.entries(lineValues)) {
|
|
90
|
+
responses[code] = {
|
|
91
|
+
variableCode: code,
|
|
92
|
+
valeur: value,
|
|
93
|
+
dateModification: new Date()
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// Utiliser le ConditionEngine en composition (pas héritage)
|
|
97
|
+
const engine = new ConditionEngine(responses);
|
|
98
|
+
return engine.evaluate(condition);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
console.warn('[RosterConditionEngine] Erreur évaluation:', condition, error);
|
|
102
|
+
return true; // En cas d'erreur, afficher par défaut
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Extrait toutes les références de variables d'une condition
|
|
107
|
+
* Format: ${VAR_CODE}
|
|
108
|
+
*/
|
|
109
|
+
static extractVariableReferences(condition) {
|
|
110
|
+
if (!condition)
|
|
111
|
+
return [];
|
|
112
|
+
const matches = condition.match(/\$\{([A-Z_][A-Z0-9_]*)\}/g) || [];
|
|
113
|
+
return matches.map(m => m.slice(2, -1));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Extrait la cible d'un jump s'il existe dans la condition (premier match uniquement)
|
|
117
|
+
* Format: jump(condition, ${TARGET})
|
|
118
|
+
* @deprecated Utiliser extractAllJumps pour supporter les jumps multiples
|
|
119
|
+
*/
|
|
120
|
+
static extractJumpTarget(condition) {
|
|
121
|
+
if (!condition)
|
|
122
|
+
return null;
|
|
123
|
+
const match = condition.match(/jump\s*\([^,]+,\s*\$\{([A-Z_][A-Z0-9_]*)\}\s*\)/);
|
|
124
|
+
return match ? match[1] : null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Extrait TOUS les jumps d'une condition
|
|
128
|
+
* Supporte: jump(cond1, ${VAR1}) || jump(cond2, ${VAR2})
|
|
129
|
+
* Retourne un tableau de { fullExpression, innerCondition, target }
|
|
130
|
+
*/
|
|
131
|
+
static extractAllJumps(condition) {
|
|
132
|
+
if (!condition)
|
|
133
|
+
return [];
|
|
134
|
+
const jumps = [];
|
|
135
|
+
// Regex pour capturer chaque jump avec sa condition interne et sa cible
|
|
136
|
+
const jumpRegex = /jump\s*\(\s*([^,]+?)\s*,\s*\$\{([A-Z_][A-Z0-9_]*)\}\s*\)/g;
|
|
137
|
+
let match;
|
|
138
|
+
while ((match = jumpRegex.exec(condition)) !== null) {
|
|
139
|
+
jumps.push({
|
|
140
|
+
fullExpression: match[0],
|
|
141
|
+
innerCondition: match[1].trim(),
|
|
142
|
+
target: match[2]
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return jumps;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Calcule les variables qui doivent être masquées à cause des jumps actifs
|
|
149
|
+
* Supporte les jumps multiples avec court-circuit:
|
|
150
|
+
* - jump(cond1, ${VAR1}) || jump(cond2, ${VAR2})
|
|
151
|
+
* - Si cond1 est vraie, on saute à VAR1 et cond2 n'est pas évaluée
|
|
152
|
+
*/
|
|
153
|
+
static computeJumpedVariables(sortedVariables, lineValues) {
|
|
154
|
+
const jumped = new Set();
|
|
155
|
+
for (const rosterVar of sortedVariables) {
|
|
156
|
+
const condition = rosterVar.conditionsAffichage;
|
|
157
|
+
if (!condition)
|
|
158
|
+
continue;
|
|
159
|
+
// Extraire tous les jumps de cette condition
|
|
160
|
+
const allJumps = this.extractAllJumps(condition);
|
|
161
|
+
if (allJumps.length === 0)
|
|
162
|
+
continue;
|
|
163
|
+
// Évaluer les jumps dans l'ordre avec court-circuit
|
|
164
|
+
for (const jump of allJumps) {
|
|
165
|
+
// Évaluer la condition interne du jump
|
|
166
|
+
const innerConditionResult = this.evaluateInnerCondition(jump.innerCondition, lineValues);
|
|
167
|
+
if (innerConditionResult) {
|
|
168
|
+
// Ce jump s'active - masquer les variables entre source et cible
|
|
169
|
+
const targetVar = sortedVariables.find(v => v.code === jump.target);
|
|
170
|
+
if (targetVar) {
|
|
171
|
+
for (const v of sortedVariables) {
|
|
172
|
+
if (v.ordre > rosterVar.ordre && v.ordre < targetVar.ordre) {
|
|
173
|
+
jumped.add(v.code);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Court-circuit: on ne vérifie pas les jumps suivants
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return jumped;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Évalue une condition interne (sans le wrapper jump())
|
|
186
|
+
*/
|
|
187
|
+
static evaluateInnerCondition(innerCondition, lineValues) {
|
|
188
|
+
if (!innerCondition?.trim())
|
|
189
|
+
return false;
|
|
190
|
+
try {
|
|
191
|
+
// Construire un contexte isolé pour cette ligne du roster
|
|
192
|
+
const responses = {};
|
|
193
|
+
for (const [code, value] of Object.entries(lineValues)) {
|
|
194
|
+
responses[code] = {
|
|
195
|
+
variableCode: code,
|
|
196
|
+
valeur: value,
|
|
197
|
+
dateModification: new Date()
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
const engine = new ConditionEngine(responses);
|
|
201
|
+
// Si c'est déjà un showMe(), l'évaluer directement
|
|
202
|
+
if (innerCondition.trim().startsWith('showMe(')) {
|
|
203
|
+
return engine.evaluate(innerCondition);
|
|
204
|
+
}
|
|
205
|
+
// Sinon, wrapper dans showMe pour évaluation
|
|
206
|
+
return engine.evaluate(`showMe(${innerCondition})`);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
console.warn('[RosterConditionEngine] Erreur évaluation condition interne:', innerCondition, error);
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Valide la syntaxe de base d'une condition
|
|
215
|
+
*/
|
|
216
|
+
static validateSyntax(condition) {
|
|
217
|
+
try {
|
|
218
|
+
// Vérifier les parenthèses équilibrées
|
|
219
|
+
let depth = 0;
|
|
220
|
+
for (const char of condition) {
|
|
221
|
+
if (char === '(')
|
|
222
|
+
depth++;
|
|
223
|
+
if (char === ')')
|
|
224
|
+
depth--;
|
|
225
|
+
if (depth < 0) {
|
|
226
|
+
return 'Parenthèses non équilibrées';
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (depth !== 0) {
|
|
230
|
+
return 'Parenthèses non équilibrées';
|
|
231
|
+
}
|
|
232
|
+
// Vérifier les fonctions reconnues
|
|
233
|
+
const functionMatch = condition.match(/^(showMe|hideMe|jump)\s*\(/);
|
|
234
|
+
if (!functionMatch && !condition.includes('${')) {
|
|
235
|
+
return 'Condition invalide: utilisez showMe(), hideMe() ou jump()';
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
return `Erreur de syntaxe: ${error instanceof Error ? error.message : 'Erreur inconnue'}`;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
package/package.json
CHANGED