@flamingo-stack/openframe-frontend-core 0.0.852 → 0.0.854

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 (163) hide show
  1. package/dist/build/export-map.json +1 -1
  2. package/dist/{chunk-G7GF4PWA.cjs → chunk-2IJMHBOK.cjs} +3 -3
  3. package/dist/{chunk-IC4IJJB2.cjs → chunk-3WXHPD6N.cjs} +55 -55
  4. package/dist/{chunk-UV45EPXN.cjs → chunk-4LTLEUY5.cjs} +5 -5
  5. package/dist/{chunk-BNHTBLAG.cjs → chunk-4O5N5CGK.cjs} +17 -17
  6. package/dist/{chunk-COWMXUHN.cjs → chunk-4SSJ3T3N.cjs} +3 -1
  7. package/dist/{chunk-IUDBG3HJ.cjs → chunk-5HMENXSR.cjs} +3 -3
  8. package/dist/{chunk-COJG2ATY.cjs → chunk-5OENPMRU.cjs} +43 -43
  9. package/dist/{chunk-IO6NN3SL.cjs → chunk-5TXE7BXT.cjs} +46 -46
  10. package/dist/{chunk-EIACEY2L.cjs → chunk-63ZV3UGG.cjs} +10 -10
  11. package/dist/{chunk-HTYJUMFB.cjs → chunk-6DCUGIGU.cjs} +2 -2
  12. package/dist/{chunk-GEZ3UXCA.cjs → chunk-6XU3RCK3.cjs} +18 -18
  13. package/dist/{chunk-3ZQESOPJ.cjs → chunk-7J5NSWJG.cjs} +87 -87
  14. package/dist/{chunk-MXY2OGEV.cjs → chunk-7MOBD44H.cjs} +28 -28
  15. package/dist/{chunk-U767P4JU.cjs → chunk-ACPOJE32.cjs} +9 -9
  16. package/dist/{chunk-CHGIACIP.cjs → chunk-BAA3YCLT.cjs} +72 -72
  17. package/dist/{chunk-VUHHLRAY.cjs → chunk-D2XGRWPW.cjs} +73 -73
  18. package/dist/{chunk-5RMBLOJ5.cjs → chunk-EIHMPQRQ.cjs} +9 -9
  19. package/dist/{chunk-3SJVF3HW.cjs → chunk-ETQNMR2K.cjs} +5 -5
  20. package/dist/{chunk-2W22LY3W.cjs → chunk-FBANHIAG.cjs} +9 -9
  21. package/dist/{chunk-P63CRPWI.cjs → chunk-FJXSKMQJ.cjs} +20 -20
  22. package/dist/{chunk-QOW2GP3V.cjs → chunk-GVHCQRUE.cjs} +21 -21
  23. package/dist/{chunk-KNN2GCMW.cjs → chunk-H3GBPYPS.cjs} +18 -18
  24. package/dist/{chunk-FW7RKWPY.cjs → chunk-JKBNK5U6.cjs} +3 -3
  25. package/dist/{chunk-W7OFAROY.cjs → chunk-KA3L6D5N.cjs} +3 -3
  26. package/dist/{chunk-USPUZO53.cjs → chunk-LQDAB5V5.cjs} +4 -4
  27. package/dist/{chunk-KHOB3TES.cjs → chunk-ONYANAMA.cjs} +4 -4
  28. package/dist/{chunk-D3H6AI7O.cjs → chunk-PRJZMZ3X.cjs} +1182 -746
  29. package/dist/chunk-QD727QFI.cjs +62 -0
  30. package/dist/{chunk-X3WIOMEE.cjs → chunk-RMI6O3AE.cjs} +32 -32
  31. package/dist/{chunk-MFDRCTM3.cjs → chunk-S5P6CF5R.cjs} +47 -47
  32. package/dist/{chunk-477P6AQH.cjs → chunk-TMPLHUUU.cjs} +6 -6
  33. package/dist/{chunk-UO2EJGRJ.cjs → chunk-UPQ4MAR7.cjs} +14 -14
  34. package/dist/{chunk-LFEURIZN.cjs → chunk-VMAOZA2V.cjs} +215 -215
  35. package/dist/{chunk-NMWLXHFH.cjs → chunk-YY3WP2YQ.cjs} +30 -30
  36. package/dist/{chunk-WFAZMASF.cjs → chunk-ZDPPNHA2.cjs} +3 -3
  37. package/dist/{cloud-tenants-O33HCUYB.cjs → cloud-tenants-5RNK4ZAT.cjs} +19 -19
  38. package/dist/components/case-studies/index.cjs +33 -33
  39. package/dist/components/chat/index.cjs +13 -13
  40. package/dist/components/contact/index.cjs +20 -20
  41. package/dist/components/docs/index.cjs +23 -23
  42. package/dist/components/embeds/index.cjs +20 -20
  43. package/dist/components/faq/index.cjs +21 -21
  44. package/dist/components/features/cloud-tenants/index.d.ts +7 -5
  45. package/dist/components/features/cloud-tenants/index.d.ts.map +1 -1
  46. package/dist/components/features/cloud-tenants/index.js +8 -1
  47. package/dist/components/features/cloud-tenants/single-customer-tenants-table-view.d.ts +26 -0
  48. package/dist/components/features/cloud-tenants/single-customer-tenants-table-view.d.ts.map +1 -0
  49. package/dist/components/features/cloud-tenants/single-customer-tenants-table-view.js +176 -0
  50. package/dist/components/features/cloud-tenants/tenant-access-cell.d.ts +4 -2
  51. package/dist/components/features/cloud-tenants/tenant-access-cell.d.ts.map +1 -1
  52. package/dist/components/features/cloud-tenants/tenant-access-cell.js +3 -2
  53. package/dist/components/features/cloud-tenants/tenant-customer-cell.d.ts +12 -5
  54. package/dist/components/features/cloud-tenants/tenant-customer-cell.d.ts.map +1 -1
  55. package/dist/components/features/cloud-tenants/tenant-customer-cell.js +38 -7
  56. package/dist/components/features/cloud-tenants/tenant-licenses-cell.d.ts +7 -0
  57. package/dist/components/features/cloud-tenants/tenant-licenses-cell.d.ts.map +1 -0
  58. package/dist/components/features/cloud-tenants/tenant-licenses-cell.js +17 -0
  59. package/dist/components/features/cloud-tenants/tenant-presentation.d.ts +19 -0
  60. package/dist/components/features/cloud-tenants/tenant-presentation.d.ts.map +1 -1
  61. package/dist/components/features/cloud-tenants/tenant-presentation.js +24 -3
  62. package/dist/components/features/cloud-tenants/tenant-row.d.ts +35 -8
  63. package/dist/components/features/cloud-tenants/tenant-row.d.ts.map +1 -1
  64. package/dist/components/features/cloud-tenants/tenants-table-columns.d.ts +9 -3
  65. package/dist/components/features/cloud-tenants/tenants-table-columns.d.ts.map +1 -1
  66. package/dist/components/features/cloud-tenants/tenants-table-columns.js +5 -3
  67. package/dist/components/features/cloud-tenants/tenants-table-view.d.ts +27 -6
  68. package/dist/components/features/cloud-tenants/tenants-table-view.d.ts.map +1 -1
  69. package/dist/components/features/cloud-tenants/tenants-table-view.js +75 -17
  70. package/dist/components/features/devices/device-directory.d.ts +18 -0
  71. package/dist/components/features/devices/device-directory.d.ts.map +1 -0
  72. package/dist/components/features/devices/device-directory.js +35 -0
  73. package/dist/components/features/devices/devices-filter-toolbar.d.ts +3 -1
  74. package/dist/components/features/devices/devices-filter-toolbar.d.ts.map +1 -1
  75. package/dist/components/features/devices/devices-filter-toolbar.js +2 -1
  76. package/dist/components/features/devices/devices-panel-view.d.ts +3 -1
  77. package/dist/components/features/devices/devices-panel-view.d.ts.map +1 -1
  78. package/dist/components/features/devices/devices-panel-view.js +11 -2
  79. package/dist/components/features/devices/devices-table-columns.d.ts +17 -3
  80. package/dist/components/features/devices/devices-table-columns.d.ts.map +1 -1
  81. package/dist/components/features/devices/devices-table-columns.js +80 -10
  82. package/dist/components/features/devices/index.d.ts +1 -0
  83. package/dist/components/features/devices/index.d.ts.map +1 -1
  84. package/dist/components/features/devices/index.js +8 -0
  85. package/dist/components/features/devices/types.d.ts +14 -0
  86. package/dist/components/features/devices/types.d.ts.map +1 -1
  87. package/dist/components/features/devices/use-tag-filter-modal.d.ts +1 -0
  88. package/dist/components/features/devices/use-tag-filter-modal.d.ts.map +1 -1
  89. package/dist/components/features/devices/use-tag-filter-modal.js +13 -8
  90. package/dist/components/features/index.cjs +33 -19
  91. package/dist/components/help-center-pages/index.cjs +120 -120
  92. package/dist/components/help-center-pages/trust-center-page.d.ts +1 -1
  93. package/dist/components/index.cjs +96 -76
  94. package/dist/components/meeting-scheduler/index.cjs +82 -82
  95. package/dist/components/navigation/app-layout.js +2 -2
  96. package/dist/components/navigation/index.cjs +22 -22
  97. package/dist/components/onboarding-guides/index.cjs +22 -22
  98. package/dist/components/platform/tool-badge-mark.d.ts +1 -1
  99. package/dist/components/product-demo/index.cjs +30 -30
  100. package/dist/components/related-content/index.cjs +8 -8
  101. package/dist/components/tickets/index.cjs +24 -24
  102. package/dist/components/ui/data-attribution.d.ts +8 -3
  103. package/dist/components/ui/data-attribution.d.ts.map +1 -1
  104. package/dist/components/ui/data-attribution.js +17 -6
  105. package/dist/components/ui/date-picker.d.ts +2 -0
  106. package/dist/components/ui/date-picker.d.ts.map +1 -1
  107. package/dist/components/ui/date-picker.js +19 -6
  108. package/dist/components/ui/index.cjs +25 -19
  109. package/dist/components/ui/index.d.ts +1 -0
  110. package/dist/components/ui/index.d.ts.map +1 -1
  111. package/dist/components/ui/index.js +1 -0
  112. package/dist/{devices-2RGGMC2B.cjs → devices-44O5PZOW.cjs} +20 -20
  113. package/dist/{embeddable-chat-SJEEJI4N.cjs → embeddable-chat-W3CLMGC3.cjs} +90 -90
  114. package/dist/hooks/index.cjs +11 -7
  115. package/dist/hooks/ui/use-content-breakpoint.d.ts +8 -0
  116. package/dist/hooks/ui/use-content-breakpoint.d.ts.map +1 -1
  117. package/dist/hooks/ui/use-content-breakpoint.js +17 -3
  118. package/dist/hooks/ui/use-media-query.d.ts +1 -0
  119. package/dist/hooks/ui/use-media-query.d.ts.map +1 -1
  120. package/dist/hooks/ui/use-media-query.js +3 -1
  121. package/dist/index.cjs +44 -24
  122. package/dist/{knowledge-5TJIXVMD.cjs → knowledge-UUU3WVO3.cjs} +20 -20
  123. package/dist/{logs-HGQOTRKZ.cjs → logs-DFHNSODA.cjs} +26 -26
  124. package/dist/{policies-GWFSLVSM.cjs → policies-RGFUGDBP.cjs} +23 -23
  125. package/dist/{remote-session-SG44CNOK.cjs → remote-session-U3H77HKR.cjs} +10 -10
  126. package/dist/{scripts-PGJNAZF4.cjs → scripts-KOVO5DHW.cjs} +26 -26
  127. package/dist/{software-update-KKLSYHDI.cjs → software-update-4EKHVV5W.cjs} +23 -23
  128. package/dist/{tickets-board-SQY2Y6QV.cjs → tickets-board-YIFOVMDX.cjs} +10 -10
  129. package/dist/types/data-sync.d.ts +4 -1
  130. package/dist/types/data-sync.d.ts.map +1 -1
  131. package/dist/types/data-sync.js +4 -1
  132. package/dist/types/index.cjs +4 -1
  133. package/dist/utils/presentation-for.d.ts +6 -0
  134. package/dist/utils/presentation-for.d.ts.map +1 -0
  135. package/dist/utils/presentation-for.js +7 -0
  136. package/package.json +1 -1
  137. package/src/components/features/cloud-tenants/index.ts +8 -3
  138. package/src/components/features/cloud-tenants/single-customer-tenants-table-view.tsx +234 -0
  139. package/src/components/features/cloud-tenants/tenant-access-cell.tsx +12 -5
  140. package/src/components/features/cloud-tenants/tenant-customer-cell.tsx +48 -12
  141. package/src/components/features/cloud-tenants/tenant-licenses-cell.tsx +28 -0
  142. package/src/components/features/cloud-tenants/tenant-presentation.ts +46 -4
  143. package/src/components/features/cloud-tenants/tenant-row.ts +38 -8
  144. package/src/components/features/cloud-tenants/tenants-table-columns.ts +8 -5
  145. package/src/components/features/cloud-tenants/tenants-table-view.tsx +123 -28
  146. package/src/components/features/devices/device-directory.ts +55 -0
  147. package/src/components/features/devices/devices-filter-toolbar.tsx +4 -1
  148. package/src/components/features/devices/devices-panel-view.tsx +12 -2
  149. package/src/components/features/devices/devices-table-columns.tsx +123 -8
  150. package/src/components/features/devices/index.ts +7 -0
  151. package/src/components/features/devices/types.ts +14 -0
  152. package/src/components/features/devices/use-tag-filter-modal.ts +32 -14
  153. package/src/components/help-center-pages/trust-center-page.tsx +1 -1
  154. package/src/components/navigation/app-layout.tsx +4 -4
  155. package/src/components/platform/tool-badge-mark.ts +1 -1
  156. package/src/components/ui/data-attribution.tsx +20 -8
  157. package/src/components/ui/date-picker.tsx +21 -3
  158. package/src/components/ui/index.ts +2 -0
  159. package/src/hooks/ui/use-content-breakpoint.ts +21 -2
  160. package/src/hooks/ui/use-media-query.ts +1 -0
  161. package/src/types/data-sync.ts +4 -1
  162. package/src/utils/presentation-for.ts +10 -0
  163. package/dist/chunk-UAI4VMEF.cjs +0 -46
