@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.
@@ -33142,6 +33142,33 @@ var init_Lightbox = __esm({
33142
33142
  Lightbox.displayName = "Lightbox";
33143
33143
  }
33144
33144
  });
33145
+
33146
+ // lib/relationLabel.ts
33147
+ function relationLabel(value) {
33148
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date)
33149
+ return null;
33150
+ for (const key of ["name", "title", "label"]) {
33151
+ const candidate = value[key];
33152
+ if (typeof candidate === "string" && candidate !== "") return candidate;
33153
+ }
33154
+ const id = value.id;
33155
+ return id !== void 0 && id !== null ? String(id) : null;
33156
+ }
33157
+ function relationDisplayLabels(value, options) {
33158
+ if (value === void 0 || value === null || value === "") return [];
33159
+ if (Array.isArray(value)) {
33160
+ return value.flatMap((item) => relationDisplayLabels(item, options));
33161
+ }
33162
+ const hydrated = relationLabel(value);
33163
+ if (hydrated !== null) return [hydrated];
33164
+ const raw = String(value);
33165
+ const match = options?.find((opt) => opt.value === raw);
33166
+ return [match ? match.label : raw];
33167
+ }
33168
+ var init_relationLabel = __esm({
33169
+ "lib/relationLabel.ts"() {
33170
+ }
33171
+ });
33145
33172
  function renderIconInput3(icon, props) {
33146
33173
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
33147
33174
  }
@@ -33459,6 +33486,7 @@ var init_TableView = __esm({
33459
33486
  "use client";
33460
33487
  init_cn();
33461
33488
  init_format();
33489
+ init_relationLabel();
33462
33490
  init_getNestedValue();
33463
33491
  init_useEventBus();
33464
33492
  init_Box();
@@ -33472,7 +33500,13 @@ var init_TableView = __esm({
33472
33500
  init_Menu();
33473
33501
  init_useDataDnd();
33474
33502
  tableViewLog = logger.createLogger("almadar:ui:table-view");
33475
- formatCell = (value, format) => formatValue(value, format);
33503
+ formatCell = (value, format) => {
33504
+ if (value !== null && value !== void 0 && typeof value === "object" && !(value instanceof Date)) {
33505
+ const labels = relationDisplayLabels(value);
33506
+ if (labels.length > 0) return labels.join(", ");
33507
+ }
33508
+ return formatValue(value, format);
33509
+ };
33476
33510
  MAX_MEASURED_COL_CH = 32;
33477
33511
  BADGE_CHROME_CH = 4;
33478
33512
  alignClass = {
@@ -37087,6 +37121,13 @@ var init_RichTextEditor = __esm({
37087
37121
  document.execCommand("createLink", false, safe);
37088
37122
  afterEdit();
37089
37123
  }, [afterEdit, t, toolbar.link]);
37124
+ const execImage = React87.useCallback(() => {
37125
+ const url = window.prompt(t("richTextEditor.imagePrompt"));
37126
+ if (!url) return;
37127
+ const safe = safeUrl(url, ["http://", "https://", "data:image/"]) ?? `https://${url}`;
37128
+ document.execCommand("insertImage", false, safe);
37129
+ afterEdit();
37130
+ }, [afterEdit, t]);
37090
37131
  const execRule = React87.useCallback(() => {
37091
37132
  document.execCommand("insertHorizontalRule", false);
37092
37133
  afterEdit();
@@ -37124,6 +37165,7 @@ var init_RichTextEditor = __esm({
37124
37165
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Quote, label: t("richTextEditor.quote"), active: toolbar.block === "blockquote", onExec: () => execBlock("blockquote") }),
37125
37166
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Code, label: t("richTextEditor.code"), active: toolbar.block === "pre", onExec: () => execBlock("pre") }),
37126
37167
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Link, label: toolbar.link ? t("richTextEditor.removeLink") : t("richTextEditor.link"), active: toolbar.link, onExec: execLink }),
37168
+ /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Image, label: t("richTextEditor.image"), onExec: execImage }),
37127
37169
  /* @__PURE__ */ jsxRuntime.jsx(ToolbarButton, { icon: LucideIcons2.Minus, label: t("richTextEditor.divider"), onExec: execRule })
37128
37170
  ]
37129
37171
  }
