@almadar/ui 5.161.0 → 5.163.0

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.
@@ -35416,6 +35416,33 @@ var init_Lightbox = __esm({
35416
35416
  Lightbox.displayName = "Lightbox";
35417
35417
  }
35418
35418
  });
35419
+
35420
+ // lib/relationLabel.ts
35421
+ function relationLabel(value) {
35422
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date)
35423
+ return null;
35424
+ for (const key of ["name", "title", "label"]) {
35425
+ const candidate = value[key];
35426
+ if (typeof candidate === "string" && candidate !== "") return candidate;
35427
+ }
35428
+ const id = value.id;
35429
+ return id !== void 0 && id !== null ? String(id) : null;
35430
+ }
35431
+ function relationDisplayLabels(value, options) {
35432
+ if (value === void 0 || value === null || value === "") return [];
35433
+ if (Array.isArray(value)) {
35434
+ return value.flatMap((item) => relationDisplayLabels(item, options));
35435
+ }
35436
+ const hydrated = relationLabel(value);
35437
+ if (hydrated !== null) return [hydrated];
35438
+ const raw = String(value);
35439
+ const match = options?.find((opt) => opt.value === raw);
35440
+ return [match ? match.label : raw];
35441
+ }
35442
+ var init_relationLabel = __esm({
35443
+ "lib/relationLabel.ts"() {
35444
+ }
35445
+ });
35419
35446
  function renderIconInput3(icon, props) {
35420
35447
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
35421
35448
  }
@@ -35733,6 +35760,7 @@ var init_TableView = __esm({
35733
35760
  "use client";
35734
35761
  init_cn();
35735
35762
  init_format();
35763
+ init_relationLabel();
35736
35764
  init_getNestedValue();
35737
35765
  init_useEventBus();
35738
35766
  init_Box();
@@ -35746,7 +35774,13 @@ var init_TableView = __esm({
35746
35774
  init_Menu();
35747
35775
  init_useDataDnd();
35748
35776
  tableViewLog = logger.createLogger("almadar:ui:table-view");
35749
- formatCell = (value, format) => formatValue(value, format);
35777
+ formatCell = (value, format) => {
35778
+ if (value !== null && value !== void 0 && typeof value === "object" && !(value instanceof Date)) {
35779
+ const labels = relationDisplayLabels(value);
35780
+ if (labels.length > 0) return labels.join(", ");
35781
+ }
35782
+ return formatValue(value, format);
35783
+ };
35750
35784
  MAX_MEASURED_COL_CH = 32;
35751
35785
  BADGE_CHROME_CH = 4;
35752
35786
  alignClass = {
@@ -39361,6 +39395,13 @@ var init_RichTextEditor = __esm({
39361
39395
  document.execCommand("createLink", false, safe);
39362
39396
  afterEdit();
39363
39397
  }, [afterEdit, t, toolbar.link]);
39398
+ const execImage = React94.useCallback(() => {
39399
+ const url = window.prompt(t("richTextEditor.imagePrompt"));
39400
+ if (!url) return;
39401
+ const safe = safeUrl(url, ["http://", "https://", "data:image/"]) ?? `https://${url}`;
39402
+ document.execCommand("insertImage", false, safe);
39403
+ afterEdit();
39404
+ }, [afterEdit, t]);
39364
39405
  const execRule = React94.useCallback(() => {
39365
39406
  document.execCommand("insertHorizontalRule", false);
39366
39407
  afterEdit();
@@ -39398,6 +39439,7 @@ var init_RichTextEditor = __esm({
39398
39439
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Quote, label: t("richTextEditor.quote"), active: toolbar.block === "blockquote", onExec: () => execBlock("blockquote") }),
39399
39440
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Code, label: t("richTextEditor.code"), active: toolbar.block === "pre", onExec: () => execBlock("pre") }),
39400
39441
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Link, label: toolbar.link ? t("richTextEditor.removeLink") : t("richTextEditor.link"), active: toolbar.link, onExec: execLink }),
39442
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Image, label: t("richTextEditor.image"), onExec: execImage }),
39401
39443
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Minus, label: t("richTextEditor.divider"), onExec: execRule })
39402
39444
  ]
39403
39445
  }
