@rsuci/shared-form-components 1.0.64 → 1.0.66

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 (35) hide show
  1. package/dist/components/VariableRenderer.d.ts +1 -0
  2. package/dist/components/VariableRenderer.d.ts.map +1 -1
  3. package/dist/components/VariableRenderer.js +10 -3
  4. package/dist/components/form-renderer/FormRenderer.d.ts.map +1 -1
  5. package/dist/components/form-renderer/FormRenderer.js +57 -3
  6. package/dist/components/index.d.ts +6 -0
  7. package/dist/components/index.d.ts.map +1 -1
  8. package/dist/components/index.js +7 -0
  9. package/dist/components/inputs/CCInput.d.ts +23 -0
  10. package/dist/components/inputs/CCInput.d.ts.map +1 -0
  11. package/dist/components/inputs/CCInput.js +90 -0
  12. package/dist/components/inputs/CMUInput.d.ts +22 -0
  13. package/dist/components/inputs/CMUInput.d.ts.map +1 -0
  14. package/dist/components/inputs/CMUInput.js +45 -0
  15. package/dist/components/inputs/CNIInput.d.ts +24 -0
  16. package/dist/components/inputs/CNIInput.d.ts.map +1 -0
  17. package/dist/components/inputs/CNIInput.js +158 -0
  18. package/dist/components/inputs/ExtraitInput.d.ts +24 -0
  19. package/dist/components/inputs/ExtraitInput.d.ts.map +1 -0
  20. package/dist/components/inputs/ExtraitInput.js +115 -0
  21. package/dist/components/inputs/NNIInput.d.ts +22 -0
  22. package/dist/components/inputs/NNIInput.d.ts.map +1 -0
  23. package/dist/components/inputs/NNIInput.js +53 -0
  24. package/dist/components/inputs/PasseportInput.d.ts +23 -0
  25. package/dist/components/inputs/PasseportInput.d.ts.map +1 -0
  26. package/dist/components/inputs/PasseportInput.js +85 -0
  27. package/dist/hooks/useConditionValidation.d.ts +60 -0
  28. package/dist/hooks/useConditionValidation.d.ts.map +1 -0
  29. package/dist/hooks/useConditionValidation.js +209 -0
  30. package/dist/lib/condition-engine.d.ts +25 -1
  31. package/dist/lib/condition-engine.d.ts.map +1 -1
  32. package/dist/lib/condition-engine.js +138 -0
  33. package/dist/types/enquete.d.ts +14 -1
  34. package/dist/types/enquete.d.ts.map +1 -1
  35. package/package.json +1 -1
@@ -32,6 +32,7 @@ export interface VariableRendererProps {
32
32
  services?: any;
33
33
  geographicComponents?: any;
34
34
  isConsultationMode?: boolean;
35
+ conditionValidationError?: string | null;
35
36
  }
36
37
  declare const GeographicFallback: React.FC<VariableRendererProps>;
37
38
  declare const getVariableRenderer: (typeCode: string, RosterCheckInput?: React.ComponentType<VariableRendererProps>, RosterListInput?: React.ComponentType<VariableRendererProps>) => React.ComponentType<VariableRendererProps>;