@@ -37161,9 +37203,11 @@ function renderIconInput4(icon, props) {
37161
37203
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, ...props });
37162
37204
  }
37163
37205
  function DocumentPanel({
37164
- entity,
37206
+ id,
37165
37207
  title,
37166
37208
  subtitle,
37209
+ coverImage,
37210
+ icon,
37167
37211
  value,
37168
37212
  actions,
37169
37213
  maxInlineActions,
@@ -37181,14 +37225,14 @@ function DocumentPanel({
37181
37225
  const eventBus = useEventBus();
37182
37226
  const { t } = hooks.useTranslate();
37183
37227
  const [titleDraft, setTitleDraft] = React87.useState(null);
37184
- const recordId = entity?.id !== void 0 && entity?.id !== null ? String(entity.id) : "";
37228
+ const recordId = id ?? "";
37185
37229
  const actionDefs = actions ?? [];
37186
37230
  const inlineCap = Math.min(maxInlineActions ?? 1, 1);
37187
37231
  const inlineActions = actionDefs.filter((a) => a.variant === "primary").slice(0, inlineCap);
37188
37232
  const menuActions = actionDefs.filter((a) => !inlineActions.includes(a));
37189
37233
  const fireAction = (action) => {
37190
37234
  if (!action.event) return;
37191
- eventBus.emit(`UI:${action.event}`, { id: recordId, row: entity });
37235
+ eventBus.emit(`UI:${action.event}`, { id: recordId, title: title ?? "" });
37192
37236
  };
37193
37237
  const commitTitle = () => {
37194
37238
  if (!titleCommitEvent) return;
@@ -37239,62 +37283,74 @@ function DocumentPanel({
37239
37283
  const emitWithId = (event) => () => {
37240
37284
  if (event) eventBus.emit(`UI:${event}`, { id: recordId });
37241
37285
  };
37242
- 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: [
37243
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
37244
- /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
37245
- titleNode,
37246
- subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: subtitle }) : null
37247
- ] }),
37248
- /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-shrink-0 pt-1 items-center", children: editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
37249
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", className: "hidden sm:block", children: autosaveHint ?? (t("documentPanel.autosaveHint") || "") }),
37250
- /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: emitWithId(doneEvent), "data-testid": "document-done", children: doneLabel ?? (t("documentPanel.done") || "Done") })
37251
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
37252
- editEvent && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: emitWithId(editEvent), "data-testid": "document-edit", children: [
37253
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "edit", size: "xs", className: "mr-1" }),
37254
- editLabel ?? (t("documentPanel.edit") || "Edit")
37255
- ] }),
37256
- inlineActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
37257
- Button,
37258
- {
37259
- variant: "primary",
37260
- size: "sm",
37261
- onClick: () => fireAction(action),
37262
- "data-testid": `action-${action.event}`,
37263
- children: [
37264
- action.icon && renderIconInput4(action.icon, { size: "xs", className: "mr-1" }),
37265
- action.label
37266
- ]
37267
- },
37268
- idx
37269
- )),
37270
- menuActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
37271
- Menu,
37272
- {
37273
- position: "bottom-end",
37274
- 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" }) }),
37275
- items: menuActions.map((action) => ({
37276
- label: action.label,
37277
- icon: action.icon,
37278
- variant: action.variant === "danger" ? "danger" : "default",
37279
- onClick: () => fireAction(action)
37280
- }))
37281
- }
37282
- )
37283
- ] }) })
37284
- ] }),
37285
- editing ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, changeEvent: contentChangeEvent, placeholder }) : /* @__PURE__ */ jsxRuntime.jsx(
37286
- Box,
37286
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { variant: "elevated", className: cn("w-full overflow-hidden", className), children: [
37287
+ coverImage ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "h-44 w-full sm:h-52", "data-testid": "document-cover", children: /* @__PURE__ */ jsxRuntime.jsx(
37288
+ "img",
37287
37289
  {
37288
- onClick: emitWithId(editEvent),
37289
- className: cn(
37290
- "rounded-md px-1 -mx-1 min-h-[16rem]",
37291
- editEvent && "cursor-text transition-colors hover:bg-muted/30"
37292
- ),
37293
- "data-testid": "document-body",
37294
- children: value && value !== "" ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, readOnly: true }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "muted", children: placeholder ?? (t("documentPanel.placeholder") || "") })
37295
- }
37296
- )
37297
- ] }) });
37290
+ src: coverImage,
37291
+ alt: "",
37292
+ "aria-hidden": "true",
37293
+ className: "h-full w-full object-cover"
37294
+ }
37295
+ ) }) : null,
37296
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", className: "p-6 sm:p-8", children: [
37297
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
37298
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
37299
+ icon ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-muted-foreground", "data-testid": "document-icon", children: renderIconInput4(icon, { size: "lg" }) }) : null,
37300
+ titleNode,
37301
+ subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: subtitle }) : null
37302
+ ] }),
37303
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-shrink-0 pt-1 items-center", children: editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
37304
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "muted", className: "hidden sm:block", children: autosaveHint ?? (t("documentPanel.autosaveHint") || "") }),
37305
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: emitWithId(doneEvent), "data-testid": "document-done", children: doneLabel ?? (t("documentPanel.done") || "Done") })
37306
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
37307
+ editEvent && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: emitWithId(editEvent), "data-testid": "document-edit", children: [
37308
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "edit", size: "xs", className: "mr-1" }),
37309
+ editLabel ?? (t("documentPanel.edit") || "Edit")
37310
+ ] }),
37311
+ inlineActions.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
37312
+ Button,
37313
+ {
37314
+ variant: "primary",
37315
+ size: "sm",
37316
+ onClick: () => fireAction(action),
37317
+ "data-testid": `action-${action.event}`,
37318
+ children: [
37319
+ action.icon && renderIconInput4(action.icon, { size: "xs", className: "mr-1" }),
37320
+ action.label
37321
+ ]
37322
+ },
37323
+ idx
37324
+ )),
37325
+ menuActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
37326
+ Menu,
37327
+ {
37328
+ position: "bottom-end",
37329
+ 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" }) }),
37330
+ items: menuActions.map((action) => ({
37331
+ label: action.label,
37332
+ icon: action.icon,
37333
+ variant: action.variant === "danger" ? "danger" : "default",
37334
+ onClick: () => fireAction(action)
37335
+ }))
37336
+ }
37337
+ )
37338
+ ] }) })
37339
+ ] }),
37340
+ editing ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, changeEvent: contentChangeEvent, placeholder }) : /* @__PURE__ */ jsxRuntime.jsx(
37341
+ Box,
37342
+ {
37343
+ onClick: emitWithId(editEvent),
37344
+ className: cn(
37345
+ "rounded-md px-1 -mx-1 min-h-[16rem]",
37346
+ editEvent && "cursor-text transition-colors hover:bg-muted/30"
37347
+ ),
37348
+ "data-testid": "document-body",
37349
+ children: value && value !== "" ? /* @__PURE__ */ jsxRuntime.jsx(RichTextEditor, { value, readOnly: true }) : /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "muted", children: placeholder ?? (t("documentPanel.placeholder") || "") })
37350
+ }
37351
+ )
37352
+ ] })
37353
+ ] });
37298
37354
  }