@@ -39435,9 +39477,11 @@ function renderIconInput4(icon, props) {
39435
39477
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
39436
39478
  }
39437
39479
  function DocumentPanel({
39438
- entity,
39480
+ id,
39439
39481
  title,
39440
39482
  subtitle,
39483
+ coverImage,
39484
+ icon,
39441
39485
  value,
39442
39486
  actions,
39443
39487
  maxInlineActions,
@@ -39455,14 +39499,14 @@ function DocumentPanel({
39455
39499
  const eventBus = useEventBus();
39456
39500
  const { t } = hooks.useTranslate();
39457
39501
  const [titleDraft, setTitleDraft] = React94.useState(null);
39458
- const recordId = entity?.id !== void 0 && entity?.id !== null ? String(entity.id) : "";
39502
+ const recordId = id ?? "";
39459
39503
  const actionDefs = actions ?? [];
39460
39504
  const inlineCap = Math.min(maxInlineActions ?? 1, 1);
39461
39505
  const inlineActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
39462
39506
  const menuActions = actionDefs.filter((a) => !inlineActions.includes(a));
39463
39507
  const fireAction = (action) => {
39464
39508
  if (!action.event) return;
39465
- eventBus.emit(`UI:${action.event}`, { id: recordId, row: entity });
39509
+ eventBus.emit(`UI:${action.event}`, { id: recordId, title: title ?? "" });
39466
39510
  };
39467
39511
  const commitTitle = () => {
39468
39512
  if (!titleCommitEvent) return;
@@ -39513,62 +39557,74 @@ function DocumentPanel({
39513
39557
  const emitWithId = (event) => () => {
39514
39558
  if (event) eventBus.emit(`UI:${event}`, { id: recordId });
39515
39559
  };
39516
- return /* @__PURE__ */ jsxRuntime.jsx(Card, { variant: "elevated", className: cn("w-full", className), children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", className: "p-6 sm:p-8", children: [
39517
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
39518
- /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
39519
- titleNode,
39520
- subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: subtitle }) : null
39521
- ] }),
39522
- /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-shrink-0 pt-1 items-center", children: editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
39523
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", className: "hidden sm:block", children: autosaveHint ?? (t("documentPanel.autosaveHint") || "") }),
39524
- /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: emitWithId(doneEvent), "data-testid": "document-done", children: doneLabel ?? (t("documentPanel.done") || "Done") })
39525
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
39526
- editEvent && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: emitWithId(editEvent), "data-testid": "document-edit", children: [
39527
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "edit", size: "xs", className: "mr-1" }),
39528
- editLabel ?? (t("documentPanel.edit") || "Edit")
39529
- ] }),
39530
- inlineActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
39531
- Button,
39532
- {
39533
- variant: "primary",
39534
- size: "sm",
39535
- onClick: () => fireAction(action),
39536
- "data-testid": `action-${action.event}`,
39537
- children: [
39538
- action.icon && renderIconInput4(action.icon, { size: "xs", className: "mr-1" }),
39539
- action.label
39540
- ]
39541
- },
39542
- idx
39543
- )),
39544
- menuActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
39545
- Menu,
39546
- {
39547
- position: "bottom-end",
39548
- trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
39549
- items: menuActions.map((action) => ({
39550
- label: action.label,
39551
- icon: action.icon,
39552
- variant: action.variant === "danger" ? "danger" : "default",
39553
- onClick: () => fireAction(action)
39554
- }))
39555
- }
39556
- )
39557
- ] }) })
39558
- ] }),
39559
- editing ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, changeEvent: contentChangeEvent, placeholder }) : /* @__PURE__ */ jsxRuntime.jsx(
39560
- Box,
39560
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { variant: "elevated", className: cn("w-full overflow-hidden", className), children: [
39561
+ coverImage ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "h-44 w-full sm:h-52", "data-testid": "document-cover", children: /* @__PURE__ */ jsxRuntime.jsx(
39562
+ "img",
39561
39563
  {
39562
- onClick: emitWithId(editEvent),
39563
- className: cn(
39564
- "rounded-md px-1 -mx-1 min-h-[16rem]",
39565
- editEvent && "cursor-text transition-colors hover:bg-muted/30"
39566
- ),
39567
- "data-testid": "document-body",
39568
- children: value && value !== "" ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, readOnly: true }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "muted", children: placeholder ?? (t("documentPanel.placeholder") || "") })
39569
- }
39570
- )
39571
- ] }) });
39564
+ src: coverImage,
39565
+ alt: "",
39566
+ "aria-hidden": "true",
39567
+ className: "h-full w-full object-cover"
39568
+ }
39569
+ ) }) : null,
39570
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", className: "p-6 sm:p-8", children: [
39571
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
39572
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
39573
+ icon ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-muted-foreground", "data-testid": "document-icon", children: renderIconInput4(icon, { size: "lg" }) }) : null,
39574
+ titleNode,
39575
+ subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: subtitle }) : null
39576
+ ] }),
39577
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-shrink-0 pt-1 items-center", children: editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
39578
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", className: "hidden sm:block", children: autosaveHint ?? (t("documentPanel.autosaveHint") || "") }),
39579
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: emitWithId(doneEvent), "data-testid": "document-done", children: doneLabel ?? (t("documentPanel.done") || "Done") })
39580
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
39581
+ editEvent && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: emitWithId(editEvent), "data-testid": "document-edit", children: [
39582
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "edit", size: "xs", className: "mr-1" }),
39583
+ editLabel ?? (t("documentPanel.edit") || "Edit")
39584
+ ] }),
39585
+ inlineActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
39586
+ Button,
39587
+ {
39588
+ variant: "primary",
39589
+ size: "sm",
39590
+ onClick: () => fireAction(action),
39591
+ "data-testid": `action-${action.event}`,
39592
+ children: [
39593
+ action.icon && renderIconInput4(action.icon, { size: "xs", className: "mr-1" }),
39594
+ action.label
39595
+ ]
39596
+ },
39597
+ idx
39598
+ )),
39599
+ menuActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
39600
+ Menu,
39601
+ {
39602
+ position: "bottom-end",
39603
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
39604
+ items: menuActions.map((action) => ({
39605
+ label: action.label,
39606
+ icon: action.icon,
39607
+ variant: action.variant === "danger" ? "danger" : "default",
39608
+ onClick: () => fireAction(action)
39609
+ }))
39610
+ }
39611
+ )
39612
+ ] }) })
39613
+ ] }),
39614
+ editing ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, changeEvent: contentChangeEvent, placeholder }) : /* @__PURE__ */ jsxRuntime.jsx(
39615
+ Box,
39616
+ {
39617
+ onClick: emitWithId(editEvent),
39618
+ className: cn(
39619
+ "rounded-md px-1 -mx-1 min-h-[16rem]",
39620
+ editEvent && "cursor-text transition-colors hover:bg-muted/30"
39621
+ ),
39622
+ "data-testid": "document-body",
39623
+ children: value && value !== "" ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, readOnly: true }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "muted", children: placeholder ?? (t("documentPanel.placeholder") || "") })
39624
+ }
39625
+ )
39626
+ ] })
39627
+ ] });
39572
39628
  }
