@rsuci/shared-form-components 1.0.1 → 1.0.3

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.
@@ -21,6 +21,7 @@ export interface VariableRendererProps {
21
21
  valeur?: string;
22
22
  }>;
23
23
  allResponses?: Record<string, EnqueteReponse>;
24
+ reponses?: Record<string, any>;
24
25
  RosterCheckInput?: React.ComponentType<VariableRendererProps>;
25
26
  RosterListInput?: React.ComponentType<VariableRendererProps>;
26
27
  interpolateVariableLabel?: (label: string, responses: Record<string, EnqueteReponse>, numeroMembre?: number) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"VariableRenderer.d.ts","sourceRoot":"","sources":["../../src/components/VariableRenderer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAgCxC,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,CAAA;KAAE,CAAC,CAAC;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAG9C,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;CAC5B;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,CAgD3C,CAAC;AAGF,QAAA,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA+FrD,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;AA2BxC,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,CAAA;KAAE,CAAC,CAAC;IAChG,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;CAC5B;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,CAgD3C,CAAC;AAGF,QAAA,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAiGrD,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, DistrictInput, RegionInput, DepartementInput, SousPrefectureInput, QuartierInput, VillageInput, StringInput, NumberInput, DatePicker, SelectInput, RadioInput, CheckboxInput, ComboboxInput, PhoneInput, GPSInput, HourInput, RSUInput, MenageInput, EnqueteInput, PhotoCapture, ImageUpload } from './index';
6
+ import { IdDocInput, LabelDisplay, PanelDisplay, GeographicCascadeInput, StringInput, NumberInput, DatePicker, SelectInput, RadioInput, CheckboxInput, ComboboxInput, PhoneInput, GPSInput, HourInput, RSUInput, MenageInput, EnqueteInput, PhotoCapture, ImageUpload } 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"] }) }));
@@ -27,14 +27,14 @@ const getVariableRenderer = (typeCode, RosterCheckInput, RosterListInput) => {
27
27
  'PHONE': PhoneInput,
28
28
  'LABEL': LabelDisplay,
29
29
  'PANEL': PanelDisplay,
30
- // Composants géographiques spécialisés
30
+ // Composants géographiques - Utilisation du composant unifié GeographicCascadeInput
31
31
  'PAYS': GeographicFallback,
32
- 'DISTRICT': DistrictInput,
33
- 'REGION': RegionInput,
34
- 'DEPARTEMENT': DepartementInput,
35
- 'SOUSPREFECTURE': SousPrefectureInput,
36
- 'QUARTIER': QuartierInput,
37
- 'VILLAGE': VillageInput,
32
+ 'DISTRICT': GeographicCascadeInput,
33
+ 'REGION': GeographicCascadeInput,
34
+ 'DEPARTEMENT': GeographicCascadeInput,
35
+ 'SOUSPREFECTURE': GeographicCascadeInput,
36
+ 'QUARTIER': GeographicCascadeInput,
37
+ 'VILLAGE': GeographicCascadeInput,
38
38
  'RSU': RSUInput,
39
39
  // Composant de sélection de ménage
40
40
  'MENAGE': MenageInput,
@@ -53,7 +53,7 @@ const getVariableRenderer = (typeCode, RosterCheckInput, RosterListInput) => {
53
53
  return renderers[typeCode] || StringInput;
54
54
  };
55
55
  // Composant principal VariableRenderer
56
- const VariableRenderer = ({ variable, value, onChange, onBlur, error, disabled, numeroMembre, valeurMin, onFillFormFromMenage, onFillFormFromEnquete, formulaireVariables, rosterVariables, allResponses = {}, RosterCheckInput, RosterListInput, interpolateVariableLabel }) => {
56
+ const VariableRenderer = ({ variable, value, onChange, onBlur, error, disabled, numeroMembre, valeurMin, onFillFormFromMenage, onFillFormFromEnquete, formulaireVariables, rosterVariables, allResponses = {}, RosterCheckInput, RosterListInput, interpolateVariableLabel, reponses = {} }) => {
57
57
  const RendererComponent = getVariableRenderer(variable.typeCode, RosterCheckInput, RosterListInput);
58
58
  // Interpoler le libellé avec les valeurs des autres variables (si fonction fournie)
59
59
  const interpolatedDesignation = interpolateVariableLabel
@@ -77,7 +77,7 @@ const VariableRenderer = ({ variable, value, onChange, onBlur, error, disabled,
77
77
  length: Array.isArray(rosterVariables) ? rosterVariables.length : 'N/A'
78
78
  });
79
79
  return null;
80
- })(), _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 })] }), 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 })] }))] }));
80
+ })(), _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 })] }), 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 })] }))] }));
81
81
  };
82
82
  export default VariableRenderer;