37299
37355
  var init_DocumentPanel = __esm({
37300
37356
  "components/core/molecules/DocumentPanel.tsx"() {
@@ -37318,17 +37374,16 @@ function renderIconInput5(icon, props) {
37318
37374
  function fieldName(field) {
37319
37375
  return field.name ?? field.key ?? "";
37320
37376
  }
37321
- function relationLabel(value) {
37322
- if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) return null;
37323
- for (const key of ["name", "title", "label"]) {
37324
- const candidate = value[key];
37325
- if (typeof candidate === "string" && candidate !== "") return candidate;
37377
+ function relationId(value) {
37378
+ if (value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date)) {
37379
+ const id = value.id;
37380
+ if (id !== void 0 && id !== null) return String(id);
37326
37381
  }
37327
- const id = value.id;
37328
- return id !== void 0 && id !== null ? String(id) : null;
37382
+ return String(value);
37329
37383
  }
37330
37384
  function fieldKind(field, value) {
37331
37385
  if (field.kind) return field.kind;
37386
+ if (field.type === "image") return "image";
37332
37387
  if (field.options && field.options.length > 0) return "select";
37333
37388
  if (typeof value === "boolean" || field.format === "boolean") return "boolean";
37334
37389
  if (Array.isArray(value) || relationLabel(value ?? null) !== null) return "readonly";
@@ -37338,6 +37393,7 @@ function DocumentDetails({
37338
37393
  entity,
37339
37394
  fields,
37340
37395
  metaCommitEvent,
37396
+ relationEvent,
37341
37397
  title,
37342
37398
  className
37343
37399
  }) {
@@ -37352,11 +37408,51 @@ function DocumentDetails({
37352
37408
  if (!metaCommitEvent || next === current) return;
37353
37409
  eventBus.emit(`UI:${metaCommitEvent}`, { id: recordId, patch: { id: recordId, [name]: next } });
37354
37410
  };
37411
+ const relationChip = (item, name, key) => {
37412
+ const label = relationLabel(item) ?? humanizeEnumValue(String(item));
37413
+ if (!relationEvent) {
37414
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", children: label }, key);
37415
+ }
37416
+ const emitClick = () => eventBus.emit(`UI:${relationEvent}`, { field: name, id: relationId(item), name: label });
37417
+ return /* @__PURE__ */ jsxRuntime.jsx(
37418
+ Badge,
37419
+ {
37420
+ variant: "default",
37421
+ role: "button",
37422
+ tabIndex: 0,
37423
+ className: "cursor-pointer transition-colors hover:bg-accent",
37424
+ onClick: emitClick,
37425
+ onKeyDown: (e) => {
37426
+ if (e.key === "Enter" || e.key === " ") {
37427
+ e.preventDefault();
37428
+ emitClick();
37429
+ }
37430
+ },
37431
+ "data-testid": `relation-chip-${name}`,
37432
+ children: label
37433
+ },
37434
+ key
37435
+ );
37436
+ };
37355
37437
  const renderValue = (field) => {
37356
37438
  const name = fieldName(field);
37357
37439
  const raw = getNestedValue(entity ?? {}, name);
37358
37440
  const kind = fieldKind(field, raw);
37359
37441
  const label = field.label ?? field.header ?? humanizeFieldName(name);
37442
+ if (kind === "image") {
37443
+ const url = typeof raw === "string" ? raw : "";
37444
+ if (!url) return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: "\u2014" });
37445
+ return /* @__PURE__ */ jsxRuntime.jsx(
37446
+ "img",
37447
+ {
37448
+ src: url,
37449
+ alt: label,
37450
+ loading: "lazy",
37451
+ className: "max-h-32 w-full rounded-md border border-border object-cover",
37452
+ "data-testid": `document-property-image-${name}`
37453
+ }
37454
+ );
37455
+ }
37360
37456
  if (kind === "boolean") {
37361
37457
  return /* @__PURE__ */ jsxRuntime.jsx(
37362
37458
  Switch,
@@ -37386,9 +37482,12 @@ function DocumentDetails({
37386
37482
  if (raw.length === 0) {
37387
37483
  return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "secondary", children: "\u2014" });
37388
37484
  }
37389
- 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)) });
37485
+ return /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-wrap", children: raw.map((item, i) => relationChip(item, name, i)) });
37486
+ }
37487
+ if (raw !== void 0 && raw !== null && raw !== "" && relationLabel(raw) !== null) {
37488
+ return relationChip(raw, name);
37390
37489
  }
37391
- const shown2 = raw === void 0 || raw === null || raw === "" ? "\u2014" : relationLabel(raw) ?? formatValue(raw, field.format);
37490
+ const shown2 = raw === void 0 || raw === null || raw === "" ? "\u2014" : formatValue(raw, field.format);
37392
37491
  return /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "break-words", children: shown2 });
37393
37492
  }