39573
39629
  var init_DocumentPanel = __esm({
39574
39630
  "components/core/molecules/DocumentPanel.tsx"() {
@@ -39592,17 +39648,16 @@ function renderIconInput5(icon, props) {
39592
39648
  function fieldName(field) {
39593
39649
  return field.name ?? field.key ?? "";
39594
39650
  }
39595
- function relationLabel(value) {
39596
- if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) return null;
39597
- for (const key of ["name", "title", "label"]) {
39598
- const candidate = value[key];
39599
- if (typeof candidate === "string" && candidate !== "") return candidate;
39651
+ function relationId(value) {
39652
+ if (value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date)) {
39653
+ const id = value.id;
39654
+ if (id !== void 0 && id !== null) return String(id);
39600
39655
  }
39601
- const id = value.id;
39602
- return id !== void 0 && id !== null ? String(id) : null;
39656
+ return String(value);
39603
39657
  }
39604
39658
  function fieldKind(field, value) {
39605
39659
  if (field.kind) return field.kind;
39660
+ if (field.type === "image") return "image";
39606
39661
  if (field.options && field.options.length > 0) return "select";
39607
39662
  if (typeof value === "boolean" || field.format === "boolean") return "boolean";
39608
39663
  if (Array.isArray(value) || relationLabel(value ?? null) !== null) return "readonly";
@@ -39612,6 +39667,7 @@ function DocumentDetails({
39612
39667
  entity,
39613
39668
  fields,
39614
39669
  metaCommitEvent,
39670
+ relationEvent,
39615
39671
  title,
39616
39672
  className
39617
39673
  }) {
@@ -39626,11 +39682,51 @@ function DocumentDetails({
39626
39682
  if (!metaCommitEvent || next === current) return;
39627
39683
  eventBus.emit(`UI:${metaCommitEvent}`, { id: recordId, patch: { id: recordId, [name]: next } });
39628
39684
  };
39685
+ const relationChip = (item, name, key) => {
39686
+ const label = relationLabel(item) ?? humanizeEnumValue(String(item));
39687
+ if (!relationEvent) {
39688
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", children: label }, key);
39689
+ }
39690
+ const emitClick = () => eventBus.emit(`UI:${relationEvent}`, { field: name, id: relationId(item), name: label });
39691
+ return /* @__PURE__ */ jsxRuntime.jsx(
39692
+ Badge,
39693
+ {
39694
+ variant: "default",
39695
+ role: "button",
39696
+ tabIndex: 0,
39697
+ className: "cursor-pointer transition-colors hover:bg-accent",
39698
+ onClick: emitClick,
39699
+ onKeyDown: (e) => {
39700
+ if (e.key === "Enter" || e.key === " ") {
39701
+ e.preventDefault();
39702
+ emitClick();
39703
+ }
39704
+ },
39705
+ "data-testid": `relation-chip-${name}`,
39706
+ children: label
39707
+ },
39708
+ key
39709
+ );
39710
+ };
39629
39711
  const renderValue = (field) => {
39630
39712
  const name = fieldName(field);
39631
39713
  const raw = getNestedValue(entity ?? {}, name);
39632
39714
  const kind = fieldKind(field, raw);
39633
39715
  const label = field.label ?? field.header ?? humanizeFieldName(name);
39716
+ if (kind === "image") {
39717
+ const url = typeof raw === "string" ? raw : "";
39718
+ if (!url) return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: "\u2014" });
39719
+ return /* @__PURE__ */ jsxRuntime.jsx(
39720
+ "img",
39721
+ {
39722
+ src: url,
39723
+ alt: label,
39724
+ loading: "lazy",
39725
+ className: "max-h-32 w-full rounded-md border border-border object-cover",
39726
+ "data-testid": `document-property-image-${name}`
39727
+ }
39728
+ );
39729
+ }
39634
39730
  if (kind === "boolean") {
39635
39731
  return /* @__PURE__ */ jsxRuntime.jsx(
39636
39732
  Switch,
@@ -39660,9 +39756,12 @@ function DocumentDetails({
39660
39756
  if (raw.length === 0) {
39661
39757
  return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: "\u2014" });
39662
39758
  }
39663
- return /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-wrap", children: raw.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", children: relationLabel(item) ?? humanizeEnumValue(String(item)) }, i)) });
39759
+ return /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-wrap", children: raw.map((item, i) => relationChip(item, name, i)) });
39760
+ }
39761
+ if (raw !== void 0 && raw !== null && raw !== "" && relationLabel(raw) !== null) {
39762
+ return relationChip(raw, name);
39664
39763
  }
39665
- const shown2 = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
39764
+ const shown2 = raw === void 0 || raw === null || raw === "" ? "\u2014" : formatValue(raw, field.format);
39666
39765
  return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "break-words", children: shown2 });
