@ews-admin/global-design-system 1.1.21 → 1.1.23

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/index.js CHANGED
@@ -5,6 +5,60 @@ var React = require('react');
5
5
 
6
6
  function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
7
7
 
8
+ const LOCAL_PORTS = {
9
+ bff: 8082,
10
+ loginBff: 8080,
11
+ app: 3000,
12
+ login: 3001,
13
+ dashboard: 3002,
14
+ admin: 3008,
15
+ };
16
+ function buildUrls(environment) {
17
+ if (environment === "local") {
18
+ const base = "http://local";
19
+ const domain = "medecine360local.com";
20
+ return {
21
+ bffUrl: `${base}.api.${domain}:${LOCAL_PORTS.bff}/bff/api/v1`,
22
+ loginBffUrl: `${base}.api.${domain}:${LOCAL_PORTS.loginBff}/login-bff/api/v1`,
23
+ appUrl: `${base}.app.${domain}:${LOCAL_PORTS.app}`,
24
+ loginUrl: `${base}.login.${domain}:${LOCAL_PORTS.login}`,
25
+ dashboardUrl: `${base}.dashboard.${domain}:${LOCAL_PORTS.dashboard}`,
26
+ adminUrl: `${base}.admin.${domain}:${LOCAL_PORTS.admin}`,
27
+ };
28
+ }
29
+ const prefix = environment === "prod" ? "" : `${environment}.`;
30
+ const domain = "medecine360.com";
31
+ return {
32
+ bffUrl: `https://${prefix}api.${domain}/bff/api/v1`,
33
+ loginBffUrl: `https://${prefix}api.${domain}/login-bff/api/v1`,
34
+ appUrl: `https://${prefix}app.${domain}`,
35
+ loginUrl: `https://${prefix}login.${domain}`,
36
+ dashboardUrl: `https://${prefix}dashboard.${domain}`,
37
+ adminUrl: `https://${prefix}admin.${domain}`,
38
+ };
39
+ }
40
+ /**
41
+ * Creates an environment configuration with all derived URLs.
42
+ *
43
+ * For deployed environments (dev, qa, prod), only the environment name is needed
44
+ * — all URLs follow a predictable convention.
45
+ *
46
+ * For local development, sensible defaults are provided but can be overridden
47
+ * (e.g. to point BFF URL at a mock server).
48
+ *
49
+ * @param env - The environment name (VITE_ENV value)
50
+ * @param overrides - Optional URL overrides (e.g. for local mock servers)
51
+ */
52
+ function createEnvConfig(env, overrides) {
53
+ const environment = (env || "local");
54
+ const derived = buildUrls(environment);
55
+ return {
56
+ env: environment,
57
+ ...derived,
58
+ ...overrides,
59
+ };
60
+ }
61
+
8
62
  /**
9
63
  * Default currency for price formatting
10
64
  */
@@ -86,6 +140,36 @@ function isValidPhoneNumber(value) {
86
140
  const phoneRegex = /^(\+\d{1,17}|\d{1,17})$/;
87
141
  return phoneRegex.test(trimmedValue);
88
142
  }
143
+ /**
144
+ * Blood type enum matching backend BloodTypeEnum values
145
+ */
146
+ exports.BloodType = void 0;
147
+ (function (BloodType) {
148
+ BloodType["A_POSITIVE"] = "A+";
149
+ BloodType["A_NEGATIVE"] = "A-";
150
+ BloodType["B_POSITIVE"] = "B+";
151
+ BloodType["B_NEGATIVE"] = "B-";
152
+ BloodType["AB_POSITIVE"] = "AB+";
153
+ BloodType["AB_NEGATIVE"] = "AB-";
154
+ BloodType["O_POSITIVE"] = "O+";
155
+ BloodType["O_NEGATIVE"] = "O-";
156
+ BloodType["UNKNOWN"] = "UNKNOWN";
157
+ })(exports.BloodType || (exports.BloodType = {}));
158
+ /**
159
+ * Ordered list of all blood type values.
160
+ * Use this to build select/dropdown options.
161
+ */
162
+ const BLOOD_TYPES = [
163
+ exports.BloodType.A_POSITIVE,
164
+ exports.BloodType.A_NEGATIVE,
165
+ exports.BloodType.B_POSITIVE,
166
+ exports.BloodType.B_NEGATIVE,
167
+ exports.BloodType.AB_POSITIVE,
168
+ exports.BloodType.AB_NEGATIVE,
169
+ exports.BloodType.O_POSITIVE,
170
+ exports.BloodType.O_NEGATIVE,
171
+ exports.BloodType.UNKNOWN,
172
+ ];
89
173
 