37394
37493
  if (fieldDraft?.name === name) {
@@ -37457,6 +37556,7 @@ var init_DocumentDetails = __esm({
37457
37556
  init_cn();
37458
37557
  init_format();
37459
37558
  init_getNestedValue();
37559
+ init_relationLabel();
37460
37560
  init_useEventBus();
37461
37561
  init_Box();
37462
37562
  init_Stack();
@@ -42451,7 +42551,18 @@ function renderRichFieldValue(value, fieldName2, fieldType, meta) {
42451
42551
  if (value === void 0 || value === null) return "\u2014";
42452
42552
  const str = String(value);
42453
42553
  switch (fieldType) {
42454
- case "image":
42554
+ case "image": {
42555
+ if (!str) return "\u2014";
42556
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
42557
+ "img",
42558
+ {
42559
+ src: str,
42560
+ alt: formatFieldLabel(fieldName2),
42561
+ className: "max-w-full max-h-64 rounded-md object-contain",
42562
+ loading: "lazy"
42563
+ }
42564
+ ) });
42565
+ }
42455
42566
  case "url": {
42456
42567
  if (str.match(/\.(png|jpe?g|gif|svg|webp|avif)(\?|$)/i) || str.startsWith("data:image/")) {
42457
42568
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-1 max-w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -42464,7 +42575,7 @@ function renderRichFieldValue(value, fieldName2, fieldType, meta) {
42464
42575
  }
42465
42576
  ) });
42466
42577
  }
