@open-mercato/ui 0.8.1-develop.7296.1.2111d779db → 0.8.1-develop.7441.1.b42958b98c

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 (84) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/backend/CrudForm.js +19 -6
  3. package/dist/backend/CrudForm.js.map +2 -2
  4. package/dist/backend/DataTable.js +61 -9
  5. package/dist/backend/DataTable.js.map +2 -2
  6. package/dist/backend/RowActions.js +54 -5
  7. package/dist/backend/RowActions.js.map +2 -2
  8. package/dist/backend/charts/KpiCard.js +2 -1
  9. package/dist/backend/charts/KpiCard.js.map +2 -2
  10. package/dist/backend/detail/AttachmentsSection.js +17 -5
  11. package/dist/backend/detail/AttachmentsSection.js.map +2 -2
  12. package/dist/backend/detail/NotesSection.js +38 -20
  13. package/dist/backend/detail/NotesSection.js.map +2 -2
  14. package/dist/backend/dev/DevRuntimeDiagnosticsBanner.js +11 -1
  15. package/dist/backend/dev/DevRuntimeDiagnosticsBanner.js.map +2 -2
  16. package/dist/backend/icons/lucideRegistry.generated.js +26 -0
  17. package/dist/backend/icons/lucideRegistry.generated.js.map +2 -2
  18. package/dist/backend/injection/eventBridge.js +33 -0
  19. package/dist/backend/injection/eventBridge.js.map +2 -2
  20. package/dist/backend/inputs/LookupSelect.js +20 -3
  21. package/dist/backend/inputs/LookupSelect.js.map +2 -2
  22. package/dist/backend/inputs/PhoneNumberField.js +1 -1
  23. package/dist/backend/inputs/PhoneNumberField.js.map +2 -2
  24. package/dist/backend/inputs/TagsInput.js +20 -2
  25. package/dist/backend/inputs/TagsInput.js.map +2 -2
  26. package/dist/backend/messages/EmailThreadsPanel.js +2 -1
  27. package/dist/backend/messages/EmailThreadsPanel.js.map +2 -2
  28. package/dist/backend/messages/MessageComposer.js.map +2 -2
  29. package/dist/backend/messages/index.js.map +2 -2
  30. package/dist/backend/messages/message-compose-form-groups.js +24 -0
  31. package/dist/backend/messages/message-compose-form-groups.js.map +2 -2
  32. package/dist/backend/messages/useMessageCompose.js +27 -0
  33. package/dist/backend/messages/useMessageCompose.js.map +2 -2
  34. package/dist/backend/messages/useMessageComposeOperations.js +1 -0
  35. package/dist/backend/messages/useMessageComposeOperations.js.map +2 -2
  36. package/dist/backend/notifications/NotificationItem.js +8 -1
  37. package/dist/backend/notifications/NotificationItem.js.map +2 -2
  38. package/dist/portal/PortalShell.js +2 -2
  39. package/dist/portal/PortalShell.js.map +2 -2
  40. package/dist/primitives/calendar.js +69 -14
  41. package/dist/primitives/calendar.js.map +2 -2
  42. package/package.json +3 -3
  43. package/src/backend/CrudForm.tsx +30 -8
  44. package/src/backend/DataTable.tsx +92 -19
  45. package/src/backend/RowActions.tsx +114 -6
  46. package/src/backend/__tests__/AttachmentsSection.upload.test.tsx +183 -0
  47. package/src/backend/__tests__/CrudForm.fieldInjection.test.tsx +139 -2
  48. package/src/backend/__tests__/CrudForm.hiddenGroups.test.tsx +1 -1
  49. package/src/backend/__tests__/DataTable.extensions.test.tsx +30 -0
  50. package/src/backend/__tests__/DataTable.perspectiveReconcile.test.tsx +320 -2
  51. package/src/backend/__tests__/DevRuntimeDiagnosticsBanner.test.tsx +117 -0
  52. package/src/backend/__tests__/NotesSection.test.tsx +138 -0
  53. package/src/backend/__tests__/RowActions.test.tsx +202 -0
  54. package/src/backend/charts/KpiCard.tsx +1 -0
  55. package/src/backend/charts/__tests__/KpiCard.test.tsx +20 -0
  56. package/src/backend/detail/AttachmentsSection.tsx +17 -5
  57. package/src/backend/detail/NotesSection.tsx +122 -66
  58. package/src/backend/dev/DevRuntimeDiagnosticsBanner.tsx +24 -1
  59. package/src/backend/icons/lucideRegistry.generated.tsx +26 -0
  60. package/src/backend/injection/__tests__/eventBridge.test.tsx +47 -1
  61. package/src/backend/injection/eventBridge.ts +35 -0
  62. package/src/backend/inputs/LookupSelect.tsx +32 -3
  63. package/src/backend/inputs/PhoneNumberField.tsx +1 -1
  64. package/src/backend/inputs/TagsInput.tsx +24 -3
  65. package/src/backend/inputs/__tests__/LookupSelect.test.tsx +166 -1
  66. package/src/backend/inputs/__tests__/PhoneNumberField.typeahead.test.tsx +43 -0
  67. package/src/backend/inputs/__tests__/TagsInput.test.tsx +81 -0
  68. package/src/backend/messages/EmailThreadsPanel.tsx +21 -1
  69. package/src/backend/messages/MessageComposer.tsx +1 -0
  70. package/src/backend/messages/__tests__/EmailThreadsPanel.senderLabel.test.tsx +99 -0
  71. package/src/backend/messages/__tests__/MessageComposer.test.tsx +44 -0
  72. package/src/backend/messages/__tests__/useMessageCompose.test.tsx +69 -0
  73. package/src/backend/messages/__tests__/useMessageComposeSender.test.tsx +208 -0
  74. package/src/backend/messages/index.ts +1 -0
  75. package/src/backend/messages/message-compose-form-groups.tsx +41 -0
  76. package/src/backend/messages/message-composer.types.ts +26 -0
  77. package/src/backend/messages/useMessageCompose.ts +46 -0
  78. package/src/backend/messages/useMessageComposeOperations.ts +8 -0
  79. package/src/backend/notifications/NotificationItem.tsx +7 -0
  80. package/src/backend/notifications/__tests__/NotificationItem.customRenderer.test.tsx +139 -0
  81. package/src/portal/PortalShell.tsx +7 -4
  82. package/src/portal/__tests__/PortalShell.test.tsx +29 -1
  83. package/src/primitives/__tests__/calendar.test.tsx +174 -1
  84. package/src/primitives/calendar.tsx +168 -96
