@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.
- package/dist/components/VariableRenderer.d.ts +1 -0
- package/dist/components/VariableRenderer.d.ts.map +1 -1
- package/dist/components/VariableRenderer.js +10 -3
- package/dist/components/form-renderer/FormRenderer.d.ts.map +1 -1
- package/dist/components/form-renderer/FormRenderer.js +57 -3
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +7 -0
- package/dist/components/inputs/CCInput.d.ts +23 -0
- package/dist/components/inputs/CCInput.d.ts.map +1 -0
- package/dist/components/inputs/CCInput.js +90 -0
- package/dist/components/inputs/CMUInput.d.ts +22 -0
- package/dist/components/inputs/CMUInput.d.ts.map +1 -0
- package/dist/components/inputs/CMUInput.js +45 -0
- package/dist/components/inputs/CNIInput.d.ts +24 -0
- package/dist/components/inputs/CNIInput.d.ts.map +1 -0
- package/dist/components/inputs/CNIInput.js +158 -0
- package/dist/components/inputs/ExtraitInput.d.ts +24 -0
- package/dist/components/inputs/ExtraitInput.d.ts.map +1 -0
- package/dist/components/inputs/ExtraitInput.js +115 -0
- package/dist/components/inputs/NNIInput.d.ts +22 -0
- package/dist/components/inputs/NNIInput.d.ts.map +1 -0
- package/dist/components/inputs/NNIInput.js +53 -0
- package/dist/components/inputs/PasseportInput.d.ts +23 -0
- package/dist/components/inputs/PasseportInput.d.ts.map +1 -0
- package/dist/components/inputs/PasseportInput.js +85 -0
- package/dist/hooks/useConditionValidation.d.ts +60 -0
- package/dist/hooks/useConditionValidation.d.ts.map +1 -0
- package/dist/hooks/useConditionValidation.js +209 -0
- package/dist/lib/condition-engine.d.ts +25 -1
- package/dist/lib/condition-engine.d.ts.map +1 -1
- package/dist/lib/condition-engine.js +138 -0
- package/dist/types/enquete.d.ts +14 -1
- package/dist/types/enquete.d.ts.map +1 -1
- 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;
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
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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"}
|
package/dist/components/index.js
CHANGED
|
@@ -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"}
|