39667
39766
  }
39668
39767
  if (fieldDraft?.name === name) {
@@ -39731,6 +39830,7 @@ var init_DocumentDetails = __esm({
39731
39830
  init_cn();
39732
39831
  init_format();
39733
39832
  init_getNestedValue();
39833
+ init_relationLabel();
39734
39834
  init_useEventBus();
39735
39835
  init_Box();
39736
39836
  init_Stack();
@@ -44316,7 +44416,18 @@ function renderRichFieldValue(value, fieldName2, fieldType, meta) {
44316
44416
  if (value === void 0 || value === null) return "\u2014";
44317
44417
  const str = String(value);
44318
44418
  switch (fieldType) {
44319
- case "image":
44419
+ case "image": {
44420
+ if (!str) return "\u2014";
44421
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
44422
+ "img",
44423
+ {
44424
+ src: str,
44425
+ alt: formatFieldLabel(fieldName2),
44426
+ className: "max-w-full max-h-64 rounded-md object-contain",
44427
+ loading: "lazy"
44428
+ }
44429
+ ) });
44430
+ }
44320
44431
  case "url": {
44321
44432
  if (str.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str.startsWith("data:image/")) {
44322
44433
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -44329,7 +44440,7 @@ function renderRichFieldValue(value, fieldName2, fieldType, meta) {
44329
44440
  }
44330
44441
  ) });
44331
44442
  }
