@open-mercato/ui 0.9.1-develop.7498.1.08d233a6cc → 0.9.1-develop.7513.1.ae2715553f

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.
Files changed (33) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/backend/CrudForm.js +56 -36
  3. package/dist/backend/CrudForm.js.map +2 -2
  4. package/dist/backend/DataTable.js +40 -14
  5. package/dist/backend/DataTable.js.map +2 -2
  6. package/dist/backend/columns/ColumnChooserPanel.js +4 -3
  7. package/dist/backend/columns/ColumnChooserPanel.js.map +2 -2
  8. package/dist/backend/crud/injectedFieldGroups.js +50 -0
  9. package/dist/backend/crud/injectedFieldGroups.js.map +7 -0
  10. package/dist/backend/filters/FilterFieldPicker.js +3 -2
  11. package/dist/backend/filters/FilterFieldPicker.js.map +2 -2
  12. package/dist/backend/utils/api.js +25 -4
  13. package/dist/backend/utils/api.js.map +2 -2
  14. package/dist/backend/utils/groupLabels.js +16 -0
  15. package/dist/backend/utils/groupLabels.js.map +7 -0
  16. package/dist/theme/QueryProvider.js +7 -1
  17. package/dist/theme/QueryProvider.js.map +2 -2
  18. package/package.json +3 -3
  19. package/src/backend/CrudForm.tsx +68 -45
  20. package/src/backend/DataTable.tsx +54 -14
  21. package/src/backend/__tests__/CrudForm.fieldInjection.test.tsx +85 -4
  22. package/src/backend/__tests__/DataTable.injectedColumnPlacement.test.tsx +146 -0
  23. package/src/backend/columns/ColumnChooserPanel.tsx +4 -3
  24. package/src/backend/columns/__tests__/ColumnChooserPanel.groupLabels.test.tsx +72 -0
  25. package/src/backend/crud/__tests__/injectedFieldGroups.test.ts +90 -0
  26. package/src/backend/crud/injectedFieldGroups.ts +75 -0
  27. package/src/backend/filters/FilterFieldPicker.tsx +3 -2
  28. package/src/backend/filters/__tests__/FilterFieldPicker.groupLabels.test.tsx +77 -0
  29. package/src/backend/utils/__tests__/api.test.ts +67 -0
  30. package/src/backend/utils/api.ts +33 -4
  31. package/src/backend/utils/groupLabels.ts +26 -0
  32. package/src/theme/QueryProvider.tsx +7 -1
  33. package/src/theme/__tests__/QueryProvider.i18n.test.tsx +40 -0
@@ -1,3 +1,3 @@
1
1
  Generated lucide registry with 182 icons -> /home/runner/work/open-mercato/open-mercato/packages/ui/src/backend/icons/lucideRegistry.generated.tsx
2
- [build:ui] found 454 entry points
2
+ [build:ui] found 456 entry points
3
3
  [build:ui] built successfully
@@ -98,7 +98,7 @@ import { CollapsibleGroup } from "./crud/CollapsibleGroup.js";
98
98
  import { SortableGroupHandleProvider } from "./crud/SortableGroupHandle.js";
99
99
  import { useGroupOrder } from "./crud/useGroupOrder.js";
100
100
  import { InjectedField } from "./injection/InjectedField.js";
101
- import { insertByInjectionPlacement } from "@open-mercato/shared/modules/widgets/injection-position";
101
+ import { placeInjectedFieldsInGroups } from "./crud/injectedFieldGroups.js";
102
102
  import { evaluateInjectedVisibility } from "./injection/visibility-utils.js";
103
103
  import { ComponentReplacementHandles } from "@open-mercato/shared/modules/widgets/component-registry";
104
104
  import { crudFormExtensionSpotId, extensionSpotChildId } from "@open-mercato/shared/modules/widgets/extension-points";
@@ -173,6 +173,21 @@ function buildResolvedEntityIdsKey(entityId, entityIds) {
173
173
  }
174
174
  return list.join("\0");
175
175
  }