@@ -10,9 +10,9 @@ var _chunkFDZOE5TLcjs = require('./chunk-FDZOE5TL.cjs');
10
10
 
11
11
 
12
12
 
13
- var _chunkP63CRPWIcjs = require('./chunk-P63CRPWI.cjs');
14
- require('./chunk-IUDBG3HJ.cjs');
15
- require('./chunk-U767P4JU.cjs');
13
+ var _chunkFJXSKMQJcjs = require('./chunk-FJXSKMQJ.cjs');
14
+ require('./chunk-5HMENXSR.cjs');
15
+ require('./chunk-ACPOJE32.cjs');
16
16
  require('./chunk-KXFIYO4O.cjs');
17
17
  require('./chunk-7HH4Y27U.cjs');
18
18
  require('./chunk-QKCF77NZ.cjs');
@@ -1807,7 +1807,7 @@ require('./chunk-XIBCMGBW.cjs');
1807
1807
  require('./chunk-IGVAPIEW.cjs');
1808
1808
  require('./chunk-WWUSFOJ7.cjs');
1809
1809
  require('./chunk-SKI6NKOE.cjs');
1810
- require('./chunk-W7OFAROY.cjs');
1810
+ require('./chunk-KA3L6D5N.cjs');
1811
1811
  require('./chunk-SSKMGH5O.cjs');