@@ -1 +1 @@
1
- {"version":3,"file":"VariableRenderer.d.ts","sourceRoot":"","sources":["../../src/components/VariableRenderer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmB,MAAM,OAAO,CAAC;AA+BxC,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC9D,qBAAqB,CAAC,EAAE,CAAC,WAAW,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACjE,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC3C,eAAe,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1L,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAG/B,gBAAgB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC;IAC9D,eAAe,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC;IAC7D,wBAAwB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAGvH,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,oBAAoB,CAAC,EAAE,GAAG,CAAC;IAG3B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAGD,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAQvD,CAAC;AAGF,QAAA,MAAM,mBAAmB,GACvB,UAAU,MAAM,EAChB,mBAAmB,KAAK,CAAC,aAAa,CAAC,qBAAqB,CAAC,EAC7D,kBAAkB,KAAK,CAAC,aAAa,CAAC,qBAAqB,CAAC,KAC3D,KAAK,CAAC,aAAa,CAAC,qBAAqB,CAsD3C,CAAC;AAGF,QAAA,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAiIrD,CAAC;AAEF,eAAe,gBAAgB,CAAC;AAChC,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"VariableRenderer.d.ts","sourceRoot":"","sources":["../../src/components/VariableRenderer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAqCxC,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC9D,qBAAqB,CAAC,EAAE,CAAC,WAAW,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACjE,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC3C,eAAe,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1L,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAG/B,gBAAgB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC;IAC9D,eAAe,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC;IAC7D,wBAAwB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAGvH,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,oBAAoB,CAAC,EAAE,GAAG,CAAC;IAG3B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAG7B,wBAAwB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1C;AAGD,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAQvD,CAAC;AAGF,QAAA,MAAM,mBAAmB,GACvB,UAAU,MAAM,EAChB,mBAAmB,KAAK,CAAC,aAAa,CAAC,qBAAqB,CAAC,EAC7D,kBAAkB,KAAK,CAAC,aAAa,CAAC,qBAAqB,CAAC,KAC3D,KAAK,CAAC,aAAa,CAAC,qBAAqB,CA8D3C,CAAC;AAGF,QAAA,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAyIrD,CAAC;AAEF,eAAe,gBAAgB,CAAC;AAChC,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,CAAC"}
@@ -3,7 +3,7 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Suspense } from 'react';
4
4
  import { AlertCircle, HelpCircle } from 'lucide-react';
5
5
  // Import des composants depuis l'index
6
- import { IdDocInput, LabelDisplay, PanelDisplay, GeographicCascadeInput, StringInput, NumberInput, DatePicker, SelectInput, RadioInput, CheckboxInput, ComboboxInput, PhoneInput, EmailInput, GPSInput, HourInput, RSUInput, MenageInput, EnqueteInput, PhotoCapture, ImageUpload, EnqueteurInput, EmailEnqueteurInput, TelephoneEnqueteurInput } from './index';
6
+ import { IdDocInput, LabelDisplay, PanelDisplay, GeographicCascadeInput, StringInput, NumberInput, DatePicker, SelectInput, RadioInput, CheckboxInput, ComboboxInput, PhoneInput, EmailInput, GPSInput, HourInput, RSUInput, MenageInput, EnqueteInput, PhotoCapture, ImageUpload, EnqueteurInput, EmailEnqueteurInput, TelephoneEnqueteurInput, CNIInput, NNIInput, CMUInput, PasseportInput, CCInput, ExtraitInput } from './index';
7
7
  // Composant générique pour les types géographiques non encore implémentés
8
8
  const GeographicFallback = ({ variable }) => {
9
9
  return (_jsx("div", { className: "p-3 bg-yellow-50 border border-yellow-200 rounded-lg", children: _jsxs("p", { className: "text-yellow-800 text-sm", children: ["Composant ", variable.typeCode, " en cours d'impl\u00E9mentation"] }) }));
@@ -47,6 +47,13 @@ const getVariableRenderer = (typeCode, RosterCheckInput, RosterListInput) => {
47
47
  'ENQUETE': EnqueteInput,
48
48
  // Composant pour documents d'identité
49
49
  'IDDOC': IdDocInput,
50
+ // Documents d'identité ivoiriens
51
+ 'CNI': CNIInput,
52
+ 'NNI': NNIInput,
53
+ 'CMU': CMUInput,
54
+ 'PASSEPORT': PasseportInput,
55
+ 'CC': CCInput,
56
+ 'EXTRAIT': ExtraitInput,
50
57
  };
51
58
  // Ajouter les composants Roster injectés s'ils sont fournis
52
59
  if (RosterCheckInput) {
@@ -58,7 +65,7 @@ const getVariableRenderer = (typeCode, RosterCheckInput, RosterListInput) => {
58
65
  return renderers[typeCode] || StringInput;
59
66
  };
60
67
  // Composant principal VariableRenderer
61
- const VariableRenderer = ({ variable, value, onChange, onBlur, error, disabled, numeroMembre, valeurMin, onFillFormFromMenage, onFillFormFromEnquete, formulaireVariables, rosterVariables, allResponses = {}, RosterCheckInput, RosterListInput, interpolateVariableLabel, reponses = {}, services, isConsultationMode = false }) => {
68
+ const VariableRenderer = ({ variable, value, onChange, onBlur, error, disabled, numeroMembre, valeurMin, onFillFormFromMenage, onFillFormFromEnquete, formulaireVariables, rosterVariables, allResponses = {}, RosterCheckInput, RosterListInput, interpolateVariableLabel, reponses = {}, services, isConsultationMode = false, conditionValidationError }) => {
62
69
  console.log('🎨 [VariableRenderer] Rendu de variable', {
63
70
  code: variable.code,
64
71
  designation: variable.designation,
@@ -107,7 +114,7 @@ const VariableRenderer = ({ variable, value, onChange, onBlur, error, disabled,
107
114
  length: Array.isArray(rosterVariables) ? rosterVariables.length : 'N/A'
108
115
  });
109
116
  return null;
110
- })(), _jsx(RendererComponent, { variable: variable, value: value, onChange: onChange, onBlur: onBlur, error: error, disabled: disabled, numeroMembre: numeroMembre, valeurMin: valeurMin, onFillFormFromMenage: onFillFormFromMenage, onFillFormFromEnquete: onFillFormFromEnquete, formulaireVariables: formulaireVariables, rosterVariables: rosterVariables, allResponses: allResponses, reponses: reponses, services: services, isConsultationMode: isConsultationMode })] }), variable.proprietes?.helpText && (_jsx("p", { className: "text-xs text-gray-500", children: variable.proprietes.helpText })), error && (_jsxs("div", { className: "flex items-center space-x-1 text-red-600 text-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4" }), _jsx("span", { children: error })] }))] }));
117
+ })(), _jsx(RendererComponent, { variable: variable, value: value, onChange: onChange, onBlur: onBlur, error: error, disabled: disabled, numeroMembre: numeroMembre, valeurMin: valeurMin, onFillFormFromMenage: onFillFormFromMenage, onFillFormFromEnquete: onFillFormFromEnquete, formulaireVariables: formulaireVariables, rosterVariables: rosterVariables, allResponses: allResponses, reponses: reponses, services: services, isConsultationMode: isConsultationMode })] }), variable.proprietes?.helpText && (_jsx("p", { className: "text-xs text-gray-500", children: variable.proprietes.helpText })), error && (_jsxs("div", { className: "flex items-center space-x-1 text-red-600 text-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4" }), _jsx("span", { children: error })] })), conditionValidationError && (_jsxs("div", { className: "flex items-center space-x-1 text-red-600 text-sm mt-1", children: [_jsx(AlertCircle, { className: "h-4 w-4 flex-shrink-0" }), _jsx("span", { children: conditionValidationError })] }))] }));
111
118
  };