90
174
  const Button = React.forwardRef(({ className, variant = "ews-primary", size = "md", loading = false, fullWidth = false, leftIcon, rightIcon, children, disabled, ...props }, ref) => {
91
175
  const baseStyles = "inline-flex items-center justify-center font-medium rounded-md transition-colors focus:outline-none disabled:opacity-50 disabled:pointer-events-none";
@@ -229,11 +313,11 @@ const createLucideIcon = (iconName, iconNode) => {
229
313
  */
230
314
 
231
315
 
232
- const __iconNode$c = [
316
+ const __iconNode$e = [
233
317
  ["path", { d: "M5 12h14", key: "1ays0h" }],
234
318
  ["path", { d: "m12 5 7 7-7 7", key: "xquz4c" }]
235
319
  ];
236
- const ArrowRight = createLucideIcon("arrow-right", __iconNode$c);
320
+ const ArrowRight = createLucideIcon("arrow-right", __iconNode$e);
237
321
 
238
322
  /**
239
323
  * @license lucide-react v0.544.0 - ISC
@@ -243,8 +327,8 @@ const ArrowRight = createLucideIcon("arrow-right", __iconNode$c);
243
327
  */
244
328
 
245
329
 
246
- const __iconNode$b = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
247
- const Check = createLucideIcon("check", __iconNode$b);
330
+ const __iconNode$d = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
331
+ const Check = createLucideIcon("check", __iconNode$d);
248
332
 
249
333
  /**
250
334
  * @license lucide-react v0.544.0 - ISC
@@ -254,8 +338,8 @@ const Check = createLucideIcon("check", __iconNode$b);
254
338
  */
255
339
 
256
340
 
257
- const __iconNode$a = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
258
- const ChevronDown = createLucideIcon("chevron-down", __iconNode$a);
341
+ const __iconNode$c = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
342
+ const ChevronDown = createLucideIcon("chevron-down", __iconNode$c);
259
343
 
260
344
  /**
261
345
  * @license lucide-react v0.544.0 - ISC
@@ -265,12 +349,12 @@ const ChevronDown = createLucideIcon("chevron-down", __iconNode$a);
265
349
  */
266
350
 
267
351
 
268
- const __iconNode$9 = [
352
+ const __iconNode$b = [
269
353
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
270
354
  ["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
271
355
  ["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
272
356
  ];
273
- const CircleAlert = createLucideIcon("circle-alert", __iconNode$9);
357
+ const CircleAlert = createLucideIcon("circle-alert", __iconNode$b);
274
358
 
275
359
  /**
276
360
  * @license lucide-react v0.544.0 - ISC
@@ -280,11 +364,11 @@ const CircleAlert = createLucideIcon("circle-alert", __iconNode$9);
280
364
  */
281
365
 
282
366
 
283
- const __iconNode$8 = [
367
+ const __iconNode$a = [
284
368
  ["path", { d: "M21.801 10A10 10 0 1 1 17 3.335", key: "yps3ct" }],
285
369
  ["path", { d: "m9 11 3 3L22 4", key: "1pflzl" }]
286
370
  ];
287
- const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$8);
371
+ const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$a);
288
372
 
289
373
  /**
290
374
  * @license lucide-react v0.544.0 - ISC
@@ -294,7 +378,7 @@ const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$8);
294
378
  */
295
379
 
296
380
 
297
- const __iconNode$7 = [
381
+ const __iconNode$9 = [
298
382
  [
299
383
  "path",
300
384
  {
@@ -312,7 +396,7 @@ const __iconNode$7 = [
312
396
  ],
313
397
  ["path", { d: "m2 2 20 20", key: "1ooewy" }]
314
398
  ];
315
- const EyeOff = createLucideIcon("eye-off", __iconNode$7);
399
+ const EyeOff = createLucideIcon("eye-off", __iconNode$9);
316
400
 
317
401
  /**
318
402
  * @license lucide-react v0.544.0 - ISC
@@ -322,7 +406,7 @@ const EyeOff = createLucideIcon("eye-off", __iconNode$7);
322
406
  */
323
407
 
324
408
 
325
- const __iconNode$6 = [
409
+ const __iconNode$8 = [
326
410
  [
327
411
  "path",
328
412
  {
@@ -332,7 +416,7 @@ const __iconNode$6 = [
332
416
  ],
333
417
  ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
334
418
  ];
335
- const Eye = createLucideIcon("eye", __iconNode$6);
419
+ const Eye = createLucideIcon("eye", __iconNode$8);
336
420
 
337
421
  /**
338
422
  * @license lucide-react v0.544.0 - ISC
@@ -342,7 +426,7 @@ const Eye = createLucideIcon("eye", __iconNode$6);
342
426
  */
343
427
 
344
428
 
345
- const __iconNode$5 = [
429
+ const __iconNode$7 = [
346
430
  [
347
431
  "path",
348
432
  {
@@ -351,7 +435,7 @@ const __iconNode$5 = [
351
435
  }
352
436
  ]
353
437
  ];
354
- const Heart = createLucideIcon("heart", __iconNode$5);
438
+ const Heart = createLucideIcon("heart", __iconNode$7);
355
439
 
356
440
  /**
357
441
  * @license lucide-react v0.544.0 - ISC
@@ -361,11 +445,31 @@ const Heart = createLucideIcon("heart", __iconNode$5);
361
445
  */
362
446
 
363
447
 
364
- const __iconNode$4 = [
448
+ const __iconNode$6 = [
449
+ [
450
+ "path",
451
+ {
452
+ d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
453
+ key: "1a8usu"
454
+ }
455
+ ],
456
+ ["path", { d: "m15 5 4 4", key: "1mk7zo" }]
457
+ ];
458
+ const Pencil = createLucideIcon("pencil", __iconNode$6);
459
+
460
+ /**
461
+ * @license lucide-react v0.544.0 - ISC
462
+ *
463
+ * This source code is licensed under the ISC license.
464
+ * See the LICENSE file in the root directory of this source tree.
465
+ */
466
+
467
+
468
+ const __iconNode$5 = [
365
469
  ["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
366
470
  ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
367
471
  ];
368
- const Search = createLucideIcon("search", __iconNode$4);
472
+ const Search = createLucideIcon("search", __iconNode$5);
369
473
 
370
474
  /**
371
475
  * @license lucide-react v0.544.0 - ISC
@@ -375,14 +479,29 @@ const Search = createLucideIcon("search", __iconNode$4);
375
479
  */
376
480
 
377
481
 
378
- const __iconNode$3 = [
482
+ const __iconNode$4 = [
379
483
  ["path", { d: "M11 2v2", key: "1539x4" }],
380
484
  ["path", { d: "M5 2v2", key: "1yf1q8" }],
381
485
  ["path", { d: "M5 3H4a2 2 0 0 0-2 2v4a6 6 0 0 0 12 0V5a2 2 0 0 0-2-2h-1", key: "rb5t3r" }],
382
486
  ["path", { d: "M8 15a6 6 0 0 0 12 0v-3", key: "x18d4x" }],
383
487
  ["circle", { cx: "20", cy: "10", r: "2", key: "ts1r5v" }]
384
488
  ];
385
- const Stethoscope = createLucideIcon("stethoscope", __iconNode$3);
489
+ const Stethoscope = createLucideIcon("stethoscope", __iconNode$4);
490
+
491
+ /**
492
+ * @license lucide-react v0.544.0 - ISC
493
+ *
494
+ * This source code is licensed under the ISC license.
495
+ * See the LICENSE file in the root directory of this source tree.
496
+ */
497
+
498
+
499
+ const __iconNode$3 = [
500
+ ["path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6", key: "miytrc" }],
501
+ ["path", { d: "M3 6h18", key: "d0wm0j" }],
502
+ ["path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2", key: "e791ji" }]
503
+ ];
504
+ const Trash = createLucideIcon("trash", __iconNode$3);
386
505
 
387
506
  /**
388
507
  * @license lucide-react v0.544.0 - ISC
@@ -457,7 +576,7 @@ const UserIcon = ({ size = 24, color = "currentColor", className = "", ...props
457
576
  return jsxRuntime.jsx(User, { size: size, color: color, className: className, ...props });
458
577
  };
459
578
 
460
- const Input = React.forwardRef(({ className, variant = "default", size = "md", label, helperText, error, leftIcon, rightIcon, fullWidth = false, showPasswordToggle = false, required = false, countryCodeSelect, id, type = "text", ...props }, ref) => {
579
+ const Input = React.forwardRef(({ className, variant = "default", size = "md", label, helperText, error, leftIcon, rightIcon, fullWidth = false, showPasswordToggle = false, required = false, countryCodeSelect, leftAddon, rightAddon, id, type = "text", ...props }, ref) => {
461
580
  const inputId = id || `input-${Math.random().toString(36).substr(2, 9)}`;
462
581
  const hasError = Boolean(error);
463
582
  const actualVariant = hasError ? "error" : variant;
@@ -516,7 +635,7 @@ const Input = React.forwardRef(({ className, variant = "default", size = "md", l
516
635
  countryCodeSelect.onChange(item.code);
517
636
  setIsDropdownOpen(false);
518
637
  }, className: cn("px-3 py-2 text-sm cursor-pointer transition-colors", isSelected && "bg-ews-primary text-white", !isSelected && "hover:bg-ews-gray-50"), children: [jsxRuntime.jsx("span", { className: "font-medium", children: item.code }), item.country && (jsxRuntime.jsx("span", { className: cn("ml-2 text-xs", isSelected ? "text-white/80" : "text-ews-gray-500"), children: item.country }))] }, item.code));
519
- }) }))] }) })), leftIcon && !countryCodeSelect && (jsxRuntime.jsx("div", { className: "flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none", children: jsxRuntime.jsx("span", { className: cn("text-ews-gray-400", iconSizes[size]), children: leftIcon }) })), jsxRuntime.jsx("input", { id: inputId, type: actualType, className: cn(baseStyles, variants[actualVariant], sizes[size], countryCodeSelect && "pl-24", leftIcon && !countryCodeSelect && "pl-10", (rightIcon || shouldShowPasswordToggle) && "pr-10", className), ref: ref, ...props }), rightIcon && !shouldShowPasswordToggle && (jsxRuntime.jsx("div", { className: "flex absolute inset-y-0 right-0 items-center pr-3 pointer-events-none", children: jsxRuntime.jsx("span", { className: cn("text-ews-gray-400", iconSizes[size]), children: rightIcon }) })), shouldShowPasswordToggle && (jsxRuntime.jsx("button", { type: "button", className: "flex absolute inset-y-0 right-0 items-center pr-3", onClick: () => setShowPassword(!showPassword), tabIndex: -1, children: jsxRuntime.jsx("span", { className: cn("transition-colors text-ews-gray-400 hover:text-ews-gray-600", iconSizes[size]), children: showPassword ? jsxRuntime.jsx(EyeOff, { size: 16 }) : jsxRuntime.jsx(Eye, { size: 16 }) }) }))] }), (error || helperText) && (jsxRuntime.jsx("p", { className: cn("text-sm", error ? "text-ews-error" : "text-ews-gray-500"), children: error || helperText }))] }));
638
+ }) }))] }) })), leftIcon && !countryCodeSelect && !leftAddon && (jsxRuntime.jsx("div", { className: "flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none", children: jsxRuntime.jsx("span", { className: cn("text-ews-gray-400", iconSizes[size]), children: leftIcon }) })), leftAddon && !countryCodeSelect && !leftIcon && (jsxRuntime.jsx("div", { className: "flex absolute inset-y-0 left-0 items-center pointer-events-none overflow-hidden rounded-l-md", children: jsxRuntime.jsx("span", { className: "flex items-center h-full px-3 text-sm font-medium whitespace-nowrap bg-ews-gray-50 border-r border-ews-gray-300 text-ews-gray-600", children: leftAddon }) })), jsxRuntime.jsx("input", { id: inputId, type: actualType, className: cn(baseStyles, variants[actualVariant], sizes[size], countryCodeSelect && "pl-24", leftIcon && !countryCodeSelect && !leftAddon && "pl-10", leftAddon && !countryCodeSelect && !leftIcon && "pl-16", (rightIcon || shouldShowPasswordToggle) && "pr-10", rightAddon && !rightIcon && !shouldShowPasswordToggle && "pr-16", className), ref: ref, ...props }), rightAddon && !rightIcon && !shouldShowPasswordToggle && (jsxRuntime.jsx("div", { className: "flex absolute inset-y-0 right-0 items-center pr-3 pointer-events-none", children: jsxRuntime.jsx("span", { className: "text-sm font-medium text-ews-gray-500", children: rightAddon }) })), rightIcon && !shouldShowPasswordToggle && (jsxRuntime.jsx("div", { className: "flex absolute inset-y-0 right-0 items-center pr-3 pointer-events-none", children: jsxRuntime.jsx("span", { className: cn("text-ews-gray-400", iconSizes[size]), children: rightIcon }) })), shouldShowPasswordToggle && (jsxRuntime.jsx("button", { type: "button", className: "flex absolute inset-y-0 right-0 items-center pr-3", onClick: () => setShowPassword(!showPassword), tabIndex: -1, children: jsxRuntime.jsx("span", { className: cn("transition-colors text-ews-gray-400 hover:text-ews-gray-600", iconSizes[size]), children: showPassword ? jsxRuntime.jsx(EyeOff, { size: 16 }) : jsxRuntime.jsx(Eye, { size: 16 }) }) }))] }), (error || helperText) && (jsxRuntime.jsx("p", { className: cn("text-sm", error ? "text-ews-error" : "text-ews-gray-500"), children: error || helperText }))] }));
520
639
  });
521
640
  Input.displayName = "Input";
522
641
 
@@ -1712,6 +1831,65 @@ const Modal = ({ isOpen, onClose, title, children, variant = "info", size = "md"
1712
1831
  return (jsxRuntime.jsxs("div", { className: "flex fixed inset-0 z-50 justify-center items-center", children: [jsxRuntime.jsx("div", { className: "absolute inset-0 backdrop-blur-sm bg-black/50", onClick: handleOverlayClick }), jsxRuntime.jsxs("div", { className: cn("relative w-full bg-white rounded-lg shadow-xl transition-all transform", "duration-200 animate-in fade-in-0 zoom-in-95", getSizeClasses(), "mx-4", className), role: "dialog", "aria-modal": "true", "aria-labelledby": "modal-title", children: [jsxRuntime.jsxs("div", { className: cn("flex items-center justify-between p-6 border-b", variantStyles.borderColor), children: [jsxRuntime.jsxs("div", { className: "flex items-center space-x-3", children: [jsxRuntime.jsx("div", { className: cn("p-2 rounded-full", variantStyles.iconBg), children: variantStyles.icon }), jsxRuntime.jsx("h2", { id: "modal-title", className: cn("text-lg font-semibold", variantStyles.titleColor), children: title })] }), jsxRuntime.jsx("button", { onClick: onClose, className: "p-1 text-gray-400 transition-colors hover:text-gray-600", "aria-label": "Close modal", children: jsxRuntime.jsx(X, { className: "w-5 h-5" }) })] }), jsxRuntime.jsx("div", { className: cn("p-6", contentClassName), children: jsxRuntime.jsx("div", { className: "leading-relaxed text-gray-700", children: error && variant === "error" ? (jsxRuntime.jsxs("div", { className: "space-y-3", children: [jsxRuntime.jsx("p", { children: error.message }), error.fields && error.fields.length > 0 && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("p", { className: "font-semibold text-gray-900", children: "Erreurs de champ:" }), jsxRuntime.jsx("ul", { className: "mt-2 space-y-1", children: error.fields.map((field, index) => (jsxRuntime.jsxs("li", { className: "text-ews-error", children: ["\u2022 ", field.path, ": ", field.message] }, index))) })] }))] })) : (children) }) }), (primaryAction || secondaryAction) && (jsxRuntime.jsxs("div", { className: "flex justify-end items-center p-6 pt-0 space-x-3", children: [secondaryAction && (jsxRuntime.jsx(Button, { variant: "outline", onClick: onSecondaryAction || onClose, disabled: isLoading, children: secondaryAction })), primaryAction && (jsxRuntime.jsx(Button, { variant: variant === "error" ? "error" : "ews-primary", onClick: onPrimaryAction, loading: isLoading, children: primaryAction }))] }))] })] }));
1713
1832
  };
1714
1833
 
1834
+ const SIZE_CLASSES = {
1835
+ sm: "h-10 w-10",
1836
+ md: "h-16 w-16",
1837
+ lg: "h-32 w-32",
1838
+ };
1839
+ const INDICATOR_CLASSES = {
1840
+ sm: "h-2 w-2",
1841
+ md: "h-4 w-4",
1842
+ lg: "h-8 w-8",
1843
+ };
1844
+ function UploadProgressBar({ progress, isLoading, }) {
1845
+ const [visible, setVisible] = React.useState(false);
1846
+ React.useEffect(() => {
1847
+ if (isLoading) {
1848
+ setVisible(true);
1849
+ return;
1850
+ }
1851
+ // isLoading just turned false
1852
+ if (progress >= 100) {
1853
+ setVisible(true);
1854
+ const t = setTimeout(() => setVisible(false), 1000);
1855
+ return () => clearTimeout(t);
1856
+ }
1857
+ // Legacy path (no isLoading prop): driven purely by progress
1858
+ if (progress > 0 && progress < 100) {
1859
+ setVisible(true);
1860
+ return;
1861
+ }
1862
+ setVisible(false);
1863
+ }, [progress, isLoading]);
1864
+ if (!visible)
1865
+ return null;
1866
+ const indeterminate = isLoading && progress === 0;
1867
+ return (jsxRuntime.jsx("div", { className: "mt-2 h-1.5 w-32 rounded-full bg-gray-200", children: jsxRuntime.jsx("div", { className: cn("h-full rounded-full bg-ews-primary transition-all duration-300", indeterminate && "animate-pulse"), style: { width: indeterminate ? "35%" : `${progress}%` } }) }));
1868
+ }
1869
+ const ProfileImageUpload = ({ imageUrl, altText, readOnly = false, size = "lg", uploadProgress = 0, isLoading = false, showDeleteButton = true, accept = "image/*", maxFileSizeMB = 3, onFileSelect, onFileSizeExceeded, onDeleteConfirm, deleteConfirmTitle, deleteConfirmMessage, deleteConfirmLabel, cancelLabel, }) => {
1870
+ const fileInputRef = React.useRef(null);
1871
+ const [isHovered, setIsHovered] = React.useState(false);
1872
+ const [showConfirm, setShowConfirm] = React.useState(false);
1873
+ const handleEditClick = () => fileInputRef.current?.click();
1874
+ const handleFileChange = (e) => {
1875
+ const file = e.target.files?.[0];
1876
+ if (!file)
1877
+ return;
1878
+ if (file.size > maxFileSizeMB * 1024 * 1024) {
1879
+ onFileSizeExceeded?.();
1880
+ e.target.value = "";
1881
+ return;
1882
+ }
1883
+ onFileSelect(file);
1884
+ e.target.value = ""; // allow re-selecting the same file
1885
+ };
1886
+ const handleConfirmDelete = () => {
1887
+ setShowConfirm(false);
1888
+ onDeleteConfirm();
1889
+ };
1890
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "inline-flex flex-col items-center", children: [jsxRuntime.jsxs("div", { className: "relative", onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [jsxRuntime.jsx("img", { src: imageUrl, alt: altText, className: cn("rounded-full border-4 border-white object-cover", SIZE_CLASSES[size]) }), jsxRuntime.jsx("div", { className: cn("absolute bottom-0 right-0 rounded-full border-4 border-white bg-green-400", INDICATOR_CLASSES[size]) }), jsxRuntime.jsx("input", { type: "file", ref: fileInputRef, accept: accept, className: "hidden", onChange: handleFileChange }), isHovered && !readOnly && !isLoading && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { type: "button", onClick: handleEditClick, className: "absolute left-0 top-0 flex h-8 w-8 cursor-pointer items-center justify-center rounded-full bg-ews-primary transition-colors hover:bg-ews-secondary", "aria-label": "Edit profile image", children: jsxRuntime.jsx(Pencil, { className: "h-4 w-4 text-white" }) }), showDeleteButton && (jsxRuntime.jsx("button", { type: "button", onClick: () => setShowConfirm(true), className: "absolute left-0 top-10 flex h-8 w-8 cursor-pointer items-center justify-center rounded-full bg-red-600 transition-colors hover:bg-red-700", "aria-label": "Delete profile image", children: jsxRuntime.jsx(Trash, { className: "h-4 w-4 text-white" }) }))] }))] }), jsxRuntime.jsx(UploadProgressBar, { progress: uploadProgress, isLoading: isLoading })] }), jsxRuntime.jsx(Modal, { isOpen: showConfirm, onClose: () => setShowConfirm(false), title: deleteConfirmTitle, variant: "error", size: "sm", primaryAction: deleteConfirmLabel, secondaryAction: cancelLabel, onPrimaryAction: handleConfirmDelete, onSecondaryAction: () => setShowConfirm(false), children: jsxRuntime.jsx("p", { children: deleteConfirmMessage }) })] }));
1891
+ };
1892
+
1715
1893
  const DropdownMultiSelect = ({ options, name, control, placeholder = "Select options", searchPlaceholder = "Search...", onChange, value: controlledValue, defaultValue, onValidate, disabled = false, error, label, className, }) => {
1716
1894
  const [isOpen, setIsOpen] = React.useState(false);
1717
1895
  const [searchTerm, setSearchTerm] = React.useState("");
@@ -1999,6 +2177,7 @@ const SpecialtySearchAutocomplete = ({ selectedSpecialties = [], onSpecialtiesCh
1999
2177
  };
2000
2178
 
2001
2179
  exports.ArrowRight = ArrowRight;
2180
+ exports.BLOOD_TYPES = BLOOD_TYPES;
2002
2181
  exports.Button = Button;
2003
2182
  exports.Check = Check;
2004
2183
  exports.DoctorIcon = DoctorIcon;
@@ -2009,6 +2188,7 @@ exports.Logo = Logo;
2009
2188
  exports.Modal = Modal;
2010
2189
  exports.MultiSearchAutocomplete = MultiSearchAutocomplete;
2011
2190
  exports.PatientIcon = PatientIcon;
2191
+ exports.ProfileImageUpload = ProfileImageUpload;
2012
2192
  exports.Search = Search;
2013
2193
  exports.SearchAutocomplete = SearchAutocomplete;
2014
2194
  exports.Select = Select;
@@ -2018,6 +2198,7 @@ exports.ThemeProvider = ThemeProvider;
2018
2198
  exports.ThemeToggle = ThemeToggle;
2019
2199
  exports.UserIcon = UserIcon;
2020
2200
  exports.cn = cn;
2201
+ exports.createEnvConfig = createEnvConfig;
2021
2202
  exports.debounce = debounce;
2022
2203
  exports.formatCurrency = formatCurrency;
2023
2204
  exports.formatDate = formatDate;