83
83
  export { getVariableRenderer, GeographicFallback };
@@ -9,6 +9,8 @@ export { default as DepartementInput } from './geographic/DepartementInput';
9
9
  export { default as SousPrefectureInput } from './geographic/SousPrefectureInput';
10
10
  export { default as QuartierInput } from './geographic/QuartierInput';
11
11
  export { default as VillageInput } from './geographic/VillageInput';
12
+ export { default as SearchableSelect } from './inputs/SearchableSelect';
13
+ export { default as GeographicCascadeInput } from './inputs/GeographicCascadeInput';
12
14
  export { default as StringInput } from './inputs/StringInput';
13
15
  export { default as NumberInput } from './inputs/NumberInput';
14
16
  export { default as DatePicker } from './inputs/DatePicker';
@@ -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,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,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG1D,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":"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,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG1D,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"}
@@ -14,6 +14,8 @@ export { default as SousPrefectureInput } from './geographic/SousPrefectureInput
14
14
  export { default as QuartierInput } from './geographic/QuartierInput';
15
15
  export { default as VillageInput } from './geographic/VillageInput';
16
16
  // Input Components
17
+ export { default as SearchableSelect } from './inputs/SearchableSelect';
18
+ export { default as GeographicCascadeInput } from './inputs/GeographicCascadeInput';
17
19
  export { default as StringInput } from './inputs/StringInput';
18
20
  export { default as NumberInput } from './inputs/NumberInput';
19
21
  export { default as DatePicker } from './inputs/DatePicker';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Composant unifié pour la sélection géographique en cascade
