@rsuci/shared-form-components 1.0.77 → 1.0.78

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.
@@ -1 +1 @@
1
- {"version":3,"file":"IdDocInput.d.ts","sourceRoot":"","sources":["../../src/components/IdDocInput.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAsC,MAAM,OAAO,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIrE,MAAM,WAAW,eAAe;IAC9B,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;CACpB;AAuBD,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAkYzC,CAAC;AAEF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"IdDocInput.d.ts","sourceRoot":"","sources":["../../src/components/IdDocInput.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAsC,MAAM,OAAO,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKrE,MAAM,WAAW,eAAe;IAC9B,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;CACpB;AAuBD,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA8XzC,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -9,6 +9,7 @@
9
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  import React, { useState, useEffect, useRef } from 'react';
11
11
  import { AlertCircle, Camera, Upload, X, FileText, CreditCard } from 'lucide-react';
12
+ import { VariableValueConverter } from '../lib/utils/variableValueConverter';
12
13
  // Types de documents d'identité supportés
13
14
  const DOC_TYPES = [
14
15
  { code: 'CNI', label: 'Carte Nationale d\'Identité' },
@@ -44,10 +45,8 @@ const IdDocInput = ({ variable, value, onChange, onBlur, error, disabled }) => {
44
45
  }, [value]);
45
46
  // Mettre à jour l'aperçu quand la valeur change
46
47
  useEffect(() => {
47
- if (currentData.file) {
48
- const reader = new FileReader();
49
- reader.onload = (e) => setPreview(e.target?.result);
50
- reader.readAsDataURL(currentData.file);
48
+ if (currentData.imageData) {
49
+ setPreview(currentData.imageData);
51
50
  }
52
51
  else {
53
52
  setPreview(null);
@@ -55,7 +54,7 @@ const IdDocInput = ({ variable, value, onChange, onBlur, error, disabled }) => {
55
54
  fileInputRef.current.value = '';
56
55
  }
57
56
  }
58
- }, [currentData.file]);
57
+ }, [currentData.imageData]);
59
58
  // Nettoyer le stream quand le composant est démonté
60
59
  useEffect(() => {
61
60
  return () => {
@@ -67,21 +66,21 @@ const IdDocInput = ({ variable, value, onChange, onBlur, error, disabled }) => {
67
66
  // Mettre à jour les données
68
67
  const updateData = (updates) => {
69
68
  const newData = { ...currentData, ...updates };
70
- onChange(JSON.stringify(newData));
69
+ onChange(newData);
71
70
  };
72
71
  // Gérer la sélection de fichier
73
- const handleFileSelect = (event) => {
72
+ const handleFileSelect = async (event) => {
74
73
  if (disabled)
75
74
  return;
76
75
  const file = event.target.files?.[0];
77
76
  if (!file)
78
77
  return;
79
- processFile(file);
78
+ await processFile(file);
80
79
  };
81
80
  // Traiter le fichier sélectionné
82
- const processFile = (file) => {
83
- // Validation de la taille
84
- const maxSize = variable.proprietes?.maxFileSize || 10 * 1024 * 1024; // 10MB par défaut
81
+ const processFile = async (file) => {
82
+ // Validation de la taille (5MB)
83
+ const maxSize = variable.proprietes?.maxFileSize || 5 * 1024 * 1024;
85
84
  if (file.size > maxSize) {
86
85
  alert(`Fichier trop volumineux. Taille maximum: ${Math.round(maxSize / 1024 / 1024)}MB`);
87
86
  return;
@@ -95,12 +94,13 @@ const IdDocInput = ({ variable, value, onChange, onBlur, error, disabled }) => {
95
94
  alert(`Type de fichier non supporté. Types acceptés: ${acceptedTypes.join(', ')}`);
96
95
  return;
97
96
  }
98
- // Sauvegarder le fichier
97
+ // Convertir en base64
98
+ const imageData = await VariableValueConverter.fileToBase64(file);
99
99
  updateData({
100
- file,
101
- fileName: file.name,
102
- fileSize: file.size,
103
- fileType: file.type,
100
+ imageData,
101
+ name: file.name,
102
+ size: file.size,
103
+ type: file.type,
104
104
  timestamp: new Date(),
105
105
  source: captureMode === 'camera' ? 'camera' : 'upload'
106
106
  });
@@ -113,7 +113,7 @@ const IdDocInput = ({ variable, value, onChange, onBlur, error, disabled }) => {
113
113
  setIsCapturing(true);
114
114
  const mediaStream = await navigator.mediaDevices.getUserMedia({
115
115
  video: {
116
- facingMode: 'environment' // Caméra arrière par défaut
116
+ facingMode: 'environment'
117
117
  }
118
118
  });
119
119
  setStream(mediaStream);
@@ -144,16 +144,13 @@ const IdDocInput = ({ variable, value, onChange, onBlur, error, disabled }) => {
144
144
  const context = canvas.getContext('2d');
145
145
  if (!context)
146
146
  return;
147
- // Définir les dimensions du canvas
148
147
  canvas.width = video.videoWidth;
149
148
  canvas.height = video.videoHeight;
150
- // Dessiner l'image du video sur le canvas
151
149
  context.drawImage(video, 0, 0);
152
- // Convertir en blob
153
- canvas.toBlob((blob) => {
150
+ canvas.toBlob(async (blob) => {
154
151
  if (blob) {
155
152
  const file = new File([blob], `iddoc_${Date.now()}.jpg`, { type: 'image/jpeg' });
156
- processFile(file);
153
+ await processFile(file);
157
154
  stopCamera();
158
155
  }
159
156
  }, 'image/jpeg', 0.9);
@@ -161,10 +158,10 @@ const IdDocInput = ({ variable, value, onChange, onBlur, error, disabled }) => {
161
158
  // Supprimer le document
162
159
  const clearDocument = () => {
163
160
  updateData({
164
- file: undefined,
165
- fileName: undefined,
166
- fileSize: undefined,
167
- fileType: undefined,
161
+ imageData: undefined,
162
+ name: undefined,
163
+ size: undefined,
164
+ type: undefined,
168
165
  timestamp: undefined,
169
166
  source: undefined
170
167
  });
@@ -183,6 +180,6 @@ const IdDocInput = ({ variable, value, onChange, onBlur, error, disabled }) => {
183
180
  startCamera();
184
181
  }, disabled: disabled, className: `flex items-center px-3 py-2 rounded-lg text-sm font-medium transition-colors ${captureMode === 'camera'
185
182
  ? 'bg-blue-600 text-white'
186
- : 'bg-gray-200 text-gray-700 hover:bg-gray-300'} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`, children: [_jsx(Camera, { className: "w-4 h-4 mr-2" }), "Cam\u00E9ra"] })] }), captureMode === 'upload' && !preview && (_jsxs("div", { className: "relative", children: [_jsx("input", { ref: fileInputRef, type: "file", accept: "image/*,application/pdf", onChange: handleFileSelect, disabled: disabled, className: "absolute inset-0 w-full h-full opacity-0 cursor-pointer" }), _jsxs("div", { className: `flex flex-col items-center justify-center p-6 border-2 border-dashed rounded-lg ${error ? 'border-red-500' : 'border-gray-300'} ${disabled ? 'bg-gray-100 cursor-not-allowed' : 'bg-white hover:border-green-500 cursor-pointer'}`, children: [_jsx(CreditCard, { className: "w-10 h-10 text-gray-400 mb-2" }), _jsx("span", { className: "text-sm text-gray-600", children: "Cliquez ou glissez-d\u00E9posez" }), _jsx("span", { className: "text-xs text-gray-400 mt-1", children: "JPG, PNG, PDF - Max 10MB" })] })] })), captureMode === 'camera' && (_jsxs("div", { className: "space-y-3", children: [isCapturing && (_jsxs("div", { className: "relative", children: [_jsx("video", { ref: videoRef, autoPlay: true, playsInline: true, className: "w-full max-w-md mx-auto rounded-lg border border-gray-300" }), _jsxs("div", { className: "flex justify-center space-x-2 mt-2", children: [_jsxs("button", { type: "button", onClick: capturePhoto, disabled: disabled, className: "flex items-center px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 focus:ring-2 focus:ring-green-500 focus:ring-offset-2 disabled:opacity-50", children: [_jsx(Camera, { className: "w-4 h-4 mr-2" }), "Capturer"] }), _jsxs("button", { type: "button", onClick: stopCamera, disabled: disabled, className: "flex items-center px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 focus:ring-2 focus:ring-red-500 focus:ring-offset-2 disabled:opacity-50", children: [_jsx(X, { className: "w-4 h-4 mr-2" }), "Arr\u00EAter"] })] })] })), !isCapturing && !preview && (_jsxs("button", { type: "button", onClick: startCamera, disabled: disabled, className: "w-full flex items-center justify-center px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50", children: [_jsx(Camera, { className: "w-5 h-5 mr-2" }), "D\u00E9marrer la cam\u00E9ra"] }))] })), _jsx("canvas", { ref: canvasRef, style: { display: 'none' } }), preview && (_jsxs("div", { className: "relative", children: [currentData.fileType?.startsWith('image/') ? (_jsx("img", { src: preview, alt: "Aper\u00E7u du document", className: "w-full max-w-md mx-auto rounded-lg shadow-md border border-gray-200" })) : (_jsxs("div", { className: "flex items-center justify-center p-6 bg-gray-100 rounded-lg", children: [_jsx(FileText, { className: "w-10 h-10 text-gray-500 mr-3" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-gray-700", children: currentData.fileName }), _jsx("p", { className: "text-xs text-gray-500", children: currentData.fileSize ? `${Math.round(currentData.fileSize / 1024)} KB` : '' })] })] })), _jsx("button", { type: "button", onClick: clearDocument, disabled: disabled, className: "absolute top-2 right-2 p-1 bg-red-500 text-white rounded-full hover:bg-red-600 focus:ring-2 focus:ring-red-500 focus:ring-offset-2 disabled:opacity-50", children: _jsx(X, { className: "w-4 h-4" }) })] })), currentData.fileName && (_jsxs("div", { className: "p-2 bg-green-50 border border-green-200 rounded text-sm", children: [_jsxs("div", { className: "flex items-center text-green-800", children: [_jsx(FileText, { className: "w-4 h-4 mr-2" }), _jsx("span", { className: "font-medium", children: currentData.fileName })] }), currentData.source && (_jsxs("p", { className: "text-xs text-green-600 mt-1", children: ["Source: ", currentData.source === 'camera' ? 'Caméra' : 'Upload'] }))] }))] }), 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 })] }))] }));
183
+ : 'bg-gray-200 text-gray-700 hover:bg-gray-300'} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`, children: [_jsx(Camera, { className: "w-4 h-4 mr-2" }), "Cam\u00E9ra"] })] }), captureMode === 'upload' && !preview && (_jsxs("div", { className: "relative", children: [_jsx("input", { ref: fileInputRef, type: "file", accept: "image/*,application/pdf", onChange: handleFileSelect, disabled: disabled, className: "absolute inset-0 w-full h-full opacity-0 cursor-pointer" }), _jsxs("div", { className: `flex flex-col items-center justify-center p-6 border-2 border-dashed rounded-lg ${error ? 'border-red-500' : 'border-gray-300'} ${disabled ? 'bg-gray-100 cursor-not-allowed' : 'bg-white hover:border-green-500 cursor-pointer'}`, children: [_jsx(CreditCard, { className: "w-10 h-10 text-gray-400 mb-2" }), _jsx("span", { className: "text-sm text-gray-600", children: "Cliquez ou glissez-d\u00E9posez" }), _jsx("span", { className: "text-xs text-gray-400 mt-1", children: "JPG, PNG, PDF - Max 5MB" })] })] })), captureMode === 'camera' && (_jsxs("div", { className: "space-y-3", children: [isCapturing && (_jsxs("div", { className: "relative", children: [_jsx("video", { ref: videoRef, autoPlay: true, playsInline: true, className: "w-full max-w-md mx-auto rounded-lg border border-gray-300" }), _jsxs("div", { className: "flex justify-center space-x-2 mt-2", children: [_jsxs("button", { type: "button", onClick: capturePhoto, disabled: disabled, className: "flex items-center px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 focus:ring-2 focus:ring-green-500 focus:ring-offset-2 disabled:opacity-50", children: [_jsx(Camera, { className: "w-4 h-4 mr-2" }), "Capturer"] }), _jsxs("button", { type: "button", onClick: stopCamera, disabled: disabled, className: "flex items-center px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 focus:ring-2 focus:ring-red-500 focus:ring-offset-2 disabled:opacity-50", children: [_jsx(X, { className: "w-4 h-4 mr-2" }), "Arr\u00EAter"] })] })] })), !isCapturing && !preview && (_jsxs("button", { type: "button", onClick: startCamera, disabled: disabled, className: "w-full flex items-center justify-center px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50", children: [_jsx(Camera, { className: "w-5 h-5 mr-2" }), "D\u00E9marrer la cam\u00E9ra"] }))] })), _jsx("canvas", { ref: canvasRef, style: { display: 'none' } }), preview && (_jsxs("div", { className: "relative", children: [currentData.type?.startsWith('image/') ? (_jsx("img", { src: preview, alt: "Aper\u00E7u du document", className: "w-full max-w-md mx-auto rounded-lg shadow-md border border-gray-200" })) : (_jsxs("div", { className: "flex items-center justify-center p-6 bg-gray-100 rounded-lg", children: [_jsx(FileText, { className: "w-10 h-10 text-gray-500 mr-3" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-gray-700", children: currentData.name }), _jsx("p", { className: "text-xs text-gray-500", children: currentData.size ? `${Math.round(currentData.size / 1024)} KB` : '' })] })] })), _jsx("button", { type: "button", onClick: clearDocument, disabled: disabled, className: "absolute top-2 right-2 p-1 bg-red-500 text-white rounded-full hover:bg-red-600 focus:ring-2 focus:ring-red-500 focus:ring-offset-2 disabled:opacity-50", children: _jsx(X, { className: "w-4 h-4" }) })] })), currentData.name && (_jsxs("div", { className: "p-2 bg-green-50 border border-green-200 rounded text-sm", children: [_jsxs("div", { className: "flex items-center text-green-800", children: [_jsx(FileText, { className: "w-4 h-4 mr-2" }), _jsx("span", { className: "font-medium", children: currentData.name })] }), currentData.source && (_jsxs("p", { className: "text-xs text-green-600 mt-1", children: ["Source: ", currentData.source === 'camera' ? 'Caméra' : 'Upload'] }))] }))] }), 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 })] }))] }));
187
184
  };
188
185
  export default IdDocInput;
@@ -1 +1 @@
1
- {"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/DatePicker.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,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;AAED,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA+DzC,CAAC;AAEF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/components/inputs/DatePicker.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,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;AAED,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAkEzC,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -24,16 +24,19 @@ const DatePicker = ({ variable, value, onChange, onBlur, error, disabled, isCons
24
24
  };
25
25
  // Conversion de la valeur string vers Date pour l'affichage
26
26
  const parsedDate = VariableValueConverter.parse(value, variable.typeCode);
27
- // Formatage pour l'input selon le type
27
+ // Formatage pour l'input HTML (qui exige YYYY-MM-DD / YYYY-MM-DDTHH:mm)
28
28
  let inputValue = '';
29
29
  if (parsedDate && !isNaN(parsedDate.getTime())) {
30
+ const y = parsedDate.getFullYear();
31
+ const m = (parsedDate.getMonth() + 1).toString().padStart(2, '0');
32
+ const d = parsedDate.getDate().toString().padStart(2, '0');
30
33
  if (variable.typeCode === 'DATEHEURE') {
31
- // Format pour datetime-local: "2024-03-15T14:30"
32
- inputValue = parsedDate.toISOString().slice(0, 16);
34
+ const hh = parsedDate.getHours().toString().padStart(2, '0');
35
+ const mm = parsedDate.getMinutes().toString().padStart(2, '0');
36
+ inputValue = `${y}-${m}-${d}T${hh}:${mm}`;
33
37
  }
34
38
  else {
35
- // Format pour date: "2024-03-15"
36
- inputValue = parsedDate.toISOString().split('T')[0];
39
+ inputValue = `${y}-${m}-${d}`;
37
40
  }
38
41
  }
39
42
  const inputType = variable.typeCode === 'DATEHEURE' ? 'datetime-local' : 'date';
@@ -40,11 +40,11 @@ const ListRadioInput = ({ variable, value, onChange, onBlur, error, disabled, is
40
40
  }, [value]);
41
41
  const handleOptionChange = (code, selected) => {
42
42
  const newData = { ...data, [code]: selected };
43
- onChange(JSON.stringify(newData));
43
+ onChange(newData);
44
44
  };
45
45
  const handleClear = (code) => {
46
46
  const newData = { ...data, [code]: null };
47
- onChange(JSON.stringify(newData));
47
+ onChange(newData);
48
48
  };
49
49
  return (_jsxs("div", { className: "space-y-1", style: containerStyle, children: [_jsxs("div", { className: "flex items-center text-sm font-medium text-blue-600 mb-2 pl-1", children: [_jsx("span", { children: "Oui" }), _jsx("span", { className: "mx-2", children: "/" }), _jsx("span", { children: "Non" })] }), _jsx("div", { className: "space-y-1", children: options.map((option) => {
50
50
  const optionValue = data[option.code] ?? null;
@@ -1 +1 @@
1
- {"version":3,"file":"ImageUpload.d.ts","sourceRoot":"","sources":["../../../src/components/wrappers/ImageUpload.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,UAAU,CAAC,EAAE;YACX,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;YACzB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;SACnB,CAAC;KACH,CAAC;IACF,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,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;CACpB;AAED,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAmG3C,CAAC;AAEF,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ImageUpload.d.ts","sourceRoot":"","sources":["../../../src/components/wrappers/ImageUpload.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,UAAU,CAAC,EAAE;YACX,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;YACzB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;SACnB,CAAC;KACH,CAAC;IACF,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,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;CACpB;AAED,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAiG3C,CAAC;AAEF,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,eAAe,WAAW,CAAC"}
@@ -2,32 +2,29 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React, { useState, useEffect } from 'react';
4
4
  import { AlertCircle } from 'lucide-react';
5
+ import { VariableValueConverter } from '../../lib/utils/variableValueConverter';
5
6
  const ImageUpload = ({ variable, value, onChange, error, disabled }) => {
6
7
  const [preview, setPreview] = useState(null);
7
8
  const fileInputRef = React.useRef(null);
8
9
  useEffect(() => {
9
- if (value && typeof value === 'object' && value.file) {
10
- const file = value.file;
11
- const reader = new FileReader();
12
- reader.onload = (e) => setPreview(e.target?.result);
13
- reader.readAsDataURL(file);
10
+ if (value && typeof value === 'object' && value.imageData) {
11
+ setPreview(value.imageData);
14
12
  }
15
13
  else {
16
14
  setPreview(null);
17
- // Réinitialiser l'input file quand la valeur est supprimée
18
15
  if (fileInputRef.current) {
19
16
  fileInputRef.current.value = '';
20
17
  }
21
18
  }
22
19
  }, [value]);
23
- const handleFileSelect = (event) => {
20
+ const handleFileSelect = async (event) => {
24
21
  if (disabled)
25
22
  return;
26
23
  const file = event.target.files?.[0];
27
24
  if (!file)
28
25
  return;
29
- // Validation de la taille
30
- const maxSize = variable.proprietes?.maxFileSize || 5 * 1024 * 1024; // 5MB par défaut
26
+ // Validation de la taille (5MB)
27
+ const maxSize = variable.proprietes?.maxFileSize || 5 * 1024 * 1024;
31
28
  if (file.size > maxSize) {
32
29
  alert(`Fichier trop volumineux. Taille maximum: ${Math.round(maxSize / 1024 / 1024)}MB`);
33
30
  return;
@@ -38,9 +35,10 @@ const ImageUpload = ({ variable, value, onChange, error, disabled }) => {
38
35
  alert(`Type de fichier non supporté. Types acceptés: ${acceptedTypes.join(', ')}`);
39
36
  return;
40
37
  }
41
- // Sauvegarder le fichier
38
+ // Convertir en base64
39
+ const imageData = await VariableValueConverter.fileToBase64(file);
42
40
  onChange({
43
- file,
41
+ imageData,
44
42
  name: file.name,
45
43
  size: file.size,
46
44
  type: file.type,
@@ -1 +1 @@
1
- {"version":3,"file":"PhotoCapture.d.ts","sourceRoot":"","sources":["../../../src/components/wrappers/PhotoCapture.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,UAAU,CAAC,EAAE;YACX,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;YACzB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;SACnB,CAAC;KACH,CAAC;IACF,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,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;CACpB;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA6P7C,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,CAAC;AACxB,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"PhotoCapture.d.ts","sourceRoot":"","sources":["../../../src/components/wrappers/PhotoCapture.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,UAAU,CAAC,EAAE;YACX,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;YACzB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;SACnB,CAAC;KACH,CAAC;IACF,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,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;CACpB;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAuP7C,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,CAAC;AACxB,eAAe,YAAY,CAAC"}
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React, { useState, useEffect } from 'react';
4
4
  import { AlertCircle } from 'lucide-react';
5
+ import { VariableValueConverter } from '../../lib/utils/variableValueConverter';
5
6
  const PhotoCapture = ({ variable, value, onChange, error, disabled }) => {
6
7
  const [preview, setPreview] = useState(null);
7
8
  const [captureMode, setCaptureMode] = useState('upload');
@@ -11,15 +12,11 @@ const PhotoCapture = ({ variable, value, onChange, error, disabled }) => {
11
12
  const videoRef = React.useRef(null);
12
13
  const canvasRef = React.useRef(null);
13
14
  useEffect(() => {
14
- if (value && typeof value === 'object' && value.file) {
15
- const file = value.file;
16
- const reader = new FileReader();
17
- reader.onload = (e) => setPreview(e.target?.result);
18
- reader.readAsDataURL(file);
15
+ if (value && typeof value === 'object' && value.imageData) {
16
+ setPreview(value.imageData);
19
17
  }
20
18
  else {
21
19
  setPreview(null);
22
- // Réinitialiser l'input file quand la valeur est supprimée
23
20
  if (fileInputRef.current) {
24
21
  fileInputRef.current.value = '';
25
22
  }
@@ -41,9 +38,9 @@ const PhotoCapture = ({ variable, value, onChange, error, disabled }) => {
41
38
  return;
42
39
  processFile(file);
43
40
  };
44
- const processFile = (file) => {
45
- // Validation de la taille
46
- const maxSize = variable.proprietes?.maxFileSize || 5 * 1024 * 1024; // 5MB par défaut
41
+ const processFile = async (file) => {
42
+ // Validation de la taille (5MB)
43
+ const maxSize = variable.proprietes?.maxFileSize || 5 * 1024 * 1024;
47
44
  if (file.size > maxSize) {
48
45
  alert(`Fichier trop volumineux. Taille maximum: ${Math.round(maxSize / 1024 / 1024)}MB`);
49
46
  return;
@@ -54,9 +51,10 @@ const PhotoCapture = ({ variable, value, onChange, error, disabled }) => {
54
51
  alert(`Type de fichier non supporté. Types acceptés: ${acceptedTypes.join(', ')}`);
55
52
  return;
56
53
  }
57
- // Sauvegarder le fichier
54
+ // Convertir en base64
55
+ const imageData = await VariableValueConverter.fileToBase64(file);
58
56
  onChange({
59
- file,
57
+ imageData,
60
58
  name: file.name,
61
59
  size: file.size,
62
60
  type: file.type,
@@ -100,16 +98,13 @@ const PhotoCapture = ({ variable, value, onChange, error, disabled }) => {
100
98
  const context = canvas.getContext('2d');
101
99
  if (!context)
102
100
  return;
103
- // Définir les dimensions du canvas
104
101
  canvas.width = video.videoWidth;
105
102
  canvas.height = video.videoHeight;
106
- // Dessiner l'image du video sur le canvas
107
103
  context.drawImage(video, 0, 0);
108
- // Convertir en blob
109
- canvas.toBlob((blob) => {
104
+ canvas.toBlob(async (blob) => {
110
105
  if (blob) {
111
106
  const file = new File([blob], `photo_${Date.now()}.jpg`, { type: 'image/jpeg' });
112
- processFile(file);
107
+ await processFile(file);
113
108
  stopCamera();
114
109
  }
115
110
  }, 'image/jpeg', 0.8);
@@ -15,11 +15,14 @@ export interface GPSData {
15
15
  timestamp: Date;
16
16
  }
17
17
  export interface FileData {
18
- file: File;
18
+ imageData: string;
19
19
  name: string;
20
20
  size: number;
21
21
  type: string;
22
22
  timestamp: Date;
23
+ source?: 'camera' | 'upload';
24
+ docType?: string;
25
+ docNumber?: string;
23
26
  }
24
27
  /**
25
28
  * Classe utilitaire pour la conversion des valeurs de variables
@@ -41,6 +44,11 @@ export declare class VariableValueConverter {
41
44
  private static parseGPS;
42
45
  private static parseFile;
43
46
  private static parseRSU;
47
+ /**
48
+ * Déséchappe itérativement une chaîne JSON multi-échappée jusqu'à obtenir un objet JSON propre.
49
+ * Équivalent frontend de ParseGeographicJsonObject du backend.
50
+ */
51
+ private static unescapeToJsonObject;
44
52
  private static parseDistrict;
45
53
  private static parseGeographicData;
46
54
  private static serializeNumeric;
@@ -49,6 +57,10 @@ export declare class VariableValueConverter {
49
57
  private static serializeCheckboxValue;
50
58
  private static serializeGPS;
51
59
  private static serializeFile;
60
+ /**
61
+ * Convertit un objet File en data URL base64
62
+ */
63
+ static fileToBase64(file: File): Promise<string>;
52
64
  private static serializeRSU;
53
65
  private static serializeDistrict;
54
66
  private static serializeGeographicData;
@@ -1 +1 @@
1
- {"version":3,"file":"variableValueConverter.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/variableValueConverter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGlE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,qBAAa,sBAAsB;IAEjC;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,aAAa;IAsEjF;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM;IAgElE,OAAO,CAAC,MAAM,CAAC,YAAY;IAK3B,OAAO,CAAC,MAAM,CAAC,SAAS;IAMxB,OAAO,CAAC,MAAM,CAAC,aAAa;IAqB5B,OAAO,CAAC,MAAM,CAAC,SAAS;IAQxB,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAIjC,OAAO,CAAC,MAAM,CAAC,QAAQ;IAcvB,OAAO,CAAC,MAAM,CAAC,SAAS;IAexB,OAAO,CAAC,MAAM,CAAC,QAAQ;IAKvB,OAAO,CAAC,MAAM,CAAC,aAAa;IA4B5B,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAiElC,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAI/B,OAAO,CAAC,MAAM,CAAC,aAAa;IAK5B,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAKhC,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAIrC,OAAO,CAAC,MAAM,CAAC,YAAY;IAS3B,OAAO,CAAC,MAAM,CAAC,aAAa;IAU5B,OAAO,CAAC,MAAM,CAAC,YAAY;IAK3B,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAWhC,OAAO,CAAC,MAAM,CAAC,uBAAuB;IA2DtC;;OAEG;IACH,MAAM,CAAC,wBAAwB,CAAC,aAAa,EAAE,MAAM,GAAG,UAAU,EAAE;IAuBpE;;OAEG;IACH,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,UAAU,EAAE;;;;;IAUlD;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO;IA4ChE;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM;CAsC1E"}
1
+ {"version":3,"file":"variableValueConverter.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/variableValueConverter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGlE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,sBAAsB;IAEjC;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,aAAa;IAuEjF;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM;IAiElE,OAAO,CAAC,MAAM,CAAC,YAAY;IAK3B,OAAO,CAAC,MAAM,CAAC,SAAS;IAexB,OAAO,CAAC,MAAM,CAAC,aAAa;IAqB5B,OAAO,CAAC,MAAM,CAAC,SAAS;IAQxB,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAIjC,OAAO,CAAC,MAAM,CAAC,QAAQ;IAcvB,OAAO,CAAC,MAAM,CAAC,SAAS;IAmBxB,OAAO,CAAC,MAAM,CAAC,QAAQ;IAKvB;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAkDnC,OAAO,CAAC,MAAM,CAAC,aAAa;IA8B5B,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAoElC,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAI/B,OAAO,CAAC,MAAM,CAAC,aAAa;IAQ5B,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAUhC,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAIrC,OAAO,CAAC,MAAM,CAAC,YAAY;IAS3B,OAAO,CAAC,MAAM,CAAC,aAAa;IAc5B;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IAShD,OAAO,CAAC,MAAM,CAAC,YAAY;IAK3B,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAWhC,OAAO,CAAC,MAAM,CAAC,uBAAuB;IA2DtC;;OAEG;IACH,MAAM,CAAC,wBAAwB,CAAC,aAAa,EAAE,MAAM,GAAG,UAAU,EAAE;IAuBpE;;OAEG;IACH,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,UAAU,EAAE;;;;;IAUlD;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO;IA4ChE;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM;CAsC1E"}
@@ -46,6 +46,7 @@ export class VariableValueConverter {
46
46
  return this.parseGPS(value);
47
47
  case 'PHOTO':
48
48
  case 'IMAGE':
49
+ case 'IDDOC':
49
50
  return this.parseFile(value);
50
51
  case 'RSU':
51
52
  return this.parseRSU(value);
@@ -99,6 +100,7 @@ export class VariableValueConverter {
99
100
  return this.serializeGPS(value);
100
101
  case 'PHOTO':
101
102
  case 'IMAGE':
103
+ case 'IDDOC':
102
104
  return this.serializeFile(value);
103
105
  case 'RSU':
104
106
  return this.serializeRSU(value);
@@ -128,6 +130,13 @@ export class VariableValueConverter {
128
130
  static parseDate(value) {
129
131
  if (!value)
130
132
  return null;
133
+ // Format DD/MM/YYYY
134
+ if (value.includes('/')) {
135
+ const [day, month, year] = value.split('/');
136
+ const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
137
+ return isNaN(date.getTime()) ? null : date;
138
+ }
139
+ // Fallback ISO (YYYY-MM-DD) pour rétrocompatibilité
131
140
  const date = new Date(value);
132
141
  return isNaN(date.getTime()) ? null : date;
133
142
  }
@@ -179,12 +188,17 @@ export class VariableValueConverter {
179
188
  static parseFile(value) {
180
189
  try {
181
190
  const parsed = JSON.parse(value);
191
+ if (!parsed || typeof parsed !== 'object')
192
+ return null;
182
193
  return {
183
- file: parsed.file,
184
- name: parsed.name,
185
- size: parsed.size,
186
- type: parsed.type,
187
- timestamp: new Date(parsed.timestamp)
194
+ imageData: parsed.imageData || '',
195
+ name: parsed.name || '',
196
+ size: parsed.size || 0,
197
+ type: parsed.type || '',
198
+ timestamp: parsed.timestamp ? new Date(parsed.timestamp) : new Date(),
199
+ source: parsed.source,
200
+ docType: parsed.docType,
201
+ docNumber: parsed.docNumber
188
202
  };
189
203
  }
190
204
  catch {
@@ -195,18 +209,71 @@ export class VariableValueConverter {
195
209
  // RSU stocke directement l'ID comme string
196
210
  return value || '';
197
211
  }
212
+ /**
213
+ * Déséchappe itérativement une chaîne JSON multi-échappée jusqu'à obtenir un objet JSON propre.
214
+ * Équivalent frontend de ParseGeographicJsonObject du backend.
215
+ */
216
+ static unescapeToJsonObject(value) {
217
+ if (!value)
218
+ return null;
219
+ let current = value;
220
+ const maxIterations = 5;
221
+ for (let i = 0; i < maxIterations; i++) {
222
+ // Si c'est déjà un objet JSON, tenter le parse
223
+ if (current.startsWith('{') && current.endsWith('}')) {
224
+ try {
225
+ return JSON.parse(current);
226
+ }
227
+ catch {
228
+ return null;
229
+ }
230
+ }
231
+ // Si c'est une string JSON (commence par "), désérialiser un niveau
232
+ if (current.startsWith('"') && current.endsWith('"')) {
233
+ try {
234
+ const unescaped = JSON.parse(current);
235
+ if (typeof unescaped === 'string') {
236
+ current = unescaped;
237
+ continue;
238
+ }
239
+ return unescaped;
240
+ }
241
+ catch {
242
+ // Tenter de supprimer manuellement les guillemets et échappements
243
+ if (current.length >= 2) {
244
+ current = current.slice(1, -1).replace(/\\"/g, '"');
245
+ continue;
246
+ }
247
+ return null;
248
+ }
249
+ }
250
+ break;
251
+ }
252
+ // Dernière tentative
253
+ if (current.startsWith('{') && current.endsWith('}')) {
254
+ try {
255
+ return JSON.parse(current);
256
+ }
257
+ catch {
258
+ return null;
259
+ }
260
+ }
261
+ return null;
262
+ }
198
263
  static parseDistrict(value) {
199
264
  if (!value)
200
265
  return null;
201
266
  try {
202
- // Si c'est un JSON, parser l'objet complet
203
- if (value.startsWith('{')) {
204
- const parsed = JSON.parse(value);
205
- return {
206
- id: parsed.id,
207
- code: parsed.code,
208
- designation: parsed.designation
209
- };
267
+ // Tenter le parsing avec support du multi-échappement
268
+ if (value.startsWith('{') || value.startsWith('"')) {
269
+ const parsed = this.unescapeToJsonObject(value);
270
+ if (parsed && typeof parsed === 'object') {
271
+ return {
272
+ id: parsed.id,
273
+ code: parsed.code,
274
+ designation: parsed.designation
275
+ };
276
+ }
210
277
  }
211
278
  // Sinon, c'est juste un ID - retourner un objet minimal
212
279
  const id = parseInt(value, 10);
@@ -226,8 +293,12 @@ export class VariableValueConverter {
226
293
  if (!value)
227
294
  return null;
228
295
  try {
229
- // Parser le JSON stocké
230
- const parsed = JSON.parse(value);
296
+ // Parser le JSON stocké avec support du multi-échappement
297
+ const parsed = (value.startsWith('{') || value.startsWith('"'))
298
+ ? this.unescapeToJsonObject(value)
299
+ : JSON.parse(value);
300
+ if (!parsed || typeof parsed !== 'object')
301
+ return null;
231
302
  const result = {};
232
303
  // Reconstruire l'objet GeographicData
233
304
  if (parsed.district) {
@@ -285,12 +356,20 @@ export class VariableValueConverter {
285
356
  static serializeDate(date) {
286
357
  if (!date || isNaN(date.getTime()))
287
358
  return '';
288
- return date.toISOString().split('T')[0];
359
+ const d = date.getDate().toString().padStart(2, '0');
360
+ const m = (date.getMonth() + 1).toString().padStart(2, '0');
361
+ const y = date.getFullYear();
362
+ return `${d}/${m}/${y}`;
289
363
  }
290
364
  static serializeDateTime(date) {
291
365
  if (!date || isNaN(date.getTime()))
292
366
  return '';
293
- return date.toISOString();
367
+ const d = date.getDate().toString().padStart(2, '0');
368
+ const m = (date.getMonth() + 1).toString().padStart(2, '0');
369
+ const y = date.getFullYear();
370
+ const hh = date.getHours().toString().padStart(2, '0');
371
+ const mm = date.getMinutes().toString().padStart(2, '0');
372
+ return `${d}/${m}/${y} ${hh}:${mm}`;
294
373
  }
295
374
  static serializeCheckboxValue(values) {
296
375
  return Array.isArray(values) ? values.filter(v => v).join('|') : '';
@@ -304,12 +383,29 @@ export class VariableValueConverter {
304
383
  });
305
384
  }
306
385
  static serializeFile(fileData) {
307
- return JSON.stringify({
386
+ const obj = {
387
+ imageData: fileData.imageData,
308
388
  name: fileData.name,
309
389
  size: fileData.size,
310
390
  type: fileData.type,
311
- timestamp: fileData.timestamp.toISOString()
312
- // Note: Le fichier lui-même devrait être uploadé séparément
391
+ timestamp: fileData.timestamp instanceof Date ? fileData.timestamp.toISOString() : fileData.timestamp,
392
+ source: fileData.source
393
+ };
394
+ if (fileData.docType)
395
+ obj.docType = fileData.docType;
396
+ if (fileData.docNumber)
397
+ obj.docNumber = fileData.docNumber;
398
+ return JSON.stringify(obj);
399
+ }
400
+ /**
401
+ * Convertit un objet File en data URL base64
402
+ */
403
+ static fileToBase64(file) {
404
+ return new Promise((resolve, reject) => {
405
+ const reader = new FileReader();
406
+ reader.onload = () => resolve(reader.result);
407
+ reader.onerror = reject;
408
+ reader.readAsDataURL(file);
313
409
  });
314
410
  }
315
411
  static serializeRSU(value) {
@@ -423,9 +519,9 @@ export class VariableValueConverter {
423
519
  case 'NUMERIQUE':
424
520
  return !isNaN(parseFloat(value));
425
521
  case 'DATE':
426
- return !isNaN(new Date(value).getTime());
522
+ return this.parseDate(value) !== null;
427
523
  case 'DATEHEURE':
428
- return !isNaN(new Date(value).getTime());
524
+ return this.parseDateTime(value) !== null;
429
525
  case 'HOUR':
430
526
  return /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/.test(value);
431
527
  case 'PHONE':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsuci/shared-form-components",
3
- "version": "1.0.77",
3
+ "version": "1.0.78",
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",