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