@rsuci/shared-form-components 1.0.83 → 1.0.85
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/inputs/DatePicker.d.ts +3 -0
- package/dist/components/inputs/DatePicker.d.ts.map +1 -1
- package/dist/components/inputs/DatePicker.js +151 -30
- package/dist/components/inputs/GeographicCascadeInput.d.ts.map +1 -1
- package/dist/components/inputs/GeographicCascadeInput.js +15 -11
- package/dist/utils/variableDependencyResolver.d.ts +1 -1
- package/dist/utils/variableDependencyResolver.d.ts.map +1 -1
- package/dist/utils/variableDependencyResolver.js +16 -7
- package/package.json +1 -1
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Composant DatePicker - Sélecteur de date et date-heure
|
|
3
3
|
* RSU v2 - Moteur de Rendu des Formulaires d'Enquête
|
|
4
|
+
*
|
|
5
|
+
* Supporte la saisie manuelle (dd/MM/yyyy ou dd/MM/yyyy HH:mm)
|
|
6
|
+
* ET la sélection via le calendrier natif du navigateur.
|
|
4
7
|
*/
|
|
5
8
|
import React from 'react';
|
|
6
9
|
import { VariableFormulaire, VariableValue } from '../../types/enquete';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/DatePicker.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/DatePicker.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAExE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKxE,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,kBAAkB,GAAG;QAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAAA;KAAE,CAAC;IAClE,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;AAuDD,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA+JzC,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
|
@@ -1,57 +1,178 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Composant DatePicker - Sélecteur de date et date-heure
|
|
3
3
|
* RSU v2 - Moteur de Rendu des Formulaires d'Enquête
|
|
4
|
+
*
|
|
5
|
+
* Supporte la saisie manuelle (dd/MM/yyyy ou dd/MM/yyyy HH:mm)
|
|
6
|
+
* ET la sélection via le calendrier natif du navigateur.
|
|
4
7
|
*/
|
|
5
8
|
'use client';
|
|
6
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
11
|
+
import { Calendar } from 'lucide-react';
|
|
7
12
|
import { VariableValueConverter } from '../../lib/utils/variableValueConverter';
|
|
8
13
|
import { applyComponentStyle } from '../../utils/styleUtils';
|
|
9
14
|
import { isComponentReadonly, readonlyClasses } from '../../utils/componentStateUtils';
|
|
15
|
+
/**
|
|
16
|
+
* Applique le masque de saisie date sur une chaîne de chiffres bruts.
|
|
17
|
+
* DATE: dd/MM/yyyy (max 8 chiffres → 10 chars avec séparateurs)
|
|
18
|
+
* DATEHEURE: dd/MM/yyyy HH:mm (max 12 chiffres → 16 chars avec séparateurs)
|
|
19
|
+
*/
|
|
20
|
+
function applyDateMask(raw, isDateTime) {
|
|
21
|
+
// Ne garder que les chiffres
|
|
22
|
+
const digits = raw.replace(/\D/g, '');
|
|
23
|
+
const maxDigits = isDateTime ? 12 : 8;
|
|
24
|
+
const limited = digits.slice(0, maxDigits);
|
|
25
|
+
let result = '';
|
|
26
|
+
for (let i = 0; i < limited.length; i++) {
|
|
27
|
+
// Séparateurs pour la partie date: après position 2 et 4 (dd/MM/yyyy)
|
|
28
|
+
if (i === 2 || i === 4)
|
|
29
|
+
result += '/';
|
|
30
|
+
// Séparateurs pour la partie heure: espace après position 8, : après position 10
|
|
31
|
+
if (i === 8)
|
|
32
|
+
result += ' ';
|
|
33
|
+
if (i === 10)
|
|
34
|
+
result += ':';
|
|
35
|
+
result += limited[i];
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Convertit une Date JS en chaîne d'affichage dd/MM/yyyy ou dd/MM/yyyy HH:mm
|
|
41
|
+
*/
|
|
42
|
+
function dateToDisplay(date, isDateTime) {
|
|
43
|
+
const d = date.getDate().toString().padStart(2, '0');
|
|
44
|
+
const m = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
45
|
+
const y = date.getFullYear();
|
|
46
|
+
if (isDateTime) {
|
|
47
|
+
const hh = date.getHours().toString().padStart(2, '0');
|
|
48
|
+
const mm = date.getMinutes().toString().padStart(2, '0');
|
|
49
|
+
return `${d}/${m}/${y} ${hh}:${mm}`;
|
|
50
|
+
}
|
|
51
|
+
return `${d}/${m}/${y}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Convertit une Date JS en format ISO pour l'input natif caché
|
|
55
|
+
*/
|
|
56
|
+
function dateToIso(date, isDateTime) {
|
|
57
|
+
const y = date.getFullYear();
|
|
58
|
+
const m = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
59
|
+
const d = date.getDate().toString().padStart(2, '0');
|
|
60
|
+
if (isDateTime) {
|
|
61
|
+
const hh = date.getHours().toString().padStart(2, '0');
|
|
62
|
+
const mm = date.getMinutes().toString().padStart(2, '0');
|
|
63
|
+
return `${y}-${m}-${d}T${hh}:${mm}`;
|
|
64
|
+
}
|
|
65
|
+
return `${y}-${m}-${d}`;
|
|
66
|
+
}
|
|
10
67
|
const DatePicker = ({ variable, value, onChange, onBlur, error, disabled, isConsultationMode = false }) => {
|
|
11
68
|
const props = variable.proprietes;
|
|
12
69
|
const { textStyle, containerStyle } = applyComponentStyle(variable.componentStyle);
|
|
13
|
-
|
|
70
|
+
const isDateTime = variable.typeCode === 'DATEHEURE';
|
|
14
71
|
const isReadonly = isComponentReadonly(variable, isConsultationMode);
|
|
15
|
-
|
|
72
|
+
const hiddenInputRef = useRef(null);
|
|
73
|
+
const isTypingRef = useRef(false);
|
|
74
|
+
// État local pour le texte affiché dans l'input
|
|
75
|
+
const [displayValue, setDisplayValue] = useState('');
|
|
76
|
+
const [hasFormatError, setHasFormatError] = useState(false);
|
|
77
|
+
// Synchroniser la prop value → displayValue (quand la valeur change depuis l'extérieur)
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (isTypingRef.current)
|
|
80
|
+
return; // Ne pas écraser pendant la saisie
|
|
81
|
+
if (!value || value === '') {
|
|
82
|
+
setDisplayValue('');
|
|
83
|
+
setHasFormatError(false);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const parsed = VariableValueConverter.parse(value, variable.typeCode);
|
|
87
|
+
if (parsed && !isNaN(parsed.getTime())) {
|
|
88
|
+
setDisplayValue(dateToDisplay(parsed, isDateTime));
|
|
89
|
+
setHasFormatError(false);
|
|
90
|
+
}
|
|
91
|
+
}, [value, variable.typeCode, isDateTime]);
|
|
92
|
+
// Valeur ISO pour l'input natif caché
|
|
93
|
+
const isoValue = (() => {
|
|
94
|
+
const parsed = VariableValueConverter.parse(value, variable.typeCode);
|
|
95
|
+
if (parsed && !isNaN(parsed.getTime())) {
|
|
96
|
+
return dateToIso(parsed, isDateTime);
|
|
97
|
+
}
|
|
98
|
+
return '';
|
|
99
|
+
})();
|
|
100
|
+
const inputType = isDateTime ? 'datetime-local' : 'date';
|
|
101
|
+
const placeholder = isDateTime ? 'jj/MM/aaaa HH:mm' : 'jj/MM/aaaa';
|
|
102
|
+
const maxLength = isDateTime ? 16 : 10;
|
|
103
|
+
// Classes CSS
|
|
16
104
|
const getInputClasses = () => {
|
|
17
|
-
const baseClasses = 'w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent text-gray-900';
|
|
18
|
-
const
|
|
105
|
+
const baseClasses = 'w-full px-3 py-2 pr-10 border rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent text-gray-900';
|
|
106
|
+
const borderClass = (error || hasFormatError) ? 'border-red-500' : 'border-gray-300';
|
|
19
107
|
if (disabled)
|
|
20
|
-
return `${baseClasses} ${
|
|
108
|
+
return `${baseClasses} ${borderClass} bg-gray-100 cursor-not-allowed text-gray-500`;
|
|
21
109
|
if (isReadonly)
|
|
22
|
-
return `${baseClasses} ${
|
|
23
|
-
return `${baseClasses} ${
|
|
110
|
+
return `${baseClasses} ${borderClass} ${readonlyClasses.readonly}`;
|
|
111
|
+
return `${baseClasses} ${borderClass} bg-white`;
|
|
24
112
|
};
|
|
25
|
-
//
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
113
|
+
// Saisie manuelle avec masque
|
|
114
|
+
const handleTextChange = useCallback((e) => {
|
|
115
|
+
isTypingRef.current = true;
|
|
116
|
+
const masked = applyDateMask(e.target.value, isDateTime);
|
|
117
|
+
setDisplayValue(masked);
|
|
118
|
+
setHasFormatError(false);
|
|
119
|
+
}, [isDateTime]);
|
|
120
|
+
// Validation au blur
|
|
121
|
+
const handleBlur = useCallback(() => {
|
|
122
|
+
isTypingRef.current = false;
|
|
123
|
+
if (!displayValue || displayValue.trim() === '') {
|
|
124
|
+
setHasFormatError(false);
|
|
125
|
+
onChange('');
|
|
126
|
+
onBlur?.();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
// Vérifier que la longueur est complète
|
|
130
|
+
const expectedLength = isDateTime ? 16 : 10;
|
|
131
|
+
if (displayValue.length < expectedLength) {
|
|
132
|
+
setHasFormatError(true);
|
|
133
|
+
onBlur?.();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
// Tenter de parser
|
|
137
|
+
const parsed = VariableValueConverter.parse(displayValue, variable.typeCode);
|
|
138
|
+
if (parsed && !isNaN(parsed.getTime())) {
|
|
139
|
+
const serialized = VariableValueConverter.serialize(parsed, variable.typeCode);
|
|
140
|
+
setHasFormatError(false);
|
|
141
|
+
onChange(serialized);
|
|
37
142
|
}
|
|
38
143
|
else {
|
|
39
|
-
|
|
144
|
+
setHasFormatError(true);
|
|
40
145
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
146
|
+
onBlur?.();
|
|
147
|
+
}, [displayValue, isDateTime, variable.typeCode, onChange, onBlur]);
|
|
148
|
+
// Sélection via le calendrier natif
|
|
149
|
+
const handlePickerChange = useCallback((e) => {
|
|
44
150
|
if (!e.target.value) {
|
|
45
|
-
|
|
151
|
+
setDisplayValue('');
|
|
152
|
+
onChange('');
|
|
46
153
|
return;
|
|
47
154
|
}
|
|
48
155
|
const newDate = new Date(e.target.value);
|
|
49
156
|
if (!isNaN(newDate.getTime())) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
157
|
+
setDisplayValue(dateToDisplay(newDate, isDateTime));
|
|
158
|
+
setHasFormatError(false);
|
|
159
|
+
const serialized = VariableValueConverter.serialize(newDate, variable.typeCode);
|
|
160
|
+
onChange(serialized);
|
|
53
161
|
}
|
|
54
|
-
};
|
|
55
|
-
|
|
162
|
+
}, [isDateTime, variable.typeCode, onChange]);
|
|
163
|
+
// Ouvrir le picker natif
|
|
164
|
+
const handleCalendarClick = useCallback(() => {
|
|
165
|
+
if (hiddenInputRef.current) {
|
|
166
|
+
try {
|
|
167
|
+
hiddenInputRef.current.showPicker();
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Fallback: focus si showPicker() n'est pas supporté
|
|
171
|
+
hiddenInputRef.current.focus();
|
|
172
|
+
hiddenInputRef.current.click();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}, []);
|
|
176
|
+
return (_jsxs("div", { style: containerStyle, className: "relative", children: [_jsx("input", { type: "text", value: displayValue, onChange: handleTextChange, onBlur: handleBlur, placeholder: placeholder, maxLength: maxLength, disabled: disabled, readOnly: isReadonly, style: textStyle, className: getInputClasses() }), !disabled && !isReadonly && (_jsx("button", { type: "button", onClick: handleCalendarClick, className: "absolute right-2 top-1/2 -translate-y-1/2 p-1 text-gray-400 hover:text-gray-600 focus:outline-none", tabIndex: -1, "aria-label": "Ouvrir le calendrier", children: _jsx(Calendar, { className: "h-4 w-4" }) })), _jsx("input", { ref: hiddenInputRef, type: inputType, value: isoValue, onChange: handlePickerChange, min: props?.minDate ? dateToIso(props.minDate, isDateTime) : undefined, max: props?.maxDate ? dateToIso(props.maxDate, isDateTime) : undefined, className: "sr-only", tabIndex: -1, "aria-hidden": "true" })] }));
|
|
56
177
|
};
|
|
57
178
|
export default DatePicker;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GeographicCascadeInput.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/GeographicCascadeInput.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AAWjF;;GAEG;AACH,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAWD,UAAU,2BAA2B;IACnC,QAAQ,EAAE,GAAG,CAAC;IACd,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,
|
|
1
|
+
{"version":3,"file":"GeographicCascadeInput.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/GeographicCascadeInput.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AAWjF;;GAEG;AACH,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAWD,UAAU,2BAA2B;IACnC,QAAQ,EAAE,GAAG,CAAC;IACd,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAwPxE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
|
|
@@ -15,6 +15,10 @@ export const GeographicCascadeInput = ({ variable, value, onChange, reponses, di
|
|
|
15
15
|
const [error, setError] = useState(null);
|
|
16
16
|
// 1. Déterminer si ce type nécessite un parent
|
|
17
17
|
const needsParent = requiresParent(variable.typeCode);
|
|
18
|
+
// 1b. Vérifier si un parent est CONFIGURÉ dans la variable (valeur non vide)
|
|
19
|
+
const hasParentConfigured = !!extractVariableCode(variable);
|
|
20
|
+
// Le champ a besoin d'un parent SEULEMENT si le type le requiert ET qu'un parent est configuré
|
|
21
|
+
const effectiveNeedsParent = needsParent && hasParentConfigured;
|
|
18
22
|
// 2. Résoudre la valeur du parent depuis les réponses
|
|
19
23
|
const parentValue = resolveParentValue(variable, reponses);
|
|
20
24
|
// Debug temporaire - À SUPPRIMER après validation
|
|
@@ -34,11 +38,11 @@ export const GeographicCascadeInput = ({ variable, value, onChange, reponses, di
|
|
|
34
38
|
? (variable.valeur || 'CIV')
|
|
35
39
|
: undefined;
|
|
36
40
|
// 4. Déterminer l'endpoint API à appeler
|
|
37
|
-
const apiEndpoint = getApiEndpoint(variable.typeCode, parentValue, countryCode);
|
|
41
|
+
const apiEndpoint = getApiEndpoint(variable.typeCode, parentValue, countryCode, hasParentConfigured);
|
|
38
42
|
// 4. Fonction pour charger les données
|
|
39
43
|
const fetchData = useCallback(async () => {
|
|
40
|
-
// Si un parent est requis mais absent, ne rien charger
|
|
41
|
-
if (
|
|
44
|
+
// Si un parent est requis (configuré) mais absent, ne rien charger
|
|
45
|
+
if (effectiveNeedsParent && !parentValue) {
|
|
42
46
|
setItems([]);
|
|
43
47
|
setLoading(false);
|
|
44
48
|
setError(null);
|
|
@@ -90,7 +94,7 @@ export const GeographicCascadeInput = ({ variable, value, onChange, reponses, di
|
|
|
90
94
|
finally {
|
|
91
95
|
setLoading(false);
|
|
92
96
|
}
|
|
93
|
-
}, [apiEndpoint,
|
|
97
|
+
}, [apiEndpoint, effectiveNeedsParent, parentValue, variable.type, variable.libelle]);
|
|
94
98
|
// 5. Charger les données quand l'endpoint ou le parent change
|
|
95
99
|
useEffect(() => {
|
|
96
100
|
fetchData();
|
|
@@ -98,8 +102,8 @@ export const GeographicCascadeInput = ({ variable, value, onChange, reponses, di
|
|
|
98
102
|
// 6. Réinitialiser la valeur si le parent change
|
|
99
103
|
const previousParentRef = useRef(undefined);
|
|
100
104
|
useEffect(() => {
|
|
101
|
-
// Si un parent est requis et que la valeur du parent a changé
|
|
102
|
-
if (
|
|
105
|
+
// Si un parent est requis (configuré) et que la valeur du parent a changé
|
|
106
|
+
if (effectiveNeedsParent) {
|
|
103
107
|
// Premier rendu : initialiser la référence sans réinitialiser
|
|
104
108
|
if (previousParentRef.current === undefined) {
|
|
105
109
|
previousParentRef.current = parentValue;
|
|
@@ -111,7 +115,7 @@ export const GeographicCascadeInput = ({ variable, value, onChange, reponses, di
|
|
|
111
115
|
}
|
|
112
116
|
previousParentRef.current = parentValue;
|
|
113
117
|
}
|
|
114
|
-
}, [parentValue,
|
|
118
|
+
}, [parentValue, effectiveNeedsParent, value, onChange]);
|
|
115
119
|
// 7. Gérer le changement de valeur
|
|
116
120
|
const handleChange = (option) => {
|
|
117
121
|
if (!option) {
|
|
@@ -128,12 +132,12 @@ export const GeographicCascadeInput = ({ variable, value, onChange, reponses, di
|
|
|
128
132
|
};
|
|
129
133
|
// 8. Déterminer si le champ est désactivé (select ne supporte pas readonly, on utilise disabled)
|
|
130
134
|
const isReadonly = isComponentReadonly(variable, isConsultationMode);
|
|
131
|
-
const isDisabled = disabled || isReadonly || loading || (
|
|
135
|
+
const isDisabled = disabled || isReadonly || loading || (effectiveNeedsParent && !parentValue);
|
|
132
136
|
// 9. Déterminer le message du placeholder
|
|
133
137
|
const getPlaceholder = () => {
|
|
134
138
|
if (loading)
|
|
135
139
|
return 'Chargement...';
|
|
136
|
-
if (
|
|
140
|
+
if (effectiveNeedsParent && !parentValue) {
|
|
137
141
|
return `Sélectionnez d'abord ${getParentLabel(variable.typeCode)}`;
|
|
138
142
|
}
|
|
139
143
|
if (items.length === 0 && !loading) {
|
|
@@ -176,11 +180,11 @@ export const GeographicCascadeInput = ({ variable, value, onChange, reponses, di
|
|
|
176
180
|
designation: parsedValue.designation
|
|
177
181
|
} : null;
|
|
178
182
|
// 13. Rendu du composant
|
|
179
|
-
return (_jsxs("div", { className: "w-full", children: [_jsx(SearchableSelect, { options: options, value: currentValue, onChange: handleChange, placeholder: getPlaceholder(), searchPlaceholder: "Rechercher...", disabled: isDisabled, required: required, loading: loading, error: error || undefined, className: className, noOptionsMessage:
|
|
183
|
+
return (_jsxs("div", { className: "w-full", children: [_jsx(SearchableSelect, { options: options, value: currentValue, onChange: handleChange, placeholder: getPlaceholder(), searchPlaceholder: "Rechercher...", disabled: isDisabled, required: required, loading: loading, error: error || undefined, className: className, noOptionsMessage: effectiveNeedsParent && !parentValue
|
|
180
184
|
? `Veuillez d'abord sélectionner ${getParentLabel(variable.typeCode)}`
|
|
181
185
|
: `Aucun(e) ${variable.designation?.toLowerCase() || 'élément'} disponible`, formatOptionLabel: (option) => {
|
|
182
186
|
// Afficher uniquement la désignation
|
|
183
187
|
return option.designation;
|
|
184
|
-
} }),
|
|
188
|
+
} }), effectiveNeedsParent && !parentValue && !loading && !error && (_jsxs("p", { className: "mt-1 text-sm text-gray-500 flex items-center", children: [_jsx("svg", { className: "w-4 h-4 mr-1", fill: "currentColor", viewBox: "0 0 20 20", children: _jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z", clipRule: "evenodd" }) }), "Veuillez d'abord s\u00E9lectionner ", getParentLabel(variable.typeCode)] })), loading && (_jsxs("p", { className: "mt-1 text-sm text-blue-600 flex items-center", children: [_jsxs("svg", { className: "animate-spin h-4 w-4 mr-1", fill: "none", viewBox: "0 0 24 24", children: [_jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), _jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }), "Chargement des donn\u00E9es..."] }))] }));
|
|
185
189
|
};
|
|
186
190
|
export default GeographicCascadeInput;
|
|
@@ -40,7 +40,7 @@ export declare function resolveParentValue(variable: any, reponses: Record<strin
|
|
|
40
40
|
* getApiEndpoint('REGION', 1) // Returns: '/api/v1/Regions/1/select'
|
|
41
41
|
* getApiEndpoint('REGION') // Returns: null (parent requis mais absent)
|
|
42
42
|
*/
|
|
43
|
-
export declare function getApiEndpoint(variableType: string, parentId?: number | null, defaultCountryCode?: string): string | null;
|
|
43
|
+
export declare function getApiEndpoint(variableType: string, parentId?: number | null, defaultCountryCode?: string, hasParentConfigured?: boolean): string | null;
|
|
44
44
|
/**
|
|
45
45
|
* Détermine le libellé du parent selon le type de variable
|
|
46
46
|
* @param variableType - Type de la variable
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variableDependencyResolver.d.ts","sourceRoot":"","sources":["../../src/utils/variableDependencyResolver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAmCpG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,GAAG,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,GAAG,IAAI,CA+Cf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EACxB,kBAAkB,GAAE,MAAc,
|
|
1
|
+
{"version":3,"file":"variableDependencyResolver.d.ts","sourceRoot":"","sources":["../../src/utils/variableDependencyResolver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAmCpG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,GAAG,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,GAAG,IAAI,CA+Cf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EACxB,kBAAkB,GAAE,MAAc,EAClC,mBAAmB,GAAE,OAAc,GAClC,MAAM,GAAG,IAAI,CA+Bf;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAa3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAW5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAY9D"}
|
|
@@ -114,23 +114,32 @@ export function resolveParentValue(variable, reponses) {
|
|
|
114
114
|
* getApiEndpoint('REGION', 1) // Returns: '/api/v1/Regions/1/select'
|
|
115
115
|
* getApiEndpoint('REGION') // Returns: null (parent requis mais absent)
|
|
116
116
|
*/
|
|
117
|
-
export function getApiEndpoint(variableType, parentId, defaultCountryCode = 'CIV') {
|
|
117
|
+
export function getApiEndpoint(variableType, parentId, defaultCountryCode = 'CIV', hasParentConfigured = true) {
|
|
118
118
|
const type = variableType?.toUpperCase();
|
|
119
119
|
switch (type) {
|
|
120
120
|
case 'DISTRICT':
|
|
121
|
-
// Pour DISTRICT, pas besoin de code pays dans l'URL
|
|
122
121
|
return `/api/v1/Districts/select`;
|
|
123
122
|
case 'REGION':
|
|
124
|
-
|
|
123
|
+
if (parentId)
|
|
124
|
+
return `/api/v1/Regions/${parentId}/select`;
|
|
125
|
+
return hasParentConfigured ? null : `/api/v1/Regions/select`;
|
|
125
126
|
case 'DEPARTEMENT':
|
|
126
|
-
|
|
127
|
+
if (parentId)
|
|
128
|
+
return `/api/v1/Departements/${parentId}/select`;
|
|
129
|
+
return hasParentConfigured ? null : `/api/v1/Departements/select`;
|
|
127
130
|
case 'SOUS_PREFECTURE':
|
|
128
131
|
case 'SOUSPREFECTURE':
|
|
129
|
-
|
|
132
|
+
if (parentId)
|
|
133
|
+
return `/api/v1/SousPrefectures/${parentId}/select`;
|
|
134
|
+
return hasParentConfigured ? null : `/api/v1/SousPrefectures/select`;
|
|
130
135
|
case 'QUARTIER':
|
|
131
|
-
|
|
136
|
+
if (parentId)
|
|
137
|
+
return `/api/v1/Quartiers/${parentId}/select`;
|
|
138
|
+
return hasParentConfigured ? null : `/api/v1/Quartiers/select`;
|
|
132
139
|
case 'VILLAGE':
|
|
133
|
-
|
|
140
|
+
if (parentId)
|
|
141
|
+
return `/api/v1/Villages/${parentId}/select`;
|
|
142
|
+
return hasParentConfigured ? null : `/api/v1/Villages/select`;
|
|
134
143
|
default:
|
|
135
144
|
return null;
|
|
136
145
|
}
|
package/package.json
CHANGED