@open-mercato/ui 0.6.6-develop.5716.1.b108502d0d → 0.6.6-develop.5738.1.4182c2b2c7

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.
@@ -678,6 +678,15 @@ function CrudForm({
678
678
  );
679
679
  const { triggerEvent: triggerInjectionEvent } = useInjectionSpotEvents(resolvedInjectionSpotId ?? "", injectionWidgets);
680
680
  const extendedInjectionEventsEnabled = CRUDFORM_EXTENDED_EVENTS_ENABLED && Boolean(resolvedInjectionSpotId);
681
+ const widgetRequiredFieldIds = React.useMemo(() => {
682
+ const ids = /* @__PURE__ */ new Set();
683
+ for (const widget of injectionWidgets ?? []) {
684
+ const metadata = widget.module?.metadata;
685
+ if (!metadata || metadata.enabled === false) continue;
686
+ for (const fieldId of metadata.requiredFields ?? []) ids.add(fieldId);
687
+ }
688
+ return ids;
689
+ }, [injectionWidgets]);
681
690
  const transformValidationErrors = React.useCallback(
682
691
  async (fieldErrors) => {
683
692
  if (!extendedInjectionEventsEnabled || !Object.keys(fieldErrors).length) return fieldErrors;
@@ -1656,6 +1665,28 @@ function CrudForm({
1656
1665
  lastErrorFieldRef.current = fieldId;
1657
1666
  }
1658
1667
  }, [errors, formId]);
1668
+ const scrollToInjectionBlockedTarget = React.useCallback(
1669
+ (fieldErrors) => {
1670
+ if (typeof document === "undefined") return;
1671
+ const form = document.getElementById(formId);
1672
+ if (!form) return;
1673
+ let target = null;
1674
+ for (const fieldId of fieldErrors ? Object.keys(fieldErrors) : []) {
1675
+ const fieldContainer = form.querySelector(`[data-crud-field-id="${fieldId}"]`);
1676
+ if (fieldContainer) {
1677
+ target = fieldContainer;
1678
+ break;
1679
+ }
1680
+ }
1681
+ if (!target) {
1682
+ target = form.querySelector("[data-crud-injection-region]");
1683
+ }
1684
+ if (target && typeof target.scrollIntoView === "function") {
1685
+ target.scrollIntoView({ behavior: "smooth", block: "center" });
1686
+ }
1687
+ },
1688
+ [formId]
1689
+ );
1659
1690
  const updateEditedFieldMarker = React.useCallback((id, nextValue, baselineSource = dirtyBaselineValuesRef.current) => {
1660
1691
  if (baselineSource && createDirtyValueSnapshot(baselineSource[id]) === createDirtyValueSnapshot(nextValue)) {
1661
1692
  userEditedFieldIdsRef.current.delete(id);
@@ -2078,6 +2109,7 @@ function CrudForm({
2078
2109
  }
2079
2110
  const message = result.message || t("ui.forms.flash.saveBlocked", "Save blocked by validation");
2080
2111
  flash(message, "error");
2112
+ scrollToInjectionBlockedTarget(result.fieldErrors);
2081
2113
  setPending(false);
2082
2114
  return;
2083
2115
  }
@@ -2315,7 +2347,8 @@ function CrudForm({
2315
2347
  onSubmitRequest: requestSubmit,
2316
2348
  wrapperClassName,
2317
2349
  entityIdForField: primaryEntityId ?? void 0,
2318
- recordId
2350
+ recordId,
2351
+ markRequired: widgetRequiredFieldIds.has(f.id)
2319
2352
  },
2320
2353
  f.id
2321
2354
  );
@@ -2554,7 +2587,7 @@ function CrudForm({
2554
2587
  const customFieldsInnerNodes = [];
2555
2588
  if (g.component) {
2556
2589
  customFieldsInnerNodes.push(
2557
- /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card px-4 py-3", children: g.component({ values, setValue, errors }) }, `${g.id}-component`)
2590
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card px-4 py-3", children: g.component({ values, setValue, errors, requiredFieldIds: widgetRequiredFieldIds }) }, `${g.id}-component`)
2558
2591
  );
2559
2592
  }
2560
2593
  const renderedSections = renderCustomFieldsContent();
@@ -2604,7 +2637,7 @@ function CrudForm({
2604
2637
  }
2605
2638
  continue;
2606
2639
  }
2607
- const componentNode = g.component ? g.component({ values, setValue, errors }) : null;
2640
+ const componentNode = g.component ? g.component({ values, setValue, errors, requiredFieldIds: widgetRequiredFieldIds }) : null;
2608
2641
  if (g.bare) {
2609
2642
  if (componentNode) {
2610
2643
  nodes.push(/* @__PURE__ */ jsx(React.Fragment, { children: componentNode }, g.id));
@@ -2710,7 +2743,7 @@ function CrudForm({
2710
2743
  className: hasSecondaryColumn ? "grid grid-cols-1 lg:grid-cols-[7fr_3fr] gap-4" : "grid grid-cols-1 gap-4",
2711
2744
  children: [
2712
2745
  sortableGroupsEnabled ? /* @__PURE__ */ jsx(DndContext, { sensors: sortableSensors, collisionDetection: closestCenter, onDragEnd: handleGroupDragEnd, children: /* @__PURE__ */ jsx(SortableContext, { items: col1Ids, strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsx("div", { className: "space-y-3", children: col1Content }) }) }) : /* @__PURE__ */ jsx("div", { className: "space-y-3", children: col1Content }),
2713
- hasSecondaryColumn ? /* @__PURE__ */ jsx("div", { className: "space-y-3", children: col2Content }) : null
2746
+ hasSecondaryColumn ? /* @__PURE__ */ jsx("div", { className: "space-y-3", "data-crud-injection-region": true, children: col2Content }) : null
2714
2747
  ]
2715
2748
  }
2716
2749
  ),
@@ -2806,7 +2839,8 @@ function CrudForm({
2806
2839
  onSubmitRequest: requestSubmit,
2807
2840
  wrapperClassName,
2808
2841
  entityIdForField: primaryEntityId ?? void 0,
2809
- recordId
2842
+ recordId,
2843
+ markRequired: widgetRequiredFieldIds.has(f.id)
2810
2844
  },
2811
2845
  f.id
2812
2846
  );
@@ -3261,7 +3295,8 @@ const FieldControl = React.memo(
3261
3295
  onSubmitRequest,
3262
3296
  wrapperClassName,
3263
3297
  entityIdForField,
3264
- recordId
3298
+ recordId,
3299
+ markRequired
3265
3300
  }) {
3266
3301
  const t = useT();
3267
3302
  const fieldSetValue = React.useCallback(
@@ -3304,7 +3339,7 @@ const FieldControl = React.memo(
3304
3339
  children: [
3305
3340
  field.type !== "checkbox" && field.label.trim().length > 0 ? /* @__PURE__ */ jsxs("label", { className: "block text-sm font-medium", children: [
3306
3341
  field.label,
3307
- field.required ? /* @__PURE__ */ jsx("span", { className: "text-status-error-text", children: " *" }) : null
3342
+ field.required || markRequired ? /* @__PURE__ */ jsx("span", { className: "text-status-error-text", children: " *" }) : null
3308
3343
  ] }) : null,
3309
3344
  field.type === "text" && /* @__PURE__ */ jsx(
3310
3345
  TextInput,
@@ -3560,7 +3595,7 @@ const FieldControl = React.memo(
3560
3595
  }
3561
3596
  );
3562
3597
  },
3563
- (prev, next) => prev.field.id === next.field.id && prev.field.type === next.field.type && prev.field.label === next.field.label && prev.field.description === next.field.description && prev.field.required === next.field.required && prev.value === next.value && prev.error === next.error && prev.options === next.options && prev.loadFieldOptions === next.loadFieldOptions && prev.autoFocus === next.autoFocus && prev.onSubmitRequest === next.onSubmitRequest && prev.setValue === next.setValue && prev.onBlurRequest === next.onBlurRequest && prev.wrapperClassName === next.wrapperClassName && prev.entityIdForField === next.entityIdForField && prev.recordId === next.recordId && (prev.field.type !== "custom" || prev.values === next.values && prev.field.component === next.field.component)
3598
+ (prev, next) => prev.field.id === next.field.id && prev.field.type === next.field.type && prev.field.label === next.field.label && prev.field.description === next.field.description && prev.field.required === next.field.required && prev.markRequired === next.markRequired && prev.value === next.value && prev.error === next.error && prev.options === next.options && prev.loadFieldOptions === next.loadFieldOptions && prev.autoFocus === next.autoFocus && prev.onSubmitRequest === next.onSubmitRequest && prev.setValue === next.setValue && prev.onBlurRequest === next.onBlurRequest && prev.wrapperClassName === next.wrapperClassName && prev.entityIdForField === next.entityIdForField && prev.recordId === next.recordId && (prev.field.type !== "custom" || prev.values === next.values && prev.field.component === next.field.component)
3564
3599
  );
3565
3600
  export {
3566
3601
  CrudForm