44332
- if (fieldType === "url" && /^https?:\/\//i.test(str)) {
44443
+ if (/^https?:\/\//i.test(str)) {
44333
44444
  return /* @__PURE__ */ jsxRuntime.jsx(
44334
44445
  "a",
44335
44446
  {
@@ -44415,8 +44526,10 @@ function renderRichFieldValue(value, fieldName2, fieldType, meta) {
44415
44526
  return str;
44416
44527
  }
44417
44528
  case "relation": {
44418
- const match = meta?.options?.find((opt) => opt.value === str);
44419
- return match ? match.label : str;
44529
+ const labels = relationDisplayLabels(value, meta?.options);
44530
+ if (labels.length === 0) return "\u2014";
44531
+ if (labels.length === 1) return labels[0];
44532
+ return /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", wrap: true, children: labels.map((label, i) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", children: label }, i)) });
44420
44533
  }
44421
44534
  case "file": {
44422
44535
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
@@ -44518,6 +44631,7 @@ var init_DetailPanel = __esm({
44518
44631
  init_cn();
44519
44632
  init_format();
44520
44633
  init_getNestedValue();
44634
+ init_relationLabel();
44521
44635
  init_useEventBus();
44522
44636
  init_UploadDropZone();
44523
44637
  formatFieldLabel = humanizeFieldName;
@@ -45176,6 +45290,8 @@ function determineInputType(field) {
45176
45290
  return "url";
45177
45291
  case "file":
45178
45292
  return "file";
45293
+ case "image":
45294
+ return "image";
45179
45295
  case "money":
45180
45296
  return "currency";
45181
45297
  case "number":
@@ -45775,6 +45891,49 @@ var init_Form = __esm({
45775
45891
  }
45776
45892
  }
45777
45893
  );
45894
+ case "image": {
45895
+ const imageUrl = typeof currentValue === "string" ? currentValue : "";
45896
+ return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
45897
+ imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
45898
+ "img",
45899
+ {
45900
+ src: imageUrl,
45901
+ alt: "",
45902
+ "aria-hidden": "true",
45903
+ className: "h-24 w-full max-w-xs rounded-md border border-border object-cover",
45904
+ "data-testid": `image-preview-${fieldName2}`
45905
+ }
45906
+ ) : null,
45907
+ /* @__PURE__ */ jsxRuntime.jsx(
45908
+ Input,
45909
+ {
45910
+ ...commonProps,
45911
+ type: "url",
45912
+ value: imageUrl,
45913
+ onChange: (e) => handleChange(fieldName2, e.target.value)
45914
+ }
45915
+ ),
45916
+ /* @__PURE__ */ jsxRuntime.jsx(
45917
+ UploadDropZone,
45918
+ {
45919
+ accept: "image/*",
45920
+ maxFiles: 1,
45921
+ maxSize: 2 * 1024 * 1024,
45922
+ disabled: isLoading,
45923
+ onFiles: (files) => {
45924
+ const f3 = files[0];
45925
+ if (!f3) return;
45926
+ const reader = new FileReader();
45927
+ reader.onload = () => handleChange(
45928
+ fieldName2,
45929
+ typeof reader.result === "string" ? reader.result : ""
45930
+ );
45931
+ reader.readAsDataURL(f3);
45932
+ }
45933
+ }
45934
+ )
45935
+ ] });
45936
+ }
45778
45937
  case "date":
45779
45938
  return /* @__PURE__ */ jsxRuntime.jsx(
45780
45939
  Input,