176
+ function renderNoInjectedFieldWidget() {
177
+ return null;
178
+ }
179
+ async function skipInjectedFieldDeleteEvent() {
180
+ }
181
+ const INJECTED_FIELD_DELETE_EVENT_DEFAULTS = {
182
+ onBeforeDelete: skipInjectedFieldDeleteEvent,
183
+ onDelete: skipInjectedFieldDeleteEvent,
184
+ onAfterDelete: skipInjectedFieldDeleteEvent
185
+ };
186
+ function injectionWidgetPriority(widget) {
187
+ const placementPriority = widget.placement?.priority;
188
+ if (typeof placementPriority === "number") return placementPriority;
189
+ return typeof widget.module.metadata.priority === "number" ? widget.module.metadata.priority : 0;
190
+ }
176
191
  function mergeByKey(primary, legacy, keyOf) {
177
192
  if (!legacy.length) return primary;
178
193
  const seen = new Set(primary.map(keyOf));
@@ -754,7 +769,26 @@ function CrudForm({
754
769
  () => mergeByKey(primaryFieldWidgets, legacyFieldWidgets, (w) => w.metadata.id),
755
770
  [primaryFieldWidgets, legacyFieldWidgets]
756
771
  );
757
- const { triggerEvent: triggerInjectionEvent } = useInjectionSpotEvents(resolvedInjectionSpotId ?? "", injectionWidgets);
772
+ const injectionEventWidgets = React.useMemo(() => {
773
+ const fieldEventWidgets = [];
774
+ for (const widget of injectedFieldWidgets) {
775
+ if (!("fields" in widget) || !widget.eventHandlers) continue;
776
+ fieldEventWidgets.push({
777
+ widgetId: widget.metadata.id,
778
+ module: {
779
+ metadata: widget.metadata,
780
+ eventHandlers: { ...INJECTED_FIELD_DELETE_EVENT_DEFAULTS, ...widget.eventHandlers },
781
+ Widget: renderNoInjectedFieldWidget
782
+ },
783
+ moduleId: widget.moduleId,
784
+ key: widget.key,
785
+ placement: widget.placement
786
+ });
787
+ }
788
+ if (!fieldEventWidgets.length) return injectionWidgets;
789
+ return mergeByKey(injectionWidgets, fieldEventWidgets, (widget) => widget.widgetId).map((widget, index) => ({ widget, index })).sort((left, right) => injectionWidgetPriority(right.widget) - injectionWidgetPriority(left.widget) || left.index - right.index).map((entry) => entry.widget);
790
+ }, [injectionWidgets, injectedFieldWidgets]);
791
+ const { triggerEvent: triggerInjectionEvent } = useInjectionSpotEvents(resolvedInjectionSpotId ?? "", injectionEventWidgets);
758
792
  const extendedInjectionEventsEnabled = CRUDFORM_EXTENDED_EVENTS_ENABLED && Boolean(resolvedInjectionSpotId);
759
793
  const widgetRequiredFieldIds = React.useMemo(() => {
760
794
  const ids = /* @__PURE__ */ new Set();
@@ -1360,6 +1394,24 @@ function CrudForm({
1360
1394
  }
1361
1395
  return definitions;
1362
1396
  }, [injectedFieldWidgets]);
1397
+ const injectedFieldPlacements = React.useMemo(() => {
1398
+ const placements = /* @__PURE__ */ new Map();
1399
+ for (const widget of injectedFieldWidgets) {
1400
+ if (!("fields" in widget)) continue;
1401
+ for (const field of widget.fields ?? []) {
1402
+ placements.set(field.id, widget.placement);
1403
+ }
1404
+ }
1405
+ return placements;
1406
+ }, [injectedFieldWidgets]);
1407
+ const groupsWithInjectedFields = React.useMemo(() => {
1408
+ if (!groups || groups.length === 0 || injectedFieldDefinitions.length === 0) return groups;
1409
+ return placeInjectedFieldsInGroups(groups, injectedFieldDefinitions, injectedFieldPlacements, (definition) => {
1410
+ if (process.env.NODE_ENV !== "production") {
1411
+ logger.warn("Injected field targets a group that does not exist; appended to the last plain-field group", { fieldId: definition.id, group: definition.group });
1412
+ }
1413
+ });
1414
+ }, [groups, injectedFieldDefinitions, injectedFieldPlacements]);
1363
1415
  const injectedFieldContext = React.useMemo(() => {
1364
1416
  const recordValues = values;
1365
1417
  const organizationId = typeof recordValues.organizationId === "string" ? recordValues.organizationId : null;
@@ -1406,22 +1458,15 @@ function CrudForm({
1406
1458
  }
1407
1459
  return (group.fields ?? []).map((entry) => typeof entry === "string" ? entry : entry.id);
1408
1460
  };
1409
- const declaredGroups = groups ?? [];
1410
1461
  const hidden = /* @__PURE__ */ new Set();
1411
1462
  const visible = /* @__PURE__ */ new Set();
1412
- for (const group of declaredGroups) {
1463
+ for (const group of groupsWithInjectedFields ?? []) {
1413
1464
  const target = hiddenGroupIdSet.has(group.id) ? hidden : visible;
1414
1465
  for (const fieldId of groupFieldIds(group)) target.add(fieldId);
1415
1466
  }
1416
- const declaredGroupIds = new Set(declaredGroups.map((group) => group.id));
1417
- const fallbackGroupId = declaredGroups[declaredGroups.length - 1]?.id;
1418
- for (const definition of injectedFieldDefinitions) {
1419
- const targetGroupId = definition.group && declaredGroupIds.has(definition.group) ? definition.group : fallbackGroupId;
1420
- if (targetGroupId && hiddenGroupIdSet.has(targetGroupId)) hidden.add(definition.id);
1421
- }
1422
1467
  for (const fieldId of visible) hidden.delete(fieldId);
1423
1468
  return hidden;
1424
- }, [cfFields, groups, hiddenGroupIdSet, injectedFieldDefinitions, placedCustomFieldIds]);
1469
+ }, [cfFields, groupsWithInjectedFields, hiddenGroupIdSet, placedCustomFieldIds]);
1425
1470
  const hiddenInjectedFieldIds = React.useMemo(() => {
1426
1471
  const hidden = /* @__PURE__ */ new Set();
1427
1472
  for (const definition of injectedFieldDefinitions) {
@@ -1628,31 +1673,6 @@ function CrudForm({
1628
1673
  pairs.sort((a, b) => b.priority - a.priority);
1629
1674
  return pairs.map((p) => p.group);
1630
1675
  }, [injectionWidgets, injectionContext, pending, setValues, values]);
1631
- const groupsWithInjectedFields = React.useMemo(() => {
1632
- if (!groups || groups.length === 0 || injectedFieldDefinitions.length === 0) return groups;
1633
- const cloned = groups.map((group) => ({ ...group, fields: [...group.fields ?? []] }));
1634
- const fallbackIndex = cloned.length - 1;
1635
- for (const definition of injectedFieldDefinitions) {
1636
- const targetIndex = cloned.findIndex((group) => group.id === definition.group);
1637
- const index = targetIndex >= 0 ? targetIndex : fallbackIndex;
1638
- if (targetIndex < 0 && process.env.NODE_ENV !== "production") {
1639
- logger.warn("Injected field targets a group that does not exist; appended to last group", { fieldId: definition.id, group: definition.group });
1640
- }
1641
- if (index < 0) continue;
1642
- const fieldEntries = cloned[index].fields ?? [];
1643
- const alreadyPresent = fieldEntries.some((entry) => {
1644
- const entryId = typeof entry === "string" ? entry : entry.id;
1645
- return entryId === definition.id;
1646
- });
1647
- cloned[index].fields = alreadyPresent ? fieldEntries : insertByInjectionPlacement(
1648
- fieldEntries,
1649
- definition.id,
1650
- definition.placement,
1651
- (entry) => typeof entry === "string" ? entry : entry.id
1652
- );
1653
- }
1654
- return cloned;
1655
- }, [groups, injectedFieldDefinitions]);
1656
1676
  const shouldAutoGroup = (!groupsWithInjectedFields || groupsWithInjectedFields.length === 0) && injectionGroupCards.length > 0;
1657
1677
  const declaredGroupsForLayout = React.useMemo(() => {
1658
1678
  const baseGroups = groupsWithInjectedFields && groupsWithInjectedFields.length ? groupsWithInjectedFields : [];