3
+ * S'adapte automatiquement selon le type de variable (DISTRICT, REGION, DEPARTEMENT, etc.)
4
+ * Gère la cascade via le champ variable.valeur (ex: "${G_01}")
5
+ * Utilise SearchableSelect pour une meilleure UX avec recherche intégrée
6
+ */
7
+ import React from 'react';
8
+ /**
9
+ * Format de données retourné par les APIs backend
10
+ */
11
+ interface SelectDto {
12
+ id: number;
13
+ code: string;
14
+ designation: string;
15
+ }
16
+ interface GeographicCascadeInputProps {
17
+ variable: any;
18
+ value: any;
19
+ onChange: (value: SelectDto | null) => void;
20
+ reponses: Record<string, any>;
21
+ disabled?: boolean;
22
+ required?: boolean;
23
+ className?: string;
24
+ }
25
+ export declare const GeographicCascadeInput: React.FC<GeographicCascadeInputProps>;
26
+ export default GeographicCascadeInput;
27
+ //# sourceMappingURL=GeographicCascadeInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GeographicCascadeInput.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/GeographicCascadeInput.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAA2C,MAAM,OAAO,CAAC;AAUhE;;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;CACpB;AAED,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CA+KxE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -0,0 +1,123 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Composant unifié pour la sélection géographique en cascade
4
+ * S'adapte automatiquement selon le type de variable (DISTRICT, REGION, DEPARTEMENT, etc.)
5
+ * Gère la cascade via le champ variable.valeur (ex: "${G_01}")
6
+ * Utilise SearchableSelect pour une meilleure UX avec recherche intégrée
7
+ */
8
+ import { useEffect, useState, useCallback } from 'react';
9
+ import SearchableSelect from './SearchableSelect';
10
+ import { resolveParentValue, getApiEndpoint, getParentLabel, requiresParent } from '../../utils/variableDependencyResolver';
11
+ export const GeographicCascadeInput = ({ variable, value, onChange, reponses, disabled = false, required = false, className = '' }) => {
12
+ const [items, setItems] = useState([]);
13
+ const [loading, setLoading] = useState(false);
14
+ const [error, setError] = useState(null);
15
+ // 1. Déterminer si ce type nécessite un parent
16
+ const needsParent = requiresParent(variable.type);
17
+ // 2. Résoudre la valeur du parent depuis les réponses
18
+ const parentValue = resolveParentValue(variable, reponses);
19
+ // 3. Déterminer l'endpoint API à appeler
20
+ const apiEndpoint = getApiEndpoint(variable.type, parentValue);
21
+ // 4. Fonction pour charger les données
22
+ const fetchData = useCallback(async () => {
23
+ // Si un parent est requis mais absent, ne rien charger
24
+ if (needsParent && !parentValue) {
25
+ setItems([]);
26
+ setLoading(false);
27
+ setError(null);
28
+ return;
29
+ }
30
+ // Si pas d'endpoint valide, ne rien charger
31
+ if (!apiEndpoint) {
32
+ setItems([]);
33
+ setLoading(false);
34
+ setError(null);
35
+ return;
36
+ }
37
+ try {
38
+ setLoading(true);
39
+ setError(null);
40
+ const response = await fetch(apiEndpoint);
41
+ if (!response.ok) {
42
+ throw new Error(`HTTP error! status: ${response.status}`);
43
+ }
44
+ const result = await response.json();
45
+ // Vérifier le format de la réponse
46
+ if (result.success && Array.isArray(result.data)) {
47
+ setItems(result.data);
48
+ }
49
+ else {
50
+ throw new Error('Format de réponse invalide');
51
+ }
52
+ }
53
+ catch (err) {
54
+ console.error(`Error fetching ${variable.type}:`, err);
55
+ setError(`Erreur de chargement des ${variable.libelle?.toLowerCase() || 'données'}`);
56
+ setItems([]);
57
+ }
58
+ finally {
59
+ setLoading(false);
60
+ }
61
+ }, [apiEndpoint, needsParent, parentValue, variable.type, variable.libelle]);
62
+ // 5. Charger les données quand l'endpoint ou le parent change
63
+ useEffect(() => {
64
+ fetchData();
65
+ }, [fetchData]);
66
+ // 6. Réinitialiser la valeur si le parent change
67
+ useEffect(() => {
68
+ // Si un parent est requis et qu'il a changé, réinitialiser la valeur
69
+ if (needsParent && value && parentValue) {
70
+ // Vérifier si la valeur actuelle est toujours valide
71
+ // (on pourrait faire une vérification plus poussée ici)
72
+ // Pour l'instant, on garde la valeur et laisse l'utilisateur la changer si nécessaire
73
+ }
74
+ }, [parentValue, needsParent, value]);
75
+ // 7. Gérer le changement de valeur
76
+ const handleChange = (option) => {
77
+ if (!option) {
78
+ onChange(null);
79
+ return;
80
+ }
81
+ // Convertir SelectOption vers SelectDto
82
+ const selectDto = {
83
+ id: typeof option.id === 'string' ? parseInt(option.id, 10) : option.id,
84
+ code: option.code || '',
85
+ designation: option.designation
86
+ };
87
+ onChange(selectDto);
88
+ };
89
+ // 8. Déterminer si le champ est désactivé
90
+ const isDisabled = disabled || loading || (needsParent && !parentValue);
91
+ // 9. Déterminer le message du placeholder
92
+ const getPlaceholder = () => {
93
+ if (loading)
94
+ return 'Chargement...';
95
+ if (needsParent && !parentValue) {
96
+ return `Sélectionnez d'abord ${getParentLabel(variable.type)}`;
97
+ }
98
+ if (items.length === 0 && !loading) {
99
+ return `Aucun(e) ${variable.libelle?.toLowerCase() || 'élément'} disponible`;
100
+ }
101
+ return `Sélectionner ${variable.libelle || 'un élément'}...`;
102
+ };
103
+ // 10. Convertir les items en SelectOption pour SearchableSelect
104
+ const options = items.map(item => ({
105
+ id: item.id,
106
+ code: item.code,
107
+ designation: item.designation
108
+ }));
109
+ // 11. Convertir la valeur actuelle en SelectOption
110
+ const currentValue = value ? {
111
+ id: value.id,
112
+ code: value.code,
113
+ designation: value.designation
114
+ } : null;
115
+ // 12. Rendu du composant
116
+ 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: needsParent && !parentValue
117
+ ? `Veuillez d'abord sélectionner ${getParentLabel(variable.type)}`
118
+ : `Aucun(e) ${variable.libelle?.toLowerCase() || 'élément'} disponible`, formatOptionLabel: (option) => {
119
+ // Formater le label avec le code si disponible
120
+ return option.code ? `${option.code} ${option.designation}` : option.designation;
121
+ } }), needsParent && !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.type)] })), 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..."] }))] }));
122
+ };
123
+ export default GeographicCascadeInput;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Composant de sélection avec recherche intégrée
3
+ * Inspiré du design de production avec champ de filtre
4
+ */
5
+ import React from 'react';
6
+ export interface SelectOption {
7
+ id: number | string;
8
+ code?: string;
9
+ designation: string;
10
+ [key: string]: any;
11
+ }
12
+ interface SearchableSelectProps {
13
+ options: SelectOption[];
14
+ value?: SelectOption | null;
15
+ onChange: (option: SelectOption | null) => void;
16
+ placeholder?: string;
17
+ searchPlaceholder?: string;
18
+ disabled?: boolean;
19
+ required?: boolean;
20
+ loading?: boolean;
21
+ error?: string;
22
+ className?: string;
23
+ noOptionsMessage?: string;
24
+ formatOptionLabel?: (option: SelectOption) => string;
25
+ }
26
+ export declare const SearchableSelect: React.FC<SearchableSelectProps>;
27
+ export default SearchableSelect;
28
+ //# sourceMappingURL=SearchableSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchableSelect.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/SearchableSelect.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAsC,MAAM,OAAO,CAAC;AAE3D,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,UAAU,qBAAqB;IAC7B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC;CACtD;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA+L5D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Composant de sélection avec recherche intégrée
4
+ * Inspiré du design de production avec champ de filtre
5
+ */
6
+ import { useState, useRef, useEffect } from 'react';
7
+ export const SearchableSelect = ({ options, value, onChange, placeholder = 'Sélectionner...', searchPlaceholder = 'Rechercher...', disabled = false, required = false, loading = false, error, className = '', noOptionsMessage = 'Aucune option disponible', formatOptionLabel }) => {
8
+ const [isOpen, setIsOpen] = useState(false);
9
+ const [searchTerm, setSearchTerm] = useState('');
10
+ const dropdownRef = useRef(null);
11
+ const searchInputRef = useRef(null);
12
+ // Fermer le dropdown quand on clique à l'extérieur
13
+ useEffect(() => {
14
+ const handleClickOutside = (event) => {
15
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
16
+ setIsOpen(false);
17
+ setSearchTerm('');
18
+ }
19
+ };
20
+ document.addEventListener('mousedown', handleClickOutside);
21
+ return () => document.removeEventListener('mousedown', handleClickOutside);
22
+ }, []);
23
+ // Focus sur le champ de recherche quand le dropdown s'ouvre
24
+ useEffect(() => {
25
+ if (isOpen && searchInputRef.current) {
26
+ searchInputRef.current.focus();
27
+ }
28
+ }, [isOpen]);
29
+ // Filtrer les options selon le terme de recherche
30
+ const filteredOptions = options.filter(option => {
31
+ const label = formatOptionLabel ? formatOptionLabel(option) : option.designation;
32
+ const searchLower = searchTerm.toLowerCase();
33
+ return (label.toLowerCase().includes(searchLower) ||
34
+ (option.code && option.code.toLowerCase().includes(searchLower)));
35
+ });
36
+ // Gérer la sélection d'une option
37
+ const handleSelect = (option) => {
38
+ onChange(option);
39
+ setIsOpen(false);
40
+ setSearchTerm('');
41
+ };
42
+ // Gérer l'ouverture/fermeture du dropdown
43
+ const toggleDropdown = () => {
44
+ if (!disabled && !loading) {
45
+ setIsOpen(!isOpen);
46
+ if (!isOpen) {
47
+ setSearchTerm('');
48
+ }
49
+ }
50
+ };
51
+ // Formater le label d'une option
52
+ const getOptionLabel = (option) => {
53
+ if (formatOptionLabel) {
54
+ return formatOptionLabel(option);
55
+ }
56
+ return option.code ? `${option.code} ${option.designation}` : option.designation;
57
+ };
58
+ // Obtenir le label de la valeur sélectionnée
59
+ const getSelectedLabel = () => {
60
+ if (!value)
61
+ return placeholder;
62
+ return getOptionLabel(value);
63
+ };
64
+ return (_jsxs("div", { className: `relative w-full ${className}`, ref: dropdownRef, children: [_jsx("button", { type: "button", onClick: toggleDropdown, disabled: disabled || loading, className: `w-full px-3 py-2 text-left border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors ${error ? 'border-red-300 bg-red-50' : 'border-gray-300'} ${disabled || loading
65
+ ? 'bg-gray-100 cursor-not-allowed text-gray-500'
66
+ : 'bg-white text-gray-900 hover:border-gray-400'} ${isOpen ? 'ring-2 ring-blue-500 border-transparent' : ''}`, children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: value ? 'text-gray-900' : 'text-gray-500', children: loading ? 'Chargement...' : getSelectedLabel() }), _jsx("svg", { className: `w-5 h-5 text-gray-400 transition-transform ${isOpen ? 'transform rotate-180' : ''}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }) }), isOpen && !disabled && !loading && (_jsxs("div", { className: "absolute z-50 w-full mt-1 bg-white border border-gray-300 rounded-lg shadow-lg max-h-80 overflow-hidden", children: [_jsx("div", { className: "p-2 border-b border-gray-200 bg-gray-50", children: _jsxs("div", { className: "relative", children: [_jsx("svg", { className: "absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx("input", { ref: searchInputRef, type: "text", value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), placeholder: searchPlaceholder, className: "w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm" })] }) }), _jsx("div", { className: "overflow-y-auto max-h-60", children: filteredOptions.length === 0 ? (_jsx("div", { className: "px-4 py-3 text-sm text-gray-500 text-center", children: searchTerm ? `Aucun résultat pour "${searchTerm}"` : noOptionsMessage })) : (_jsx("ul", { className: "py-1", children: filteredOptions.map((option) => {
67
+ const isSelected = value?.id === option.id;
68
+ return (_jsx("li", { children: _jsx("button", { type: "button", onClick: () => handleSelect(option), className: `w-full px-4 py-2 text-left text-sm hover:bg-blue-50 transition-colors ${isSelected ? 'bg-blue-100 text-blue-900 font-medium' : 'text-gray-900'}`, children: getOptionLabel(option) }) }, option.id));
69
+ }) })) })] })), error && (_jsxs("p", { className: "mt-1 text-sm text-red-600 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 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z", clipRule: "evenodd" }) }), error] })), required && !value && (_jsx("p", { className: "mt-1 text-xs text-gray-500", children: "Ce champ est obligatoire" }))] }));
70
+ };
71
+ export default SearchableSelect;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Composant SelectInput - Liste de sélection avec recherche et pagination
2
+ * Composant SelectInput - Liste de sélection avec recherche intégrée
3
3
  * RSU v2 - Moteur de Rendu des Formulaires d'Enquête
4
4
  */
5
5
  import React from 'react';
@@ -1 +1 @@
1
- {"version":3,"file":"SelectInput.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/SelectInput.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAmB,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGxE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,kBAAkB,GAAG;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IACrD,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;CACpB;AAED,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAkI3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"SelectInput.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/SelectInput.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIxE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,kBAAkB,GAAG;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IACrD,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;CACpB;AAED,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAuF3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -1,15 +1,12 @@
1
1
  /**
2
- * Composant SelectInput - Liste de sélection avec recherche et pagination
2
+ * Composant SelectInput - Liste de sélection avec recherche intégrée
3
3
  * RSU v2 - Moteur de Rendu des Formulaires d'Enquête
4
4
  */
5
5
  'use client';
6
6
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
- import { useState } from 'react';
8
7
  import { VariableValueConverter } from '../../lib/utils/variableValueConverter';
8
+ import SearchableSelect from './SearchableSelect';
9
9
  const SelectInput = ({ variable, value, onChange, onBlur, error, disabled }) => {
10
- const [searchTerm, setSearchTerm] = useState('');
11
- const [currentPage, setCurrentPage] = useState(1);
12
- const itemsPerPage = 10;
13
10
  // Récupérer les options depuis la propriété valeur OU valeurDefaut (format: "Code1#Designation1|Code2#Designation2")
14
11
  const optionsSource = variable.proprietes?.valeur || variable.valeurDefaut;
15
12
  const allOptions = optionsSource && typeof optionsSource === 'string'
@@ -18,22 +15,30 @@ const SelectInput = ({ variable, value, onChange, onBlur, error, disabled }) =>
18
15
  code: opt.value.toString(),
19
16
  designation: opt.label
20
17
  }));
21
- // Filtrage par recherche
22
- const filteredOptions = allOptions.filter(option => option.designation.toLowerCase().includes(searchTerm.toLowerCase()) ||
23
- option.code.toLowerCase().includes(searchTerm.toLowerCase()));
24
- // Pagination
25
- const totalPages = Math.ceil(filteredOptions.length / itemsPerPage);
26
- const startIndex = (currentPage - 1) * itemsPerPage;
27
- const paginatedOptions = filteredOptions.slice(startIndex, startIndex + itemsPerPage);
28
18
  const selectedValue = value || '';
29
- // Affichage radio pour peu d'options
30
- if (allOptions.length <= 5 && !searchTerm) {
19
+ // Affichage radio pour peu d'options (≤5)
20
+ if (allOptions.length <= 5) {
31
21
  return (_jsx("div", { className: "space-y-2", children: allOptions.map((option) => (_jsxs("label", { className: "flex items-center space-x-2 cursor-pointer", children: [_jsx("input", { type: "radio", name: variable.code, value: option.code, checked: selectedValue === option.code, onChange: (e) => onChange(e.target.value), onBlur: onBlur, disabled: disabled, className: `w-4 h-4 text-green-600 border-gray-300 focus:ring-green-500 ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}` }), _jsx("span", { className: `text-sm ${disabled ? 'text-gray-400' : 'text-gray-700'}`, children: option.designation })] }, option.code))) }));
32
22
  }
33
- // Affichage avec recherche et pagination pour beaucoup d'options
34
- return (_jsxs("div", { className: "space-y-3", children: [allOptions.length > 10 && (_jsx("div", { className: "relative", children: _jsx("input", { type: "text", placeholder: "Rechercher...", value: searchTerm, onChange: (e) => {
35
- setSearchTerm(e.target.value.toUpperCase());
36
- setCurrentPage(1); // Reset à la première page lors de la recherche
37
- }, disabled: disabled, className: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent text-sm text-gray-900 bg-white" }) })), _jsxs("select", { value: selectedValue, onChange: (e) => onChange(e.target.value || ''), onBlur: onBlur, disabled: disabled, className: `w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent text-gray-900 ${error ? 'border-red-500' : 'border-gray-300'} ${disabled ? 'bg-gray-100 cursor-not-allowed text-gray-500' : 'bg-white'}`, children: [_jsx("option", { value: "", children: "S\u00E9lectionner..." }), paginatedOptions.map((option) => (_jsxs("option", { value: option.code, children: [option.code, " - ", option.designation] }, option.code)))] }), totalPages > 1 && (_jsxs("div", { className: "flex items-center justify-between text-sm text-gray-600", children: [_jsxs("div", { children: ["Page ", currentPage, " sur ", totalPages, " (", filteredOptions.length, " \u00E9l\u00E9ments)"] }), _jsxs("div", { className: "flex space-x-2", children: [_jsx("button", { type: "button", onClick: () => setCurrentPage(Math.max(1, currentPage - 1)), disabled: currentPage === 1 || disabled, className: "px-2 py-1 border border-gray-300 rounded text-xs hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed", children: "Pr\u00E9c\u00E9dent" }), _jsx("button", { type: "button", onClick: () => setCurrentPage(Math.min(totalPages, currentPage + 1)), disabled: currentPage === totalPages || disabled, className: "px-2 py-1 border border-gray-300 rounded text-xs hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed", children: "Suivant" })] })] })), selectedValue && (_jsx("div", { className: "p-2 bg-green-50 border border-green-200 rounded text-sm", children: _jsxs("span", { className: "text-green-800", children: ["S\u00E9lectionn\u00E9: ", allOptions.find(opt => opt.code === selectedValue)?.designation || selectedValue] }) }))] }));
23
+ // Convertir les options au format SelectOption pour SearchableSelect
24
+ const selectOptions = allOptions.map(opt => ({
25
+ id: opt.code,
26
+ code: opt.code,
27
+ designation: opt.designation
28
+ }));
29
+ // Trouver l'option sélectionnée
30
+ const selectedOption = selectOptions.find(opt => opt.code === selectedValue) || null;
31
+ // Gérer le changement de sélection
32
+ const handleChange = (option) => {
33
+ onChange(option?.code || '');
34
+ if (onBlur)
35
+ onBlur();
36
+ };
37
+ // Format d'affichage: "Code - Designation"
38
+ const formatOptionLabel = (option) => {
39
+ return `${option.code} - ${option.designation}`;
40
+ };
41
+ // Affichage avec SearchableSelect pour beaucoup d'options (>5)
42
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(SearchableSelect, { options: selectOptions, value: selectedOption, onChange: handleChange, placeholder: "S\u00E9lectionner...", searchPlaceholder: "Rechercher...", disabled: disabled, required: variable.estObligatoire, error: error, formatOptionLabel: formatOptionLabel, noOptionsMessage: "Aucune option trouv\u00E9e" }), selectedValue && selectedOption && (_jsx("div", { className: "p-2 bg-green-50 border border-green-200 rounded text-sm", children: _jsxs("span", { className: "text-green-800", children: ["S\u00E9lectionn\u00E9: ", selectedOption.designation] }) }))] }));
38
43
  };
39
44
  export default SelectInput;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Utilitaires pour résoudre les dépendances entre variables géographiques
3
+ * Permet de gérer la cascade : District → Région → Département → Sous-Préfecture → Quartier/Village
4
+ */
5
+ /**
6
+ * Extrait le code d'une variable depuis le format "${CODE}"
7
+ * @param valeur - Valeur du champ variable.valeur (ex: "${G_01}")
8
+ * @returns Code de la variable (ex: "G_01") ou null si pas de référence
9
+ *
10
+ * @example
11
+ * extractVariableCode("${G_01}") // Returns: "G_01"
12
+ * extractVariableCode("${DISTRICT_VAR}") // Returns: "DISTRICT_VAR"
13
+ * extractVariableCode(null) // Returns: null
14
+ * extractVariableCode("G_01") // Returns: null (pas de ${})
15
+ */
16
+ export declare function extractVariableCode(valeur: string | null | undefined): string | null;
17
+ /**
18
+ * Résout la valeur ID d'une variable parente depuis les réponses
19
+ * @param variable - Variable actuelle
20
+ * @param reponses - Toutes les réponses du formulaire
21
+ * @returns ID de la valeur parente ou null
22
+ *
23
+ * @example
24
+ * // Variable G_03 (Département) avec valeur: "${G_02}"
25
+ * // reponses = { "G_02": { valeur: { id: 10, code: "REG01", designation: "Abidjan" } } }
26
+ * resolveParentValue(variable, reponses) // Returns: 10
27
+ */
28
+ export declare function resolveParentValue(variable: any, reponses: Record<string, any>): number | null;
29
+ /**
30
+ * Détermine l'endpoint API selon le type de variable et l'ID du parent
31
+ * @param variableType - Type de la variable (DISTRICT, REGION, DEPARTEMENT, etc.)
32
+ * @param parentId - ID du parent (optionnel)
33
+ * @returns Endpoint API ou null si invalide
34
+ *
35
+ * @example
36
+ * getApiEndpoint('DISTRICT') // Returns: '/api/v1/Districts/select'
37
+ * getApiEndpoint('REGION', 1) // Returns: '/api/v1/Regions/1/select'
38
+ * getApiEndpoint('REGION') // Returns: null (parent requis mais absent)
39
+ */
40
+ export declare function getApiEndpoint(variableType: string, parentId?: number | null): string | null;
41
+ /**
42
+ * Détermine le libellé du parent selon le type de variable
43
+ * @param variableType - Type de la variable
44
+ * @returns Libellé du parent en français
45
+ *
46
+ * @example
47
+ * getParentLabel('REGION') // Returns: 'un district'
48
+ * getParentLabel('DEPARTEMENT') // Returns: 'une région'
49
+ */
50
+ export declare function getParentLabel(variableType: string): string;
51
+ /**
52
+ * Vérifie si un type de variable nécessite un parent
53
+ * @param variableType - Type de la variable
54
+ * @returns true si un parent est requis
55
+ *
56
+ * @example
57
+ * requiresParent('DISTRICT') // Returns: false
58
+ * requiresParent('REGION') // Returns: true
59
+ */
60
+ export declare function requiresParent(variableType: string): boolean;
61
+ /**
62
+ * Vérifie si un type de variable est géographique
63
+ * @param variableType - Type de la variable
64
+ * @returns true si c'est un type géographique
65
+ *
66
+ * @example
67
+ * isGeographicType('DISTRICT') // Returns: true
68
+ * isGeographicType('TEXT') // Returns: false
69
+ */
70
+ export declare function isGeographicType(variableType: string): boolean;
71
+ //# sourceMappingURL=variableDependencyResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variableDependencyResolver.d.ts","sourceRoot":"","sources":["../../src/utils/variableDependencyResolver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAMpF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,GAAG,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,GAAG,IAAI,CAiCf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CA0B5F;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"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Utilitaires pour résoudre les dépendances entre variables géographiques
3
+ * Permet de gérer la cascade : District → Région → Département → Sous-Préfecture → Quartier/Village
4
+ */
5
+ /**
6
+ * Extrait le code d'une variable depuis le format "${CODE}"
7
+ * @param valeur - Valeur du champ variable.valeur (ex: "${G_01}")
8
+ * @returns Code de la variable (ex: "G_01") ou null si pas de référence
9
+ *
10
+ * @example
11
+ * extractVariableCode("${G_01}") // Returns: "G_01"
12
+ * extractVariableCode("${DISTRICT_VAR}") // Returns: "DISTRICT_VAR"
13
+ * extractVariableCode(null) // Returns: null
14
+ * extractVariableCode("G_01") // Returns: null (pas de ${})
15
+ */
16
+ export function extractVariableCode(valeur) {
17
+ if (!valeur)
18
+ return null;
19
+ // Match ${ANYTHING}
20
+ const match = valeur.match(/\$\{([^}]+)\}/);
21
+ return match ? match[1] : null;
22
+ }
23
+ /**
24
+ * Résout la valeur ID d'une variable parente depuis les réponses
25
+ * @param variable - Variable actuelle
26
+ * @param reponses - Toutes les réponses du formulaire
27
+ * @returns ID de la valeur parente ou null
28
+ *
29
+ * @example
30
+ * // Variable G_03 (Département) avec valeur: "${G_02}"
31
+ * // reponses = { "G_02": { valeur: { id: 10, code: "REG01", designation: "Abidjan" } } }
32
+ * resolveParentValue(variable, reponses) // Returns: 10
33
+ */
34
+ export function resolveParentValue(variable, reponses) {
35
+ // Extraire le code de la variable parente
36
+ const parentCode = extractVariableCode(variable.valeur);
37
+ if (!parentCode)
38
+ return null;
39
+ // Récupérer la réponse de la variable parente
40
+ const parentReponse = reponses[parentCode];
41
+ if (!parentReponse?.valeur)
42
+ return null;
43
+ // La valeur peut être :
44
+ // 1. Un objet SelectDto : { id: number, code: string, designation: string }
45
+ // 2. Un nombre (ID direct)
46
+ // 3. Autre format (on essaie d'extraire l'ID)
47
+ const parentValue = parentReponse.valeur;
48
+ // Cas 1 : Objet avec propriété id
49
+ if (typeof parentValue === 'object' && parentValue !== null && 'id' in parentValue) {
50
+ return typeof parentValue.id === 'number' ? parentValue.id : null;
51
+ }
52
+ // Cas 2 : Nombre direct
53
+ if (typeof parentValue === 'number') {
54
+ return parentValue;
55
+ }
56
+ // Cas 3 : String qui pourrait être un nombre
57
+ if (typeof parentValue === 'string') {
58
+ const parsed = parseInt(parentValue, 10);
59
+ return isNaN(parsed) ? null : parsed;
60
+ }
61
+ return null;
62
+ }
63
+ /**
64
+ * Détermine l'endpoint API selon le type de variable et l'ID du parent
65
+ * @param variableType - Type de la variable (DISTRICT, REGION, DEPARTEMENT, etc.)
66
+ * @param parentId - ID du parent (optionnel)
67
+ * @returns Endpoint API ou null si invalide
68
+ *
69
+ * @example
70
+ * getApiEndpoint('DISTRICT') // Returns: '/api/v1/Districts/select'
71
+ * getApiEndpoint('REGION', 1) // Returns: '/api/v1/Regions/1/select'
72
+ * getApiEndpoint('REGION') // Returns: null (parent requis mais absent)
73
+ */
74
+ export function getApiEndpoint(variableType, parentId) {
75
+ const type = variableType?.toUpperCase();
76
+ switch (type) {
77
+ case 'DISTRICT':
78
+ return '/api/v1/Districts/select';
79
+ case 'REGION':
80
+ return parentId ? `/api/v1/Regions/${parentId}/select` : null;
81
+ case 'DEPARTEMENT':
82
+ return parentId ? `/api/v1/Departements/${parentId}/select` : null;
83
+ case 'SOUS_PREFECTURE':
84
+ case 'SOUSPREFECTURE':
85
+ return parentId ? `/api/v1/SousPrefectures/${parentId}/select` : null;
86
+ case 'QUARTIER':
87
+ return parentId ? `/api/v1/Quartiers/${parentId}/select` : null;
88
+ case 'VILLAGE':
89
+ return parentId ? `/api/v1/Villages/${parentId}/select` : null;
90
+ default:
91
+ return null;
92
+ }
93
+ }
94
+ /**
95
+ * Détermine le libellé du parent selon le type de variable
96
+ * @param variableType - Type de la variable
97
+ * @returns Libellé du parent en français
98
+ *
99
+ * @example
100
+ * getParentLabel('REGION') // Returns: 'un district'
101
+ * getParentLabel('DEPARTEMENT') // Returns: 'une région'
102
+ */
103
+ export function getParentLabel(variableType) {
104
+ const type = variableType?.toUpperCase();
105
+ const labels = {
106
+ 'REGION': 'un district',
107
+ 'DEPARTEMENT': 'une région',
108
+ 'SOUS_PREFECTURE': 'un département',
109
+ 'SOUSPREFECTURE': 'un département',
110
+ 'QUARTIER': 'une sous-préfecture',
111
+ 'VILLAGE': 'une sous-préfecture'
112
+ };
113
+ return labels[type] || 'le niveau précédent';
114
+ }
115
+ /**
116
+ * Vérifie si un type de variable nécessite un parent
117
+ * @param variableType - Type de la variable
118
+ * @returns true si un parent est requis
119
+ *
120
+ * @example
121
+ * requiresParent('DISTRICT') // Returns: false
122
+ * requiresParent('REGION') // Returns: true
123
+ */
124
+ export function requiresParent(variableType) {
125
+ const type = variableType?.toUpperCase();
126
+ return [
127
+ 'REGION',
128
+ 'DEPARTEMENT',
129
+ 'SOUS_PREFECTURE',
130
+ 'SOUSPREFECTURE',
131
+ 'QUARTIER',
132
+ 'VILLAGE'
133
+ ].includes(type);
134
+ }
135
+ /**
136
+ * Vérifie si un type de variable est géographique
137
+ * @param variableType - Type de la variable
138
+ * @returns true si c'est un type géographique
139
+ *
140
+ * @example
141
+ * isGeographicType('DISTRICT') // Returns: true
142
+ * isGeographicType('TEXT') // Returns: false
143
+ */
144
+ export function isGeographicType(variableType) {
145
+ const type = variableType?.toUpperCase();
146
+ return [
147
+ 'DISTRICT',
148
+ 'REGION',
149
+ 'DEPARTEMENT',
150
+ 'SOUS_PREFECTURE',
151
+ 'SOUSPREFECTURE',
152
+ 'QUARTIER',
153
+ 'VILLAGE'
154
+ ].includes(type);
155
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsuci/shared-form-components",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Composants partagés de rendu de formulaires RSU v2 - Package local pour frontend Admin et Public",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",