@@ -50,6 +50,7 @@ import { flash } from "./FlashMessages.js";
50
50
  import { useConfirmDialog } from "./confirm-dialog/index.js";
51
51
  import { surfaceRecordConflict } from "./conflicts/index.js";
52
52
  import { ComponentReplacementHandles } from "@open-mercato/shared/modules/widgets/component-registry";
53
+ import { useRegisteredComponent } from "./injection/useRegisteredComponent.js";
53
54
  import { dataTableExtensionSpotId, extensionSpotChildId } from "@open-mercato/shared/modules/widgets/extension-points";
54
55
  import { insertByInjectionPlacement } from "@open-mercato/shared/modules/widgets/injection-position";
55
56
  import { useVirtualizer } from "@tanstack/react-virtual";
@@ -736,7 +737,7 @@ function ViewSwitcherDropdown({
736
737
  ] })
737
738
  ] });
738
739
  }
739
- function DataTable({
740
+ function DataTableImpl({
740
741
  columns,
741
742
  data,
742
743
  toolbar,
@@ -846,6 +847,11 @@ function DataTable({
846
847
  const snapshotHydratedTableRef = React.useRef(null);
847
848
  const serverReconciledTableRef = React.useRef(null);
848
849
  const hydratedSnapshotRef = React.useRef(null);
850
+ const hydratedAdvancedFilterRestoredRef = React.useRef(false);
851
+ const pendingPerspectiveFiltersRef = React.useRef(null);
852
+ React.useLayoutEffect(() => {
853
+ pendingPerspectiveFiltersRef.current = null;
854
+ }, [advancedFilter?.value, perspectiveTableId]);
849
855
  const initialSettingsSource = perspectiveConfig?.initialState?.initialSettings ?? null;
850
856
  const mergedInitialSettings = React.useMemo(
851
857
  () => sanitizePerspectiveSettings(initialSettingsSource),
@@ -1350,9 +1356,18 @@ function DataTable({
1350
1356
  }, [table, columnOrder, columnVisibility, columnSizing, sorting, filterValues, searchValue, advancedFilter]);
1351
1357
  const applyPerspectiveSettings = React.useCallback((settings, nextId, options) => {
1352
1358
  const normalized = sanitizePerspectiveSettings(settings) ?? {};
1359
+ if (!options?.preserveAdvancedFilter && advancedFilter?.onApplyTree) {
1360
+ hydratedAdvancedFilterRestoredRef.current = false;
1361
+ pendingPerspectiveFiltersRef.current = {
1362
+ filters: isPersistedFilterTree(normalized.filters) ? normalized.filters : void 0
1363
+ };
1364
+ }
1353
1365
  autoHiddenVisibilityRef.current = {};
1354
1366
  setViewBaseline(
1355
- options?.preserveAdvancedFilter ? { ...normalized, filters: getCurrentSettings().filters } : normalized
1367
+ options?.preserveAdvancedFilter ? {
1368
+ ...normalized,
1369
+ filters: pendingPerspectiveFiltersRef.current ? pendingPerspectiveFiltersRef.current.filters : getCurrentSettings().filters
1370
+ } : normalized
1356
1371
  );
1357
1372
  if (normalized.columnOrder && normalized.columnOrder.length) {
1358
1373
  setColumnOrder(normalized.columnOrder);
@@ -1399,7 +1414,10 @@ function DataTable({
1399
1414
  if (perspectiveTableId) {
1400
1415
  writePerspectiveCookie(perspectiveTableId, nextId);
1401
1416
  if (nextId) {
1402
- const snapshot = { perspectiveId: nextId, settings: normalized, updatedAt: Date.now() };
1417
+ const previousSnapshot = readPerspectiveSnapshot(perspectiveTableId) ?? initialSnapshotRef.current;
1418
+ const preserveClearedFilter = options?.preserveAdvancedFilter && !getCurrentSettings().filters && !previousSnapshot?.settings.filters;
1419
+ const snapshotSettings = preserveClearedFilter ? { ...normalized, filters: void 0 } : normalized;
1420
+ const snapshot = { perspectiveId: nextId, settings: snapshotSettings, updatedAt: Date.now() };
1403
1421
  writePerspectiveSnapshot(perspectiveTableId, snapshot);
1404
1422
  initialSnapshotRef.current = snapshot;
1405
1423
  } else if (normalized.columnSizing && Object.keys(normalized.columnSizing).length) {
@@ -1450,18 +1468,36 @@ function DataTable({
1450
1468
  if (!perspectiveTableId) return;
1451
1469
  if (snapshotHydratedTableRef.current === perspectiveTableId) return;
1452
1470
  snapshotHydratedTableRef.current = perspectiveTableId;
1471
+ hydratedAdvancedFilterRestoredRef.current = false;
1453
1472
  const snapshot = readPerspectiveSnapshot(perspectiveTableId);
1454
1473
  if (!snapshot) return;
1455
1474
  initialSnapshotRef.current = snapshot;
1456
1475
  hydratedSnapshotRef.current = snapshot;
1457
- const preserveAdvancedFilter = !!advancedFilter?.onApplyTree;
1476
+ const restoredTree = isPersistedFilterTree(snapshot.settings.filters) ? deserializeTreeFromPersist(snapshot.settings.filters) : null;
1477
+ const preserveAdvancedFilter = !!advancedFilter?.onApplyTree && (advancedFilter.value.root.children.length > 0 || !restoredTree?.root.children.length);
1458
1478
  applyPerspectiveSettings(
1459
1479
  snapshot.settings,
1460
1480
  snapshot.perspectiveId ?? null,
1461
1481
  { preserveAdvancedFilter }
1462
1482
  );
1483
+ hydratedAdvancedFilterRestoredRef.current = !!advancedFilter?.onApplyTree && !preserveAdvancedFilter;
1463
1484
  initialPerspectiveAppliedRef.current = true;
1464
1485
  }, [perspectiveTableId, applyPerspectiveSettings, advancedFilter]);
1486
+ const previousAdvancedFilterRef = React.useRef(null);
1487
+ const advancedFilterHasRules = !!advancedFilter?.value.root.children.length;
1488
+ React.useEffect(() => {
1489
+ const previous = previousAdvancedFilterRef.current;
1490
+ previousAdvancedFilterRef.current = { tableId: perspectiveTableId, hasRules: advancedFilterHasRules };
1491
+ if (!perspectiveTableId || !advancedFilter?.onApplyTree) return;
1492
+ if (previous?.tableId !== perspectiveTableId || !previous.hasRules || advancedFilterHasRules) return;
1493
+ const snapshot = readPerspectiveSnapshot(perspectiveTableId) ?? initialSnapshotRef.current;
1494
+ if (!snapshot) return;
1495
+ const settings = { ...snapshot.settings };
1496
+ delete settings.filters;
1497
+ const clearedSnapshot = { ...snapshot, settings, updatedAt: Date.now() };
1498
+ writePerspectiveSnapshot(perspectiveTableId, clearedSnapshot);
1499
+ initialSnapshotRef.current = clearedSnapshot;
1500
+ }, [perspectiveTableId, advancedFilterHasRules, advancedFilter?.onApplyTree]);
1465
1501
  const perspectiveQueryKey = ["table-perspectives", perspectiveTableId];
1466
1502
  const rolePerspectivesForLocking = React.useMemo(
1467
1503
  () => perspectiveData?.manageableRolePerspectives ?? perspectiveData?.rolePerspectives ?? [],
@@ -1873,15 +1909,21 @@ function DataTable({
1873
1909
  React.useLayoutEffect(() => {
1874
1910
  if (!canUsePerspectives) return;
1875
1911
  if (!perspectiveTableId) return;
1912
+ if ((initialSnapshotRef.current || initialPerspectiveAppliedRef.current) && serverReconciledTableRef.current === perspectiveTableId) return;
1876
1913
  const source = perspectiveData ?? perspectiveConfig?.initialState?.response;
1877
1914
  if (!source) return;
1878
1915
  let orphanedSnapshotDropped = false;
1916
+ const currentFilters = pendingPerspectiveFiltersRef.current ? pendingPerspectiveFiltersRef.current.filters : getCurrentSettings().filters;
1917
+ const restoredFilterStillActive = hydratedAdvancedFilterRestoredRef.current && diffPerspectiveSettings(
1918
+ { filters: currentFilters },
1919
+ { filters: hydratedSnapshotRef.current?.settings.filters }
1920
+ ).length === 0;
1921
+ const preserveAdvancedFilter = !!advancedFilter?.onApplyTree && !restoredFilterStillActive;
1879
1922
  const tryResolve = (id) => {
1880
1923
  if (!id) return void 0;
1881
1924
  return source.perspectives.find((p) => p.id === id) ?? source.rolePerspectives.find((p) => p.id === id);
1882
1925
  };
1883
1926
  if (initialSnapshotRef.current || initialPerspectiveAppliedRef.current) {
1884
- if (serverReconciledTableRef.current === perspectiveTableId) return;
1885
1927
  serverReconciledTableRef.current = perspectiveTableId;
1886
1928
  const hydrated = hydratedSnapshotRef.current;
1887
1929
  const snapshot = hydrated && (!activePerspectiveId || activePerspectiveId === hydrated.perspectiveId) ? hydrated : null;
@@ -1897,7 +1939,7 @@ function DataTable({
1897
1939
  ).length > 0;
1898
1940
  if (serverIsNewer && settingsDiffer) {
1899
1941
  applyPerspectiveSettings(local.settings, local.id, {
1900
- preserveAdvancedFilter: !!advancedFilter?.onApplyTree
1942
+ preserveAdvancedFilter
1901
1943
  });
1902
1944
  initialPerspectiveAppliedRef.current = true;
1903
1945
  }
@@ -1928,15 +1970,15 @@ function DataTable({
1928
1970
  applyPerspectiveSettings(
1929
1971
  target.settings,
1930
1972
  target.id,
1931
- orphanedSnapshotDropped ? { preserveAdvancedFilter: !!advancedFilter?.onApplyTree } : void 0
1973
+ orphanedSnapshotDropped ? { preserveAdvancedFilter } : void 0
1932
1974
  );
1933
1975
  } else if (orphanedSnapshotDropped) {
1934
1976
  applyPerspectiveSettings({}, null, {
1935
- preserveAdvancedFilter: !!advancedFilter?.onApplyTree
1977
+ preserveAdvancedFilter
1936
1978
  });
1937
1979
  }
1938
1980
  initialPerspectiveAppliedRef.current = true;
1939
- }, [canUsePerspectives, perspectiveData, perspectiveTableId, perspectiveConfig, applyPerspectiveSettings, activePerspectiveId, advancedFilter?.onApplyTree]);
1981
+ }, [canUsePerspectives, perspectiveData, perspectiveTableId, perspectiveConfig, applyPerspectiveSettings, activePerspectiveId, advancedFilter?.onApplyTree, getCurrentSettings]);
1940
1982
  const scrollTableIntoView = React.useCallback(() => {
1941
1983
  const rect = containerRef.current?.getBoundingClientRect();
1942
1984
  if (!rect || rect.top >= 0) return;
@@ -2893,6 +2935,16 @@ function DataTable({
2893
2935
  ) : null
2894
2936
  ] }) });
2895
2937
  }
2938
+ const DataTableFallback = DataTableImpl;
2939
+ function DataTable(props) {
2940
+ const extensionTableId = props.perspective?.tableId ?? props.extensionTableId ?? (props.injectionSpotId?.startsWith("data-table:") ? props.injectionSpotId.slice("data-table:".length) : null);
2941
+ const handle = props.replacementHandle ?? ComponentReplacementHandles.dataTable(extensionTableId ?? "unknown");
2942
+ const Resolved = useRegisteredComponent(
2943
+ handle,
2944
+ DataTableFallback
2945
+ );
2946
+ return /* @__PURE__ */ jsx(Resolved, { ...props });
2947
+ }
2896
2948
  export {
2897
2949
  DataTable,
2898
2950
  clearAllPerspectiveState,