42467
- if (fieldType === "url" && /^https?:\/\//i.test(str)) {
42578
+ if (/^https?:\/\//i.test(str)) {
42468
42579
  return /* @__PURE__ */ jsxRuntime.jsx(
42469
42580
  "a",
42470
42581
  {
@@ -42550,8 +42661,10 @@ function renderRichFieldValue(value, fieldName2, fieldType, meta) {
42550
42661
  return str;
42551
42662
  }
42552
42663
  case "relation": {
42553
- const match = meta?.options?.find((opt) => opt.value === str);
42554
- return match ? match.label : str;
42664
+ const labels = relationDisplayLabels(value, meta?.options);
42665
+ if (labels.length === 0) return "\u2014";
42666
+ if (labels.length === 1) return labels[0];
42667
+ return /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", wrap: true, children: labels.map((label, i) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", children: label }, i)) });
42555
42668
  }
42556
42669
  case "file": {
42557
42670
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
@@ -42653,6 +42766,7 @@ var init_DetailPanel = __esm({
42653
42766
  init_cn();
42654
42767
  init_format();
42655
42768
  init_getNestedValue();
42769
+ init_relationLabel();
42656
42770
  init_useEventBus();
42657
42771
  init_UploadDropZone();
42658
42772
  formatFieldLabel = humanizeFieldName;
@@ -43311,6 +43425,8 @@ function determineInputType(field) {
43311
43425
  return "url";
43312
43426
  case "file":
43313
43427
  return "file";
43428
+ case "image":
43429
+ return "image";
43314
43430
  case "money":
43315
43431
  return "currency";
43316
43432
  case "number":
@@ -43910,6 +44026,49 @@ var init_Form = __esm({
43910
44026
  }
43911
44027
  }
43912
44028
  );
44029
+ case "image": {
44030
+ const imageUrl = typeof currentValue === "string" ? currentValue : "";
44031
+ return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
44032
+ imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
44033
+ "img",
44034
+ {
44035
+ src: imageUrl,
44036
+ alt: "",
44037
+ "aria-hidden": "true",
44038
+ className: "h-24 w-full max-w-xs rounded-md border border-border object-cover",
44039
+ "data-testid": `image-preview-${fieldName2}`
44040
+ }
44041
+ ) : null,
44042
+ /* @__PURE__ */ jsxRuntime.jsx(
44043
+ Input,
44044
+ {
44045
+ ...commonProps,
44046
+ type: "url",
44047
+ value: imageUrl,
44048
+ onChange: (e) => handleChange(fieldName2, e.target.value)
44049
+ }
44050
+ ),
44051
+ /* @__PURE__ */ jsxRuntime.jsx(
44052
+ UploadDropZone,
44053
+ {
44054
+ accept: "image/*",
44055
+ maxFiles: 1,
44056
+ maxSize: 2 * 1024 * 1024,
44057
+ disabled: isLoading,
44058
+ onFiles: (files) => {
44059
+ const f3 = files[0];
44060
+ if (!f3) return;
44061
+ const reader = new FileReader();
44062
+ reader.onload = () => handleChange(
44063
+ fieldName2,
44064
+ typeof reader.result === "string" ? reader.result : ""
44065
+ );
44066
+ reader.readAsDataURL(f3);
44067
+ }
44068
+ }
44069
+ )
44070
+ ] });
44071
+ }
43913
44072
  case "date":
43914
44073
  return /* @__PURE__ */ jsxRuntime.jsx(
43915
44074
  Input,