112
119
  export default VariableRenderer;
113
120
  export { getVariableRenderer, GeographicFallback };
@@ -1 +1 @@
1
- {"version":3,"file":"FormRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/form-renderer/FormRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAA4D,MAAM,OAAO,CAAC;AACjF,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EAIf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAqBlF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,yBAAyB;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAClD,oCAAoC;IACpC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,iCAAiC;IACjC,SAAS,EAAE,qBAAqB,CAAC;IACjC,wBAAwB;IACxB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,wCAAwC;IACxC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iCAAiC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AA8nBD;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgBpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"FormRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/form-renderer/FormRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAA4D,MAAM,OAAO,CAAC;AACjF,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EAIf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAsBlF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,yBAAyB;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAClD,oCAAoC;IACpC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,iCAAiC;IACjC,SAAS,EAAE,qBAAqB,CAAC;IACjC,wBAAwB;IACxB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,wCAAwC;IACxC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iCAAiC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAwsBD;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgBpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -19,6 +19,7 @@ import RosterCheckInput from '../roster/RosterCheckInput';
19
19
  import RosterListInput from '../roster/RosterListInput';
20
20
  // Hooks et utilitaires
21
21
  import { useFormTree } from '../../hooks/useFormTree';
22
+ import { useConditionValidation } from '../../hooks/useConditionValidation';
22
23
  import { interpolateVariableLabel } from '../../lib/utils/interpolateVariableLabel';
23
24
  /**
24
25
  * Composant interne qui utilise le contexte
@@ -36,6 +37,9 @@ const FormRendererInner = () => {
36
37
  const [isSubmittingForm, setIsSubmittingForm] = useState(false);
37
38
  // FormTree pour la visibilité des variables et les autoactions
38
39
  const { formTree, getVisibleVariables, getVisibleVariablesForInstance, validateGroupForInstance, evaluateAutoActionsForInstance } = useFormTree(groupesWithInstances.length > 0 ? groupesWithInstances : formulaire?.groupes || [], responses, { debug: false });
40
+ // Hook pour la validation des conditions (ConditionEval)
41
+ const conditionEngine = formTree?.getConditionEngine() || null;
42
+ const { validationErrors: conditionValidationErrors, validateVariable: validateConditionVariable, validateGroup: validateConditionGroup, validateAll: validateAllConditions, hasErrors: hasConditionErrors, getErrorForVariable: getConditionErrorForVariable, revalidateDependents } = useConditionValidation(conditionEngine, responses, { debug: false });
39
43
  // Refs pour éviter les boucles infinies lors de l'application des autoactions
40
44
  const isApplyingAutoActions = useRef(false);
41
45
  const lastProcessedResponsesKey = useRef('');
@@ -118,7 +122,14 @@ const FormRendererInner = () => {
118
122
  ? currentInstance?.numeroInstance
119
123
  : undefined;
120
124
  updateResponse(variable.code, value, variable, numeroMembre);
121
- }, [currentGroup, currentInstance, updateResponse]);
125
+ // Valider la condition de validation de cette variable
126
+ if (variable.conditionsValidation) {
127
+ validateConditionVariable(variable, numeroMembre);
128
+ }
129
+ // Réévaluer les validations des variables dépendantes
130
+ const groupes = groupesWithInstances.length > 0 ? groupesWithInstances : formulaire?.groupes || [];
131
+ revalidateDependents(variable.code, groupes, numeroMembre);
132
+ }, [currentGroup, currentInstance, updateResponse, validateConditionVariable, revalidateDependents, groupesWithInstances, formulaire?.groupes]);
122
133
  // Handler pour l'annulation
123
134
  const handleCancelClick = () => {
124
135
  if (hasUnsavedChanges) {
@@ -212,7 +223,29 @@ const FormRendererInner = () => {
212
223
  const canGoNext = isConsultationMode || validateCurrentGroupWithVisibility();
213
224
  const isFormValid = validation.validateAllGroups(groupes, responses).isValid;
214
225
  const handleNext = () => {
215
- if (canGoNext && !navigation.isLastGroup) {
226
+ // En mode consultation, navigation libre
227
+ if (isConsultationMode) {
228
+ if (!navigation.isLastGroup) {
229
+ navigation.goToNextGroup();
230
+ }
231
+ return;
232
+ }
233
+ // Valider les champs obligatoires
234
+ if (!canGoNext) {
235
+ console.log('❌ [Navigation] Champs obligatoires non remplis');
236
+ return;
237
+ }
238
+ // Valider les conditions de validation (ConditionEval) du groupe actuel
239
+ if (currentGroup) {
240
+ const numeroMembre = currentGroup.estMultiple ? currentInstance?.numeroInstance : undefined;
241
+ const conditionErrors = validateConditionGroup(currentGroup, numeroMembre);
242
+ if (conditionErrors.length > 0) {
243
+ console.log('❌ [Navigation] Erreurs de validation ConditionEval:', conditionErrors);
244
+ // Les erreurs sont déjà affichées sous les variables via getConditionErrorForVariable
245
+ return;
246
+ }
247
+ }
248
+ if (!navigation.isLastGroup) {
216
249
  navigation.goToNextGroup();
217
250
  }
218
251
  };
@@ -225,6 +258,7 @@ const FormRendererInner = () => {
225
258
  const handleSubmit = async () => {
226
259
  if (effectiveDisabled || isSubmitting || isSubmittingForm)
227
260
  return;
261
+ // Valider les champs obligatoires
228
262
  const result = validation.validateAllGroups(groupes, responses);
229
263
  if (!result.isValid) {
230
264
  if (callbacks.onValidationError) {
@@ -232,6 +266,26 @@ const FormRendererInner = () => {
232
266
  }
233
267
  return;
234
268
  }
269
+ // Valider les conditions de validation (ConditionEval) de tous les groupes
270
+ const instancesMap = new Map();
271
+ for (const groupe of groupes) {
272
+ if (groupe.estMultiple && groupe.instances) {
273
+ instancesMap.set(groupe.code, groupe.instances.map(i => i.numeroInstance));
274
+ }
275
+ }
276
+ const conditionErrors = validateAllConditions(groupes, instancesMap);
277
+ if (conditionErrors.length > 0) {
278
+ console.log('❌ [Soumission] Erreurs de validation ConditionEval:', conditionErrors);
279
+ if (callbacks.onValidationError) {
280
+ // Convertir les erreurs de condition en erreurs de validation standard
281
+ callbacks.onValidationError(conditionErrors.map(e => ({
282
+ variableCode: e.variableCode,
283
+ message: e.message,
284
+ type: 'custom'
285
+ })));
286
+ }
287
+ return;
288
+ }
235
289
  setIsSubmittingForm(true);
236
290
  try {
237
291
  await callbacks.onSubmit(responses);
@@ -300,7 +354,7 @@ const FormRendererInner = () => {
300
354
  const rosterVariables = (variable.typeCode === 'ROSTERCHECK' || variable.typeCode === 'ROSTERLIST')
301
355
  ? variable.rosterVariables
302
356
  : undefined;
303
- return (_jsx(VariableRenderer, { variable: variable, value: currentValue, onChange: (value) => handleVariableChange(variable, value), disabled: effectiveDisabled, numeroMembre: currentGroup?.estMultiple ? currentInstance?.numeroInstance : undefined, services: services, geographicComponents: geographicComponents, formulaireVariables: formulaire.variables, allResponses: responses, reponses: responses, interpolateVariableLabel: interpolateVariableLabel, rosterVariables: rosterVariables, RosterCheckInput: RosterCheckInput, RosterListInput: RosterListInput }, variable.id));
357
+ return (_jsx(VariableRenderer, { variable: variable, value: currentValue, onChange: (value) => handleVariableChange(variable, value), disabled: effectiveDisabled, numeroMembre: currentGroup?.estMultiple ? currentInstance?.numeroInstance : undefined, conditionValidationError: getConditionErrorForVariable(variable.code, currentGroup?.estMultiple ? currentInstance?.numeroInstance : undefined), services: services, geographicComponents: geographicComponents, formulaireVariables: formulaire.variables, allResponses: responses, reponses: responses, interpolateVariableLabel: interpolateVariableLabel, rosterVariables: rosterVariables, RosterCheckInput: RosterCheckInput, RosterListInput: RosterListInput }, variable.id));
304
358
  }) }), currentGroup?.estMultiple && currentGroup.instances && (_jsx("div", { className: "mt-6 pt-6 border-t", children: _jsx(GroupeInstanceTabs, { groupe: currentGroup, currentInstanceIndex: navigation.navigationState.instanceIndex, responses: responses, onInstanceChange: handleInstanceChange, onInstanceAdded: handleInstanceAdded, onInstanceRemoved: handleInstanceRemoved, disabled: effectiveDisabled }) }))] })] }), _jsx("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-4", children: _jsxs("div", { className: "flex flex-wrap justify-between items-center gap-2", children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs("button", { type: "button", onClick: handlePrevious, disabled: navigation.isFirstGroup || (!isConsultationMode && effectiveDisabled), className: `flex items-center px-4 py-2 rounded-lg font-medium transition-colors ${!navigation.isFirstGroup && (isConsultationMode || !effectiveDisabled)
305
359
  ? 'bg-orange-500 text-white hover:bg-orange-600'
306
360
  : 'bg-gray-100 text-gray-400 cursor-not-allowed'}`, children: [_jsx("svg", { className: "h-4 w-4 mr-2", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }), _jsx("span", { className: "hidden sm:inline", children: labels.previousButton || 'Précédent' }), _jsx("span", { className: "sm:hidden", children: "Pr\u00E9c." })] }), mode === 'admin' && features?.saveDraft && callbacks.onSaveDraft && (_jsx("button", { type: "button", onClick: handleSaveDraft, disabled: effectiveDisabled || isSavingDraft || isSubmitting || isSubmittingForm, className: `flex items-center px-4 py-2 rounded-lg font-medium transition-colors ${!effectiveDisabled && !isSavingDraft && !isSubmitting && !isSubmittingForm
@@ -33,4 +33,10 @@ export { default as MenageInput } from './selectors/MenageInput';
33
33
  export { default as EnqueteInput } from './selectors/EnqueteInput';
34
34
  export { default as PhotoCapture } from './wrappers/PhotoCapture';
35
35
  export { default as ImageUpload } from './wrappers/ImageUpload';
36
+ export { default as CNIInput } from './inputs/CNIInput';
37
+ export { default as NNIInput } from './inputs/NNIInput';
38
+ export { default as CMUInput } from './inputs/CMUInput';
39
+ export { default as PasseportInput } from './inputs/PasseportInput';
40
+ export { default as CCInput } from './inputs/CCInput';
41
+ export { default as ExtraitInput } from './inputs/ExtraitInput';
36
42
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAI3D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAGjE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGpE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAGtF,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAG9D,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGnE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"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"}
@@ -41,3 +41,10 @@ export { default as EnqueteInput } from './selectors/EnqueteInput';
41
41
  // Wrapper Components
42
42
  export { default as PhotoCapture } from './wrappers/PhotoCapture';
43
43
  export { default as ImageUpload } from './wrappers/ImageUpload';
44
+ // Documents d'identité ivoiriens
45
+ export { default as CNIInput } from './inputs/CNIInput';
46
+ export { default as NNIInput } from './inputs/NNIInput';
47
+ export { default as CMUInput } from './inputs/CMUInput';
48
+ export { default as PasseportInput } from './inputs/PasseportInput';
49
+ export { default as CCInput } from './inputs/CCInput';
50
+ export { default as ExtraitInput } from './inputs/ExtraitInput';
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Composant CCInput - Saisie de Carte Consulaire
3
+ * RSU v2 - Moteur de Rendu des Formulaires d'Enquête
4
+ *
5
+ * Format : 1-2 lettres majuscules + 1-20 chiffres
6
+ * Exemple : AB12345678901234567890
7
+ */
8
+ import React from 'react';
9
+ import { VariableFormulaire, VariableValue } from '../../types/enquete';
10
+ export interface CCInputProps {
11
+ variable: VariableFormulaire & {
12
+ typeCode: 'CC';
13
+ };
14
+ value: VariableValue;
15
+ onChange: (value: VariableValue) => void;
16
+ onBlur?: () => void;
17
+ error?: string;
18
+ disabled?: boolean;
19
+ isConsultationMode?: boolean;
20
+ }
21
+ declare const CCInput: React.FC<CCInputProps>;
22
+ export default CCInput;
23
+ //# sourceMappingURL=CCInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CCInput.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/CCInput.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAgC,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIxE,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,kBAAkB,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC;IAClD,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AA2BD,QAAA,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAyFnC,CAAC;AAEF,eAAe,OAAO,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Composant CCInput - Saisie de Carte Consulaire
3
+ * RSU v2 - Moteur de Rendu des Formulaires d'Enquête
4
+ *
5
+ * Format : 1-2 lettres majuscules + 1-20 chiffres
6
+ * Exemple : AB12345678901234567890
7
+ */
8
+ 'use client';
9
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
+ import { useState, useCallback } from 'react';
11
+ import { applyComponentStyle } from '../../utils/styleUtils';
12
+ import { isComponentReadonly, readonlyClasses } from '../../utils/componentStateUtils';
13
+ const CC_REGEX = /^[A-Z]{1,2}\d{1,20}$/;
14
+ const applyCCMask = (value) => {
15
+ const upper = value.toUpperCase();
16
+ const result = [];
17
+ let inDigitSection = false;
18
+ let letterCount = 0;
19
+ let digitCount = 0;
20
+ for (let i = 0; i < upper.length; i++) {
21
+ const char = upper[i];
22
+ if (!inDigitSection && /[A-Z]/.test(char) && letterCount < 2) {
23
+ result.push(char);
24
+ letterCount++;
25
+ }
26
+ else if (/\d/.test(char) && digitCount < 20) {
27
+ inDigitSection = true;
28
+ result.push(char);
29
+ digitCount++;
30
+ }
31
+ }
32
+ return result.join('');
33
+ };
34
+ const CCInput = ({ variable, value, onChange, onBlur, error, disabled, isConsultationMode = false }) => {
35
+ const stringValue = value || '';
36
+ const [localError, setLocalError] = useState(null);
37
+ const { textStyle, containerStyle } = applyComponentStyle(variable.componentStyle);
38
+ const isReadonly = isComponentReadonly(variable, isConsultationMode);
39
+ const handleChange = useCallback((e) => {
40
+ const rawValue = e.target.value;
41
+ const maskedValue = applyCCMask(rawValue);
42
+ onChange(maskedValue);
43
+ setLocalError(null);
44
+ }, [onChange]);
45
+ const handleBlur = useCallback(() => {
46
+ if (stringValue && stringValue.length > 0 && !CC_REGEX.test(stringValue)) {
47
+ const letterMatch = stringValue.match(/^[A-Z]*/);
48
+ const letterCount = letterMatch ? letterMatch[0].length : 0;
49
+ const digitMatch = stringValue.match(/\d+$/);
50
+ const digitCount = digitMatch ? digitMatch[0].length : 0;
51
+ if (letterCount === 0) {
52
+ setLocalError('La carte consulaire doit commencer par 1 ou 2 lettres');
53
+ }
54
+ else if (digitCount === 0) {
55
+ setLocalError('La carte consulaire doit contenir au moins 1 chiffre après les lettres');
56
+ }
57
+ else {
58
+ setLocalError('Format invalide');
59
+ }
60
+ }
61
+ else {
62
+ setLocalError(null);
63
+ }
64
+ onBlur?.();
65
+ }, [stringValue, onBlur]);
66
+ const getInputClasses = () => {
67
+ const baseClasses = 'w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent text-gray-900 font-mono uppercase';
68
+ const errorClasses = displayError ? 'border-red-500' : 'border-gray-300';
69
+ if (disabled)
70
+ return `${baseClasses} ${errorClasses} bg-gray-100 cursor-not-allowed text-gray-500`;
71
+ if (isReadonly)
72
+ return `${baseClasses} ${errorClasses} ${readonlyClasses.readonly}`;
73
+ return `${baseClasses} ${errorClasses} bg-white`;
74
+ };
75
+ const displayError = error || localError;
76
+ const getHelperText = () => {
77
+ if (!stringValue)
78
+ return null;
79
+ const letterMatch = stringValue.match(/^[A-Z]*/);
80
+ const letterCount = letterMatch ? letterMatch[0].length : 0;
81
+ if (letterCount === 0)
82
+ return 'Commencez par 1 ou 2 lettres';
83
+ if (letterCount <= 2 && !/\d/.test(stringValue))
84
+ return 'Continuez avec des chiffres';
85
+ return null;
86
+ };
87
+ const helperText = getHelperText();
88
+ return (_jsxs("div", { style: containerStyle, children: [_jsx("input", { type: "text", value: stringValue, onChange: handleChange, onBlur: handleBlur, placeholder: disabled ? '' : 'AB12345678901234567890', disabled: disabled, readOnly: isReadonly, style: textStyle, className: getInputClasses(), maxLength: 22, autoComplete: "off", spellCheck: false }), helperText && !displayError && (_jsx("p", { className: "mt-1 text-xs text-gray-500", children: helperText })), displayError && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: displayError }))] }));
89
+ };
90
+ export default CCInput;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Composant CMUInput - Saisie de Carte Maladie Universelle
3
+ * RSU v2 - Moteur de Rendu des Formulaires d'Enquête
4
+ *
5
+ * Format : exactement 13 chiffres
6
+ */
7
+ import React from 'react';
8
+ import { VariableFormulaire, VariableValue } from '../../types/enquete';
9
+ export interface CMUInputProps {
10
+ variable: VariableFormulaire & {
11
+ typeCode: 'CMU';
12
+ };
13
+ value: VariableValue;
14
+ onChange: (value: VariableValue) => void;
15
+ onBlur?: () => void;
16
+ error?: string;
17
+ disabled?: boolean;
18
+ isConsultationMode?: boolean;
19
+ }
20
+ declare const CMUInput: React.FC<CMUInputProps>;
21
+ export default CMUInput;
22
+ //# sourceMappingURL=CMUInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CMUInput.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/CMUInput.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAgC,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIxE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,kBAAkB,GAAG;QAAE,QAAQ,EAAE,KAAK,CAAA;KAAE,CAAC;IACnD,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAID,QAAA,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAmErC,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Composant CMUInput - Saisie de Carte Maladie Universelle
3
+ * RSU v2 - Moteur de Rendu des Formulaires d'Enquête
4
+ *
5
+ * Format : exactement 13 chiffres
6
+ */
7
+ 'use client';
8
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
+ import { useState, useCallback } from 'react';
10
+ import { applyComponentStyle } from '../../utils/styleUtils';
11
+ import { isComponentReadonly, readonlyClasses } from '../../utils/componentStateUtils';
12
+ const CMU_REGEX = /^\d{13}$/;
13
+ const CMUInput = ({ variable, value, onChange, onBlur, error, disabled, isConsultationMode = false }) => {
14
+ const stringValue = value || '';
15
+ const [localError, setLocalError] = useState(null);
16
+ const { textStyle, containerStyle } = applyComponentStyle(variable.componentStyle);
17
+ const isReadonly = isComponentReadonly(variable, isConsultationMode);
18
+ const handleChange = useCallback((e) => {
19
+ const rawValue = e.target.value;
20
+ const digitsOnly = rawValue.replace(/\D/g, '').slice(0, 13);
21
+ onChange(digitsOnly);
22
+ setLocalError(null);
23
+ }, [onChange]);
24
+ const handleBlur = useCallback(() => {
25
+ if (stringValue && stringValue.length > 0 && !CMU_REGEX.test(stringValue)) {
26
+ setLocalError(`Le numéro CMU doit contenir exactement 13 chiffres (${stringValue.length}/13)`);
27
+ }
28
+ else {
29
+ setLocalError(null);
30
+ }
31
+ onBlur?.();
32
+ }, [stringValue, onBlur]);
33
+ const getInputClasses = () => {
34
+ const baseClasses = 'w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent text-gray-900 font-mono';
35
+ const errorClasses = displayError ? 'border-red-500' : 'border-gray-300';
36
+ if (disabled)
37
+ return `${baseClasses} ${errorClasses} bg-gray-100 cursor-not-allowed text-gray-500`;
38
+ if (isReadonly)
39
+ return `${baseClasses} ${errorClasses} ${readonlyClasses.readonly}`;
40
+ return `${baseClasses} ${errorClasses} bg-white`;
41
+ };
42
+ const displayError = error || localError;
43
+ return (_jsxs("div", { style: containerStyle, children: [_jsx("input", { type: "text", inputMode: "numeric", value: stringValue, onChange: handleChange, onBlur: handleBlur, placeholder: disabled ? '' : '1234567890123', disabled: disabled, readOnly: isReadonly, style: textStyle, className: getInputClasses(), maxLength: 13, autoComplete: "off", spellCheck: false }), stringValue && !displayError && (_jsxs("p", { className: "mt-1 text-xs text-gray-500", children: [stringValue.length, "/13 chiffres"] })), displayError && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: displayError }))] }));
44
+ };
45
+ export default CMUInput;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Composant CNIInput - Saisie de Carte Nationale d'Identité ivoirienne
3
+ * RSU v2 - Moteur de Rendu des Formulaires d'Enquête
4
+ *
5
+ * Formats acceptés :
6
+ * - Nouveau format : CI00xxxxxxx (CI00 + 7 chiffres) = 11 caractères
7
+ * - Ancien format : C 00XX XXXXXX (C + espace + 00 + 2 alphanum + espace + 6 alphanum) = 14 caractères
8
+ */
9
+ import React from 'react';
10
+ import { VariableFormulaire, VariableValue } from '../../types/enquete';
11
+ export interface CNIInputProps {
12
+ variable: VariableFormulaire & {
13
+ typeCode: 'CNI';
14
+ };
15
+ value: VariableValue;
16
+ onChange: (value: VariableValue) => void;
17
+ onBlur?: () => void;
18
+ error?: string;
19
+ disabled?: boolean;
20
+ isConsultationMode?: boolean;
21
+ }
22
+ declare const CNIInput: React.FC<CNIInputProps>;
23
+ export default CNIInput;
24
+ //# sourceMappingURL=CNIInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CNIInput.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/CNIInput.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAgC,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIxE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,kBAAkB,GAAG;QAAE,QAAQ,EAAE,KAAK,CAAA;KAAE,CAAC;IACnD,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAwDD,QAAA,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAgHrC,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Composant CNIInput - Saisie de Carte Nationale d'Identité ivoirienne
3
+ * RSU v2 - Moteur de Rendu des Formulaires d'Enquête
4
+ *
5
+ * Formats acceptés :
6
+ * - Nouveau format : CI00xxxxxxx (CI00 + 7 chiffres) = 11 caractères
7
+ * - Ancien format : C 00XX XXXXXX (C + espace + 00 + 2 alphanum + espace + 6 alphanum) = 14 caractères
8
+ */
9
+ 'use client';
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
+ import { useState, useCallback } from 'react';
12
+ import { applyComponentStyle } from '../../utils/styleUtils';
13
+ import { isComponentReadonly, readonlyClasses } from '../../utils/componentStateUtils';
14
+ const CNI_NEW_REGEX = /^CI00\d{7}$/;
15
+ const CNI_OLD_REGEX = /^C 00[A-Z0-9]{2} [A-Z0-9]{6}$/;
16
+ const applyNewFormatMask = (value) => {
17
+ let result = value.toUpperCase();
18
+ result = result.replace(/[^CI0-9]/g, '');
19
+ if (result.length > 0 && result[0] !== 'C') {
20
+ return '';
21
+ }
22
+ if (result.length > 1 && result[1] !== 'I') {
23
+ return result[0];
24
+ }
25
+ if (result.length > 2 && result[2] !== '0') {
26
+ return result.slice(0, 2);
27
+ }
28
+ if (result.length > 3 && result[3] !== '0') {
29
+ return result.slice(0, 3);
30
+ }
31
+ return result.slice(0, 11);
32
+ };
33
+ const applyOldFormatMask = (value) => {
34
+ let result = value.toUpperCase();
35
+ const chars = [];
36
+ let rawIndex = 0;
37
+ for (let i = 0; i < result.length && chars.length < 14; i++) {
38
+ const char = result[i];
39
+ const pos = chars.length;
40
+ if (pos === 0) {
41
+ if (char === 'C')
42
+ chars.push(char);
43
+ }
44
+ else if (pos === 1) {
45
+ chars.push(' ');
46
+ if (/[0-9]/.test(char))
47
+ chars.push(char);
48
+ else
49
+ i--;
50
+ }
51
+ else if (pos === 2 || pos === 3) {
52
+ if (char === '0')
53
+ chars.push(char);
54
+ }
55
+ else if (pos === 4 || pos === 5) {
56
+ if (/[A-Z0-9]/.test(char))
57
+ chars.push(char);
58
+ }
59
+ else if (pos === 6) {
60
+ chars.push(' ');
61
+ if (/[A-Z0-9]/.test(char))
62
+ chars.push(char);
63
+ else
64
+ i--;
65
+ }
66
+ else if (pos >= 7 && pos <= 13) {
67
+ if (/[A-Z0-9]/.test(char))
68
+ chars.push(char);
69
+ }
70
+ }
71
+ return chars.join('');
72
+ };
73
+ const CNIInput = ({ variable, value, onChange, onBlur, error, disabled, isConsultationMode = false }) => {
74
+ const stringValue = value || '';
75
+ const [localError, setLocalError] = useState(null);
76
+ const { textStyle, containerStyle } = applyComponentStyle(variable.componentStyle);
77
+ const isReadonly = isComponentReadonly(variable, isConsultationMode);
78
+ const detectFormat = (val) => {
79
+ if (val.startsWith('CI'))
80
+ return 'new';
81
+ if (val.startsWith('C ') || (val.length === 1 && val === 'C'))
82
+ return 'old';
83
+ return 'unknown';
84
+ };
85
+ const handleChange = useCallback((e) => {
86
+ const rawValue = e.target.value;
87
+ if (rawValue === '') {
88
+ onChange('');
89
+ setLocalError(null);
90
+ return;
91
+ }
92
+ const upperValue = rawValue.toUpperCase();
93
+ let maskedValue = '';
94
+ if (upperValue.startsWith('CI')) {
95
+ maskedValue = applyNewFormatMask(upperValue);
96
+ }
97
+ else if (upperValue.startsWith('C ') || upperValue === 'C') {
98
+ maskedValue = applyOldFormatMask(upperValue);
99
+ }
100
+ else if (upperValue[0] === 'C') {
101
+ maskedValue = 'C';
102
+ }
103
+ else {
104
+ return;
105
+ }
106
+ onChange(maskedValue);
107
+ if (maskedValue.length > 0) {
108
+ const format = detectFormat(maskedValue);
109
+ if (format === 'new' && maskedValue.length === 11 && !CNI_NEW_REGEX.test(maskedValue)) {
110
+ setLocalError('Format CNI invalide');
111
+ }
112
+ else if (format === 'old' && maskedValue.length === 14 && !CNI_OLD_REGEX.test(maskedValue)) {
113
+ setLocalError('Format CNI invalide');
114
+ }
115
+ else {
116
+ setLocalError(null);
117
+ }
118
+ }
119
+ else {
120
+ setLocalError(null);
121
+ }
122
+ }, [onChange]);
123
+ const handleBlur = useCallback(() => {
124
+ if (stringValue && stringValue.length > 0) {
125
+ const isValidNew = CNI_NEW_REGEX.test(stringValue);
126
+ const isValidOld = CNI_OLD_REGEX.test(stringValue);
127
+ if (!isValidNew && !isValidOld) {
128
+ const format = detectFormat(stringValue);
129
+ if (format === 'new') {
130
+ setLocalError('Format attendu : CI00 suivi de 7 chiffres (ex: CI001234567)');
131
+ }
132
+ else if (format === 'old') {
133
+ setLocalError('Format attendu : C 00XX XXXXXX (ex: C 00AB 123456)');
134
+ }
135
+ else {
136
+ setLocalError('Format CNI invalide');
137
+ }
138
+ }
139
+ else {
140
+ setLocalError(null);
141
+ }
142
+ }
143
+ onBlur?.();
144
+ }, [stringValue, onBlur]);
145
+ const getInputClasses = () => {
146
+ const baseClasses = 'w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent text-gray-900 font-mono';
147
+ const errorClasses = displayError ? 'border-red-500' : 'border-gray-300';
148
+ if (disabled)
149
+ return `${baseClasses} ${errorClasses} bg-gray-100 cursor-not-allowed text-gray-500`;
150
+ if (isReadonly)
151
+ return `${baseClasses} ${errorClasses} ${readonlyClasses.readonly}`;
152
+ return `${baseClasses} ${errorClasses} bg-white`;
153
+ };
154
+ const displayError = error || localError;
155
+ const placeholder = 'CI001234567 ou C 00AB 123456';
156
+ return (_jsxs("div", { style: containerStyle, children: [_jsx("input", { type: "text", value: stringValue, onChange: handleChange, onBlur: handleBlur, placeholder: disabled ? '' : placeholder, disabled: disabled, readOnly: isReadonly, style: textStyle, className: getInputClasses(), maxLength: 14, autoComplete: "off", spellCheck: false }), displayError && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: displayError }))] }));
157
+ };
158
+ export default CNIInput;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Composant ExtraitInput - Saisie d'Extrait de Naissance
3
+ * RSU v2 - Moteur de Rendu des Formulaires d'Enquête
4
+ *
5
+ * Format composé : numéro (3 chiffres) + date d'établissement + année registre (4 chiffres)
6
+ * Exemple affiché : "556 du 12/12/2020 du registre 2020"
7
+ * Stockage : JSON { numero, dateEtablissement, anneeRegistre }
8
+ */
9
+ import React from 'react';
10
+ import { VariableFormulaire, VariableValue } from '../../types/enquete';
11
+ export interface ExtraitInputProps {
12
+ variable: VariableFormulaire & {
13
+ typeCode: 'EXTRAIT';
14
+ };
15
+ value: VariableValue;
16
+ onChange: (value: VariableValue) => void;
17
+ onBlur?: () => void;
18
+ error?: string;
19
+ disabled?: boolean;
20
+ isConsultationMode?: boolean;
21
+ }
22
+ declare const ExtraitInput: React.FC<ExtraitInputProps>;
23
+ export default ExtraitInput;
24
+ //# sourceMappingURL=ExtraitInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExtraitInput.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/ExtraitInput.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAA2C,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIxE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,kBAAkB,GAAG;QAAE,QAAQ,EAAE,SAAS,CAAA;KAAE,CAAC;IACvD,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAgED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAkJ7C,CAAC;AAEF,eAAe,YAAY,CAAC"}