1812
1812
  require('./chunk-S5C3OZCO.cjs');
1813
1813
  require('./chunk-32ASZMTV.cjs');
@@ -1816,7 +1816,7 @@ require('./chunk-4IYNDV3G.cjs');
1816
1816
  require('./chunk-6VOOPMP5.cjs');
1817
1817
  require('./chunk-ORO54HDF.cjs');
1818
1818
  require('./chunk-RUHKTWGB.cjs');
1819
- require('./chunk-COWMXUHN.cjs');
1819
+ require('./chunk-4SSJ3T3N.cjs');
1820
1820
  require('./chunk-YSDB6HGQ.cjs');
1821
1821
  require('./chunk-FX35L4Y6.cjs');
1822
1822
  require('./chunk-KKFDRCQF.cjs');
@@ -1842,7 +1842,7 @@ function buildTicketsBoardFixture(cast) {
1842
1842
  const dana = assignee(cast.person("dana"));
1843
1843
  const alex = assignee(cast.person("alex"));
1844
1844
  return [
1845
- _chunkP63CRPWIcjs.columnFromTicketStatus.call(void 0, "ACTIVE", [
1845
+ _chunkFJXSKMQJcjs.columnFromTicketStatus.call(void 0, "ACTIVE", [
1846
1846
  ticket("tk-1051", "Printer offline at the front desk", "ACTIVE", {
1847
1847
  ticketNumber: "1051",
1848
1848
  agent: "fae",
@@ -1860,7 +1860,7 @@ function buildTicketsBoardFixture(cast) {
1860
1860
  tags: ["onboarding"]
1861
1861
  })
1862
1862
  ]),
1863
- _chunkP63CRPWIcjs.columnFromTicketStatus.call(void 0, "TECH_REQUIRED", [
1863
+ _chunkFJXSKMQJcjs.columnFromTicketStatus.call(void 0, "TECH_REQUIRED", [
1864
1864
  ticket("tk-1049", "Acrobat Pro for Maya", "TECH_REQUIRED", {
1865
1865
  ticketNumber: "1049",
1866
1866
  agent: "fae",
@@ -1884,7 +1884,7 @@ function buildTicketsBoardFixture(cast) {
1884
1884
  escalatedByUser: true
1885
1885
  })
1886
1886
  ]),
1887
- _chunkP63CRPWIcjs.columnFromTicketStatus.call(void 0, "ON_HOLD", [
1887
+ _chunkFJXSKMQJcjs.columnFromTicketStatus.call(void 0, "ON_HOLD", [
1888
1888
  ticket("tk-1046", "Replace the conference room display", "ON_HOLD", {
1889
1889
  ticketNumber: "1046",
1890
1890
  organizationName: _chunkFDZOE5TLcjs.DEMO_ORGANIZATIONS.acme.name,
@@ -1893,7 +1893,7 @@ function buildTicketsBoardFixture(cast) {
1893
1893
  activity: { kind: "waiting-external" }
1894
1894
  })
1895
1895
  ]),
1896
- _chunkP63CRPWIcjs.columnFromTicketStatus.call(void 0,
1896
+ _chunkFJXSKMQJcjs.columnFromTicketStatus.call(void 0,
1897
1897
  "RESOLVED",
1898
1898
  [
1899
1899
  ticket("tk-1048", "Slow laptop", "RESOLVED", {
@@ -1929,7 +1929,7 @@ function TicketsBoardScreen({ compact = false }) {
1929
1929
  const cast = _chunkLHV2R6QUcjs.useProductDemoCast.call(void 0, );
1930
1930
  const board = _react.useMemo.call(void 0, () => buildTicketsBoardFixture(cast), [cast]);
1931
1931
  const columns = compact ? board.filter((column) => column.id !== "ON_HOLD") : board;
1932
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full bg-ods-bg p-[var(--spacing-system-m)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkP63CRPWIcjs.Board, { columns, onChange: noop, initialCollapsed: compact ? void 0 : FOLDED }) });
1932
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full bg-ods-bg p-[var(--spacing-system-m)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkFJXSKMQJcjs.Board, { columns, onChange: noop, initialCollapsed: compact ? void 0 : FOLDED }) });
1933
1933
  }
1934
1934
 
1935
1935
 
@@ -1,11 +1,14 @@
1
1
  /**
2
2
  * Every system a shared page's records are mirrored from, with the name a page shows for it. THE list: `DataSync`,
3
- * `DataSyncLine`'s marks, the docs hook and every host that answers a `sync` read it.
3
+ * `DataSyncLine`'s and `DataSyncIcon`'s marks, the docs hook and every host that answers a `sync` read it.
4
4
  */
5
5
  export declare const DATA_SYNC_SOURCES: {
6
6
  readonly clickup: "ClickUp";
7
7
  readonly hubspot: "HubSpot";
8
8
  readonly github: "GitHub";
9
+ readonly openframe: "OpenFrame";
10
+ readonly stripe: "Stripe";
11
+ readonly quickbooks: "QuickBooks";
9
12
  };
10
13
  export type DataSyncSource = keyof typeof DATA_SYNC_SOURCES;
11
14
  export declare function isDataSyncSource(value: unknown): value is DataSyncSource;
@@ -1 +1 @@
1
- {"version":3,"file":"data-sync.d.ts","sourceRoot":"","sources":["../../src/types/data-sync.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,iBAAiB;;;;CAIpB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAE5D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAExE;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,4CAA4C;IAC5C,MAAM,EAAE,cAAc,CAAC;IACvB,mFAAmF;IACnF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB"}
1
+ {"version":3,"file":"data-sync.d.ts","sourceRoot":"","sources":["../../src/types/data-sync.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,iBAAiB;;;;;;;CAOpB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAE5D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAExE;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,4CAA4C;IAC5C,MAAM,EAAE,cAAc,CAAC;IACvB,mFAAmF;IACnF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB"}
@@ -1,7 +1,10 @@
1
1
  const DATA_SYNC_SOURCES = {
2
2
  clickup: "ClickUp",
3
3
  hubspot: "HubSpot",
4
- github: "GitHub"
4
+ github: "GitHub",
5
+ openframe: "OpenFrame",
6
+ stripe: "Stripe",
7
+ quickbooks: "QuickBooks"
5
8
  };
6
9
  function isDataSyncSource(value) {
7
10
  return typeof value === "string" && Object.prototype.hasOwnProperty.call(DATA_SYNC_SOURCES, value);
@@ -28,7 +28,10 @@ var AVAILABLE_SVG_ICONS = [
28
28
  var DATA_SYNC_SOURCES = {
29
29
  clickup: "ClickUp",
30
30
  hubspot: "HubSpot",
31
- github: "GitHub"
31
+ github: "GitHub",
32
+ openframe: "OpenFrame",
33
+ stripe: "Stripe",
34
+ quickbooks: "QuickBooks"
32
35
  };
33
36
  function isDataSyncSource(value) {
34
37
  return typeof value === "string" && Object.prototype.hasOwnProperty.call(DATA_SYNC_SOURCES, value);
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The entry of a value → presentation table for a value off the wire, `undefined` for one the table does not know.
3
+ * Own keys only: `in` or a bare index would answer `toString` or `constructor` with the prototype's function.
4
+ */
5
+ export declare function presentationFor<K extends string, V>(map: Readonly<Record<K, V>>, key: string | null | undefined): V | undefined;
6
+ //# sourceMappingURL=presentation-for.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentation-for.d.ts","sourceRoot":"","sources":["../../src/utils/presentation-for.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACjD,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAC3B,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC7B,CAAC,GAAG,SAAS,CAEf"}
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ function presentationFor(map, key) {
3
+ return key != null && Object.prototype.hasOwnProperty.call(map, key) ? map[key] : void 0;
4
+ }
5
+ export {
6
+ presentationFor
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.852",
3
+ "version": "0.0.854",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -1,27 +1,32 @@
1
1
  'use client';
2
2
 
3
- export { TenantsTableView } from './tenants-table-view';
4
- export type { TenantsTableViewProps } from './tenants-table-view';
3
+ export { TENANTS_TABLE_FILTER_COLUMNS, TenantsTableView } from './tenants-table-view';
4
+ export type { TenantsTableFilterColumn, TenantsTableFilters, TenantsTableViewProps } from './tenants-table-view';
5
5
  export { TenantCell } from './tenant-cell';
6
6
  export type { TenantCellProps } from './tenant-cell';
7
7
  export { TenantCustomerCell } from './tenant-customer-cell';
8
8
  export type { TenantCustomerCellProps } from './tenant-customer-cell';
9
+ export { TenantLicensesCell } from './tenant-licenses-cell';
10
+ export type { TenantLicensesCellProps } from './tenant-licenses-cell';
9
11
  export { TenantAccessCell } from './tenant-access-cell';
10
12
  export type { TenantAccessCellProps } from './tenant-access-cell';
11
13
  export { TENANT_COLUMNS, TENANTS_TABLE_COLUMNS } from './tenants-table-columns';
12
14
  export type { TenantsTableColumn } from './tenants-table-columns';
13
- export type { TenantRow } from './tenant-row';
15
+ export type { MultiCustomerTenantRow, TenantCustomer, TenantLicenseSummary, TenantRow } from './tenant-row';
14
16
  export {
15
17
  accessStateTag,
16
18
  formatLastRead,
17
19
  lastReadAt,
20
+ licenseSummaryLines,
18
21
  providerMark,
19
22
  TENANT_EMPTY_VALUE,
23
+ unattributedLabel,
20
24
  usersCountLabel,
21
25
  } from './tenant-presentation';
22
26
  export type {
23
27
  ProviderLogo,
24
28
  ProviderMark,
29
+ TenantLicenseLines,
25
30
  TenantStatusTag,
26
31
  TenantAccessState,
27
32
  TenantProvider,
@@ -0,0 +1,234 @@
1
+ 'use client';
2
+
3
+ // Lib main's tenants list, one customer per tenant: `TenantsTableView`'s default variant, the list a host renders while
4
+ // tenant-management-v2 is off. Its markup is lib main's to the class; it is deleted with that variant.
5
+
6
+ import { useMemo } from 'react';
7
+ import { cn } from '../../../utils/cn';
8
+ import { openInNewTab } from '../../../utils/open-in-new-tab';
9
+ import { ArrowRightUpIcon } from '../../icons-v2-generated/arrows/arrow-right-up-icon';
10
+ import { Button } from '../../ui/button';
11
+ import { type ColumnDef, DataTable, dataTableLoadingRows, type Row, useDataTable } from '../../ui/data-table';
12
+ import { useLoadingScope } from '../../ui/loading-scope';
13
+ import { SquareAvatar } from '../../ui/square-avatar';
14
+ import { Tag } from '../../ui/tag';
15
+ import { TruncateText } from '../../ui/truncate-text';
16
+ import { TenantCell } from './tenant-cell';
17
+ import { accessStateTag, formatLastRead, usersCountLabel } from './tenant-presentation';
18
+ import type { TenantRow } from './tenant-row';
19
+ import type { TenantsTableColumn } from './tenants-table-columns';
20
+
21
+ /** Column layout of the single-customer list (Figma 1699-8249: Tenant, Customers, Access, open). */
22
+ const SINGLE_CUSTOMER_TENANT_COLUMNS = {
23
+ tenant: { id: 'tenant', header: 'Tenant', width: 'flex-1 min-w-0' },
24
+ customer: { id: 'customer', header: 'Customers', width: 'w-[220px] content-lg:w-[340px]', hideAt: 'md' },
25
+ access: { id: 'access', header: 'Access', width: 'w-[200px] content-lg:w-[216px]' },
26
+ open: { id: 'open', width: 'w-12 shrink-0 flex-none', hideAt: 'md', align: 'right' },
27
+ } satisfies Record<string, TenantsTableColumn>;
28
+
29
+ const getRowId = (row: TenantRow) => row.id;
30
+
31
+ const columnMeta = ({ width, hideAt, align }: TenantsTableColumn) => ({ width, hideAt, align });
32
+
33
+ /** A tenant still being read: a row the table keys, every field a cell shows empty (each draws its loading form). */
34
+ const loadingTenant = (id: string): TenantRow => ({
35
+ id,
36
+ name: '',
37
+ provider: '',
38
+ customer: { name: '' },
39
+ accessState: '',
40
+ });
41
+
42
+ /** CUSTOMERS column: customer logo + name over the synced user count. */
43
+ function SingleCustomerTenantCustomerCell({
44
+ name,
45
+ imageUrl,
46
+ userCount,
47
+ }: {
48
+ name: string;
49
+ imageUrl?: string | null;
50
+ userCount?: number | null;
51
+ }) {
52
+ // While the tenant is read (a loading scope) the user count is a bar too (its label is never empty).
53
+ const loading = useLoadingScope();
54
+ return (
55
+ <div className="flex min-w-0 flex-col justify-center">
56
+ <div className="flex min-w-0 items-center gap-[var(--spacing-system-xxs)]">
57
+ <SquareAvatar src={imageUrl ?? undefined} alt={name} fallback={name} size="xs" variant="square" />
58
+ <TruncateText>{name}</TruncateText>
59
+ </div>
60
+ <TruncateText variant="h6" tone="secondary" loading={loading}>
61
+ {usersCountLabel(userCount)}
62
+ </TruncateText>
63
+ </div>
64
+ );
65
+ }
66
+
67
+ /** ACCESS column: the access tag over "Last read: ...". */
68
+ function SingleCustomerTenantAccessCell({
69
+ accessState,
70
+ lastReadAt,
71
+ now,
72
+ }: {
73
+ accessState: string;
74
+ lastReadAt?: string | null;
75
+ now?: Date;
76
+ }) {
77
+ // While the tenant is read (a loading scope) the access tag and its last read are their loading forms.
78
+ const loading = useLoadingScope();
79
+ return (
80
+ <div className="flex min-w-0 flex-col justify-center gap-[var(--spacing-system-xxs)]">
81
+ <Tag {...accessStateTag(accessState)} loading={loading} className="self-start" />
82
+ <TruncateText variant="h6" tone="secondary" loading={loading}>
83
+ {`Last read: ${formatLastRead(lastReadAt, now)}`}
84
+ </TruncateText>
85
+ </div>
86
+ );
87
+ }
88
+
89
+ /** The row's open-in-new-tab button: static chrome, drawn real and inert while the row is read. */
90
+ function OpenTenantButton({ href, name }: { href: string; name: string }) {
91
+ const loading = useLoadingScope();
92
+ return (
93
+ // The row itself is the details link; a nested `<a>` is invalid, so this opens the same href.
94
+ <div data-no-row-click className="pointer-events-auto flex items-center justify-end">
95
+ <Button
96
+ onClick={loading ? undefined : openInNewTab(href)}
97
+ disabled={loading}
98
+ variant="outline"
99
+ size="icon"
100
+ leftIcon={<ArrowRightUpIcon className="h-5 w-5" />}
101
+ aria-label={`Open ${name} in new tab`}
102
+ className="bg-ods-card"
103
+ />
104
+ </div>
105
+ );
106
+ }
107
+
108
+ export interface SingleCustomerTenantsTableViewProps {
109
+ /** The default: one customer per tenant, no licences and no funnels. */
110
+ variant?: 'single-customer';
111
+ rows: TenantRow[];
112
+ /** Where a row leads: the row is a link to it, and its last cell opens it in a new tab. */
113
+ getHref: (row: TenantRow) => string;
114
+ /** The search the rows answer; named by the "no match" message. */
115
+ search?: string;
116
+ /** Funnels belong to the multi-customer list. */
117
+ filters?: never;
118
+ /** The rows on screen are the previous result of a search still loading: they dim. */
119
+ isPending?: boolean;
120
+ /** All matching tenants, loaded or not. Default: the rows given. */
121
+ totalCount?: number;
122
+ stickyHeaderOffset?: string;
123
+ skeletonRows?: number;
124
+ hasNextPage?: boolean;
125
+ isFetchingNextPage?: boolean;
126
+ onLoadMore?: () => void;
127
+ /** The clock "Last read" is relative to. Default: the moment of the render. */
128
+ now?: Date;
129
+ }
130
+
131
+ /** The rows a loading tenants table draws when the host states none (a typical page of tenants). */
132
+ const TENANTS_TABLE_SKELETON_ROWS = 20;
133
+
134
+ /** The tenants list: tenant, customer, access state and a way into each tenant's details. */
135
+ export function SingleCustomerTenantsTableView({
136
+ rows,
137
+ getHref,
138
+ search = '',
139
+ isPending = false,
140
+ totalCount,
141
+ stickyHeaderOffset,
142
+ skeletonRows = TENANTS_TABLE_SKELETON_ROWS,
143
+ hasNextPage = false,
144
+ isFetchingNextPage = false,
145
+ onLoadMore,
146
+ now,
147
+ }: SingleCustomerTenantsTableViewProps) {
148
+ const columns = useMemo<ColumnDef<TenantRow>[]>(
149
+ () => [
150
+ {
151
+ id: SINGLE_CUSTOMER_TENANT_COLUMNS.tenant.id,
152
+ header: SINGLE_CUSTOMER_TENANT_COLUMNS.tenant.header,
153
+ cell: ({ row }: { row: Row<TenantRow> }) => (
154
+ <TenantCell provider={row.original.provider} name={row.original.name} domain={row.original.domain} />
155
+ ),
156
+ enableSorting: false,
157
+ meta: columnMeta(SINGLE_CUSTOMER_TENANT_COLUMNS.tenant),
158
+ },
159
+ {
160
+ id: SINGLE_CUSTOMER_TENANT_COLUMNS.customer.id,
161
+ header: SINGLE_CUSTOMER_TENANT_COLUMNS.customer.header,
162
+ cell: ({ row }: { row: Row<TenantRow> }) => (
163
+ <SingleCustomerTenantCustomerCell
164
+ name={row.original.customer.name}
165
+ imageUrl={row.original.customer.imageUrl}
166
+ userCount={row.original.userCount}
167
+ />
168
+ ),
169
+ enableSorting: false,
170
+ meta: columnMeta(SINGLE_CUSTOMER_TENANT_COLUMNS.customer),
171
+ },
172
+ {
173
+ id: SINGLE_CUSTOMER_TENANT_COLUMNS.access.id,
174
+ header: SINGLE_CUSTOMER_TENANT_COLUMNS.access.header,
175
+ cell: ({ row }: { row: Row<TenantRow> }) => (
176
+ <SingleCustomerTenantAccessCell
177
+ accessState={row.original.accessState}
178
+ lastReadAt={row.original.lastReadAt}
179
+ now={now}
180
+ />
181
+ ),
182
+ enableSorting: false,
183
+ meta: columnMeta(SINGLE_CUSTOMER_TENANT_COLUMNS.access),
184
+ },
185
+ {
186
+ id: SINGLE_CUSTOMER_TENANT_COLUMNS.open.id,
187
+ cell: ({ row }: { row: Row<TenantRow> }) => (
188
+ <OpenTenantButton href={getHref(row.original)} name={row.original.name} />
189
+ ),
190
+ enableSorting: false,
191
+ meta: columnMeta(SINGLE_CUSTOMER_TENANT_COLUMNS.open),
192
+ },
193
+ ],
194
+ [getHref, now],
195
+ );
196
+
197
+ // A page's loading scope: the table holds one page of placeholder rows, drawn with the real cells (`loadingCells`).
198
+ const loading = useLoadingScope();
199
+ const loadingRows = useMemo(
200
+ () => (loading ? dataTableLoadingRows(skeletonRows, loadingTenant) : null),
201
+ [loading, skeletonRows],
202
+ );
203
+ const table = useDataTable<TenantRow>({ data: loadingRows ?? rows, columns, getRowId, enableSorting: false });
204
+
205
+ return (
206
+ // Dim, don't unmount, the stale rows while a deferred search refetches.
207
+ <div className={cn('transition-opacity duration-200', isPending && 'opacity-60')}>
208
+ <DataTable table={table}>
209
+ <DataTable.Header
210
+ stickyHeader={stickyHeaderOffset !== undefined}
211
+ stickyHeaderOffset={stickyHeaderOffset}
212
+ rightSlot={<DataTable.RowCount itemName="result" totalCount={totalCount ?? rows.length} loading={loading} />}
213
+ />
214
+ <DataTable.Body
215
+ loadingCells
216
+ skeletonRows={skeletonRows}
217
+ emptyState={{
218
+ title: 'No tenants match',
219
+ description: `Nothing matches "${search}". Try a different name, domain or customer.`,
220
+ }}
221
+ rowClassName="mb-[var(--spacing-system-xxs)]"
222
+ rowHref={getHref}
223
+ />
224
+ {rows.length > 0 && onLoadMore && (
225
+ <DataTable.InfiniteFooter
226
+ hasNextPage={hasNextPage}
227
+ isFetchingNextPage={isFetchingNextPage}
228
+ onLoadMore={onLoadMore}
229
+ />
230
+ )}
231
+ </DataTable>
232
+ </div>
233
+ );
234
+ }
@@ -8,20 +8,27 @@ import { accessStateTag, formatLastRead } from './tenant-presentation';
8
8
  export interface TenantAccessCellProps {
9
9
  accessState: string;
10
10
  lastReadAt?: string | null;
11
+ /** A person paused the sync: said instead of the last read. */
12
+ syncPaused?: boolean;
11
13
  /** The clock "Last read" is relative to. Default: the moment of the render. */
12
14
  now?: Date;
13
15
  }
14
16
 
15
- /** ACCESS column: the access tag over "Last read: ...". */
16
- export function TenantAccessCell({ accessState, lastReadAt, now }: TenantAccessCellProps) {
17
+ /** ACCESS column: the access tag over "Last read: ..." or "Sync paused"; a phone keeps the tag alone (Figma 3033-9343). */
18
+ export function TenantAccessCell({ accessState, lastReadAt, syncPaused = false, now }: TenantAccessCellProps) {
17
19
  // While the tenant is read (a loading scope) the access tag and its last read are their loading forms.
18
20
  const loading = useLoadingScope();
21
+ const line = syncPaused ? 'Sync paused' : lastReadAt ? `Last read: ${formatLastRead(lastReadAt, now)}` : null;
19
22
  return (
20
23
  <div className="flex min-w-0 flex-col justify-center gap-[var(--spacing-system-xxs)]">
21
24
  <Tag {...accessStateTag(accessState)} loading={loading} className="self-start" />
22
- <TruncateText variant="h6" tone="secondary" loading={loading}>
23
- {`Last read: ${formatLastRead(lastReadAt, now)}`}
24
- </TruncateText>
25
+ {(loading || line) && (
26
+ <div className="hidden min-w-0 content-md:block">
27
+ <TruncateText variant="h6" tone="secondary" loading={loading}>
28
+ {line ?? ''}
29
+ </TruncateText>
30
+ </div>
31
+ )}
25
32
  </div>
26
33
  );
27
34
  }
@@ -3,27 +3,63 @@
3
3
  import { useLoadingScope } from '../../ui/loading-scope';
4
4
  import { SquareAvatar } from '../../ui/square-avatar';
5
5
  import { TruncateText } from '../../ui/truncate-text';
6
- import { usersCountLabel } from './tenant-presentation';
6
+ import { lowercaseUsersCountLabel, TENANT_EMPTY_VALUE, unattributedLabel } from './tenant-presentation';
7
+ import type { TenantCustomer } from './tenant-row';
7
8
 
8
9
  export interface TenantCustomerCellProps {
9
- name: string;
10
- /** A URL the browser can load as is. */
11
- imageUrl?: string | null;
10
+ /** In the host's order: the cell names the first and counts the rest. */
11
+ customers: readonly TenantCustomer[];
12
12
  userCount?: number | null;
13
+ /** Users no customer's rule claims: while there are any, the count line says how many, in the warning colour. */
14
+ unattributedUserCount?: number | null;
15
+ /** Nothing has been read from this directory yet: the count line says so. */
16
+ neverRead?: boolean;
13
17
  }
14
18
 
15
- /** CUSTOMERS column: customer logo + name over the synced user count. */
16
- export function TenantCustomerCell({ name, imageUrl, userCount }: TenantCustomerCellProps) {
19
+ /**
20
+ * CUSTOMERS column: the first customer's logo + name and "+N" for the rest, over the synced user count — or
21
+ * "30 of 412 unattributed" while some users belong to no customer, or "Never read" before the first read.
22
+ */
23
+ export function TenantCustomerCell({
24
+ customers,
25
+ userCount,
26
+ unattributedUserCount,
27
+ neverRead = false,
28
+ }: TenantCustomerCellProps) {
17
29
  // While the tenant is read (a loading scope) the user count is a bar too (its label is never empty).
18
30
  const loading = useLoadingScope();
31
+ const [first] = customers;
32
+ const more = customers.length - 1;
33
+ const unattributed = unattributedLabel(unattributedUserCount, userCount);
19
34
  return (
20
35
  <div className="flex min-w-0 flex-col justify-center">
21
- <div className="flex min-w-0 items-center gap-[var(--spacing-system-xxs)]">
22
- <SquareAvatar src={imageUrl ?? undefined} alt={name} fallback={name} size="xs" variant="square" />
23
- <TruncateText>{name}</TruncateText>
24
- </div>
25
- <TruncateText variant="h6" tone="secondary" loading={loading}>
26
- {usersCountLabel(userCount)}
36
+ {first ? (
37
+ <div className="flex min-w-0 items-center gap-[var(--spacing-system-xxs)]">
38
+ <SquareAvatar
39
+ src={first.imageUrl ?? undefined}
40
+ alt={first.name}
41
+ fallback={first.name}
42
+ size="xs"
43
+ variant="square"
44
+ />
45
+ <TruncateText>{first.name}</TruncateText>
46
+ {more > 0 && (
47
+ <span className="shrink-0 text-ods-text-secondary text-h4">
48
+ +{more}
49
+ <span className="sr-only">{more === 1 ? ' more customer' : ' more customers'}</span>
50
+ </span>
51
+ )}
52
+ </div>
53
+ ) : (
54
+ <TruncateText>{TENANT_EMPTY_VALUE}</TruncateText>
55
+ )}
56
+ <TruncateText
57
+ variant="h6"
58
+ tone="secondary"
59
+ loading={loading}
60
+ className={unattributed ? 'text-ods-warning' : undefined}
61
+ >
62
+ {unattributed ?? (neverRead ? 'Never read' : lowercaseUsersCountLabel(userCount))}
27
63
  </TruncateText>
28
64
  </div>
29
65
  );
@@ -0,0 +1,28 @@
1
+ 'use client';
2
+
3
+ import { cn } from '../../../utils/cn';
4
+ import { useLoadingScope } from '../../ui/loading-scope';
5
+ import { TruncateText } from '../../ui/truncate-text';
6
+ import { licenseSummaryLines } from './tenant-presentation';
7
+ import type { TenantLicenseSummary } from './tenant-row';
8
+
9
+ export interface TenantLicensesCellProps {
10
+ licenses?: TenantLicenseSummary | null;
11
+ }
12
+
13
+ /** LICENCES column: assigned of purchased over what is unused (Figma 3025-3536). */
14
+ export function TenantLicensesCell({ licenses }: TenantLicensesCellProps) {
15
+ // While the tenant is read (a loading scope) both lines are bars.
16
+ const loading = useLoadingScope();
17
+ const { value, detail, warning } = licenseSummaryLines(licenses);
18
+ return (
19
+ <div className="flex min-w-0 flex-col justify-center">
20
+ <TruncateText loading={loading}>{value}</TruncateText>
21
+ {(loading || detail) && (
22
+ <TruncateText variant="h6" tone="secondary" loading={loading} className={cn(warning && 'text-ods-warning')}>
23
+ {detail ?? ''}
24
+ </TruncateText>
25
+ )}
26
+ </div>
27
+ );
28
+ }
@@ -11,10 +11,12 @@
11
11
  import type { ComponentType, SVGProps } from 'react';
12
12
  import { formatRelativeTimeFrom } from '../../../utils/date-utils';
13
13
  import { formatDate } from '../../../utils/format-date';
14
+ import { presentationFor } from '../../../utils/presentation-for';
14
15
  import { GoogleLogoIcon } from '../../icons-v2-generated/brand-logos/google-logo-icon';
15
16
  import { Office365LogoIcon } from '../../icons-v2-generated/brand-logos/office-365-logo-icon';
16
17
  import { CodingForkIcon } from '../../icons-v2-generated/coding/coding-fork-icon';
17
18
  import type { TagProps } from '../../ui/tag';
19
+ import type { TenantLicenseSummary } from './tenant-row';
18
20
 
19
21
  /** What a cell shows when it has no value. */
20
22
  export const TENANT_EMPTY_VALUE = '—';
@@ -33,10 +35,6 @@ export type TenantAccessState =
33
35
  /** The directory providers a tenant connects to. */
34
36
  export type TenantProvider = 'MICROSOFT_365' | 'GOOGLE_WORKSPACE';
35
37
 
36
- function presentationFor<K extends string, V>(map: Record<K, V>, key: string | null | undefined): V | undefined {
37
- return key != null && key in map ? map[key as K] : undefined;
38
- }
39
-
40
38
  // Colours read off the Figma list (node 1699-8249): red for the two states that
41
39
  // need the customer's admin, grey for read-only, outline for write-available,
42
40
  // green for write-enabled. NOT_AUTHORISED has no frame of its own
@@ -109,3 +107,47 @@ export function usersCountLabel(count: number | null | undefined): string {
109
107
  if (count == null) return TENANT_EMPTY_VALUE;
110
108
  return `${count} ${count === 1 ? 'User' : 'Users'}`;
111
109
  }
110
+
111
+ /** The multi-customer list's count line (Figma 3025-3536): "227 users", the empty mark before the first read. */
112
+ export function lowercaseUsersCountLabel(count: number | null | undefined): string {
113
+ if (count == null) return TENANT_EMPTY_VALUE;
114
+ return `${count} ${count === 1 ? 'user' : 'users'}`;
115
+ }
116
+
117
+ /**
118
+ * "30 of 412 unattributed" while some synced users belong to no customer (no customer's rule claims them), or `null`
119
+ * while every user is attributed or nothing has been read.
120
+ */
121
+ export function unattributedLabel(
122
+ unattributed: number | null | undefined,
123
+ users: number | null | undefined,
124
+ ): string | null {
125
+ if (unattributed == null || unattributed <= 0) return null;
126
+ return users == null ? `${unattributed} unattributed` : `${unattributed} of ${users} unattributed`;
127
+ }
128
+
129
+ /** The share of purchased seats left unused that the list calls out: every row of Figma 3025-3536 falls on its side. */
130
+ const UNUSED_WARNING_SHARE = 0.1;
131
+
132
+ export interface TenantLicenseLines {
133
+ value: string;
134
+ detail: string | null;
135
+ /** The detail is worth a look: draw it in the warning colour. */
136
+ warning: boolean;
137
+ }
138
+
139
+ /**
140
+ * The LICENCES cell: "118 of 120" over "2 unused"; "88 assigned" over "Total not available" where the provider reports
141
+ * no purchased seats; the empty mark until the numbers have been read.
142
+ */
143
+ export function licenseSummaryLines(summary: TenantLicenseSummary | null | undefined): TenantLicenseLines {
144
+ if (!summary) return { value: TENANT_EMPTY_VALUE, detail: null, warning: false };
145
+ const { assigned, purchased } = summary;
146
+ if (purchased == null) return { value: `${assigned} assigned`, detail: 'Total not available', warning: false };
147
+ const unused = summary.unused ?? purchased - assigned;
148
+ return {
149
+ value: `${assigned} of ${purchased}`,
150
+ detail: unused < 0 ? `${-unused} over` : `${unused} unused`,
151
+ warning: unused > 0 && unused / purchased >= UNUSED_WARNING_SHARE,
152
+ };
153
+ }