@flamingo-stack/openframe-frontend-core 0.0.554-1822.4841.1 → 0.0.554

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 (104) hide show
  1. package/dist/{chunk-M3SHPHQ7.cjs → chunk-2HWN6CYI.cjs} +115 -137
  2. package/dist/chunk-2HWN6CYI.cjs.map +1 -0
  3. package/dist/{chunk-2UVCQJVY.js → chunk-44QNFLHC.js} +5 -5
  4. package/dist/{chunk-7ZMCHZIJ.cjs → chunk-4KIPZULI.cjs} +7 -7
  5. package/dist/{chunk-7ZMCHZIJ.cjs.map → chunk-4KIPZULI.cjs.map} +1 -1
  6. package/dist/{chunk-CGDMM2IJ.js → chunk-5VICQQM7.js} +2 -2
  7. package/dist/{chunk-T2EHJ4GN.js → chunk-67Q4QQBA.js} +6 -6
  8. package/dist/{chunk-JCURBCB7.cjs → chunk-AQFYI3HH.cjs} +9 -9
  9. package/dist/{chunk-JCURBCB7.cjs.map → chunk-AQFYI3HH.cjs.map} +1 -1
  10. package/dist/{chunk-TF4ASBLD.cjs → chunk-CGCQ4HA5.cjs} +11 -11
  11. package/dist/{chunk-TF4ASBLD.cjs.map → chunk-CGCQ4HA5.cjs.map} +1 -1
  12. package/dist/{chunk-6V6J7P75.cjs → chunk-CWLV6DEK.cjs} +26 -26
  13. package/dist/{chunk-6V6J7P75.cjs.map → chunk-CWLV6DEK.cjs.map} +1 -1
  14. package/dist/{chunk-6G3HJCLJ.js → chunk-DXAUNMFM.js} +2 -2
  15. package/dist/{chunk-NENDMVCF.js → chunk-FHS2SA5F.js} +2 -2
  16. package/dist/{chunk-NJX7WVJB.js → chunk-FVXND3SO.js} +2 -2
  17. package/dist/{chunk-T6UFFK3K.cjs → chunk-HO6IG73U.cjs} +62 -62
  18. package/dist/{chunk-T6UFFK3K.cjs.map → chunk-HO6IG73U.cjs.map} +1 -1
  19. package/dist/{chunk-MPJ53DQU.cjs → chunk-J355HL3X.cjs} +86 -86
  20. package/dist/{chunk-MPJ53DQU.cjs.map → chunk-J355HL3X.cjs.map} +1 -1
  21. package/dist/{chunk-SE667HU6.cjs → chunk-JJKK7E66.cjs} +4 -4
  22. package/dist/{chunk-SE667HU6.cjs.map → chunk-JJKK7E66.cjs.map} +1 -1
  23. package/dist/{chunk-HUGKJGXP.js → chunk-P6LUXP5E.js} +2 -2
  24. package/dist/{chunk-TMNMJF5F.cjs → chunk-PAW27EWH.cjs} +20 -20
  25. package/dist/{chunk-TMNMJF5F.cjs.map → chunk-PAW27EWH.cjs.map} +1 -1
  26. package/dist/{chunk-AEJ4VSN7.js → chunk-PDVSL7OW.js} +15 -6
  27. package/dist/chunk-PDVSL7OW.js.map +1 -0
  28. package/dist/{chunk-JS5YP37A.cjs → chunk-PFQVCHDE.cjs} +3 -3
  29. package/dist/{chunk-JS5YP37A.cjs.map → chunk-PFQVCHDE.cjs.map} +1 -1
  30. package/dist/{chunk-2E63H7SH.js → chunk-QA7NU73D.js} +2 -2
  31. package/dist/{chunk-F2W5V5MH.cjs → chunk-QH3QWY3N.cjs} +31 -31
  32. package/dist/{chunk-F2W5V5MH.cjs.map → chunk-QH3QWY3N.cjs.map} +1 -1
  33. package/dist/{chunk-4VALLDF7.js → chunk-RMGX6BVQ.js} +2 -2
  34. package/dist/{chunk-ROUWF3PD.js → chunk-SBXQW72U.js} +4 -4
  35. package/dist/{chunk-P4OGKQYQ.js → chunk-U72YYXBT.js} +4 -4
  36. package/dist/{chunk-CUWKFI7Q.cjs → chunk-ULH6JHWZ.cjs} +27 -18
  37. package/dist/{chunk-CUWKFI7Q.cjs.map → chunk-ULH6JHWZ.cjs.map} +1 -1
  38. package/dist/{chunk-6PH34GDC.js → chunk-VIDE4YOQ.js} +2 -2
  39. package/dist/{chunk-CCV3SQ4W.cjs → chunk-WZWQWZFJ.cjs} +39 -39
  40. package/dist/{chunk-CCV3SQ4W.cjs.map → chunk-WZWQWZFJ.cjs.map} +1 -1
  41. package/dist/{chunk-YR3JVBT3.cjs → chunk-XMB5P2OS.cjs} +5 -5
  42. package/dist/{chunk-YR3JVBT3.cjs.map → chunk-XMB5P2OS.cjs.map} +1 -1
  43. package/dist/{chunk-3XOICFFT.js → chunk-ZGLMZM2I.js} +19 -41
  44. package/dist/{chunk-3XOICFFT.js.map → chunk-ZGLMZM2I.js.map} +1 -1
  45. package/dist/components/case-studies/index.cjs +8 -8
  46. package/dist/components/case-studies/index.js +2 -2
  47. package/dist/components/chat/hooks/use-chat-card-item.d.ts.map +1 -1
  48. package/dist/components/chat/index.cjs +2 -2
  49. package/dist/components/chat/index.js +1 -1
  50. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts.map +1 -1
  51. package/dist/components/contact/index.cjs +3 -3
  52. package/dist/components/contact/index.js +2 -2
  53. package/dist/components/docs/index.cjs +5 -5
  54. package/dist/components/docs/index.js +4 -4
  55. package/dist/components/embeds/index.cjs +3 -3
  56. package/dist/components/embeds/index.js +2 -2
  57. package/dist/components/faq/index.cjs +3 -3
  58. package/dist/components/faq/index.js +2 -2
  59. package/dist/components/features/index.cjs +2 -2
  60. package/dist/components/features/index.js +1 -1
  61. package/dist/components/help-center-pages/index.cjs +22 -22
  62. package/dist/components/help-center-pages/index.js +13 -13
  63. package/dist/components/index.cjs +131 -131
  64. package/dist/components/index.js +11 -11
  65. package/dist/components/meeting-scheduler/index.cjs +28 -28
  66. package/dist/components/meeting-scheduler/index.js +3 -3
  67. package/dist/components/navigation/index.cjs +2 -2
  68. package/dist/components/navigation/index.js +1 -1
  69. package/dist/components/onboarding-guides/index.cjs +5 -5
  70. package/dist/components/onboarding-guides/index.js +4 -4
  71. package/dist/components/related-content/index.cjs +3 -3
  72. package/dist/components/related-content/index.js +2 -2
  73. package/dist/components/shared/dev-section/dev-section-view.d.ts.map +1 -1
  74. package/dist/components/tickets/index.cjs +6 -6
  75. package/dist/components/tickets/index.js +5 -5
  76. package/dist/components/ui/index.cjs +2 -2
  77. package/dist/components/ui/index.js +1 -1
  78. package/dist/index.cjs +2 -2
  79. package/dist/index.js +1 -1
  80. package/dist/utils/dev-sections/openframe-dev-sections.d.ts +7 -0
  81. package/dist/utils/dev-sections/openframe-dev-sections.d.ts.map +1 -1
  82. package/dist/utils/index.cjs +8 -1
  83. package/dist/utils/index.cjs.map +1 -1
  84. package/dist/utils/index.js +8 -1
  85. package/dist/utils/index.js.map +1 -1
  86. package/package.json +1 -1
  87. package/src/components/chat/hooks/use-chat-card-item.ts +12 -22
  88. package/src/components/chat/stream/use-chat-stream-reducer.ts +1 -36
  89. package/src/components/shared/dev-section/dev-section-view.tsx +27 -7
  90. package/src/utils/dev-sections/openframe-dev-sections.ts +15 -2
  91. package/dist/chunk-AEJ4VSN7.js.map +0 -1
  92. package/dist/chunk-M3SHPHQ7.cjs.map +0 -1
  93. /package/dist/{chunk-2UVCQJVY.js.map → chunk-44QNFLHC.js.map} +0 -0
  94. /package/dist/{chunk-CGDMM2IJ.js.map → chunk-5VICQQM7.js.map} +0 -0
  95. /package/dist/{chunk-T2EHJ4GN.js.map → chunk-67Q4QQBA.js.map} +0 -0
  96. /package/dist/{chunk-6G3HJCLJ.js.map → chunk-DXAUNMFM.js.map} +0 -0
  97. /package/dist/{chunk-NENDMVCF.js.map → chunk-FHS2SA5F.js.map} +0 -0
  98. /package/dist/{chunk-NJX7WVJB.js.map → chunk-FVXND3SO.js.map} +0 -0
  99. /package/dist/{chunk-HUGKJGXP.js.map → chunk-P6LUXP5E.js.map} +0 -0
  100. /package/dist/{chunk-2E63H7SH.js.map → chunk-QA7NU73D.js.map} +0 -0
  101. /package/dist/{chunk-4VALLDF7.js.map → chunk-RMGX6BVQ.js.map} +0 -0
  102. /package/dist/{chunk-ROUWF3PD.js.map → chunk-SBXQW72U.js.map} +0 -0
  103. /package/dist/{chunk-P4OGKQYQ.js.map → chunk-U72YYXBT.js.map} +0 -0
  104. /package/dist/{chunk-6PH34GDC.js.map → chunk-VIDE4YOQ.js.map} +0 -0
@@ -6,7 +6,7 @@ import {
6
6
  resolveExternalNavigation,
7
7
  resolveSourceIcon,
8
8
  stripSameOriginToPath
9
- } from "./chunk-3XOICFFT.js";
9
+ } from "./chunk-ZGLMZM2I.js";
10
10
  import {
11
11
  contentFetch,
12
12
  useDebounce
@@ -370,4 +370,4 @@ export {
370
370
  resolveSearchResultAction,
371
371
  useDocSearch
372
372
  };
373
- //# sourceMappingURL=chunk-4VALLDF7.js.map
373
+ //# sourceMappingURL=chunk-RMGX6BVQ.js.map
@@ -3,11 +3,11 @@ import {
3
3
  FileDownloadCard,
4
4
  GoogleSheetsViewer,
5
5
  PdfViewer
6
- } from "./chunk-2E63H7SH.js";
6
+ } from "./chunk-QA7NU73D.js";
7
7
  import {
8
8
  DocSearchBar,
9
9
  useDocSearch
10
- } from "./chunk-4VALLDF7.js";
10
+ } from "./chunk-RMGX6BVQ.js";
11
11
  import {
12
12
  DEFAULT_FOLDER_INDEX_FILE,
13
13
  FigmaEmbed,
@@ -19,7 +19,7 @@ import {
19
19
  findDocNodeByPath,
20
20
  getDocAncestorNodeIds,
21
21
  stripFolderIndexFromPath
22
- } from "./chunk-3XOICFFT.js";
22
+ } from "./chunk-ZGLMZM2I.js";
23
23
  import {
24
24
  HUB_HEADER_OFFSET_PX,
25
25
  contentFetch,
@@ -1845,4 +1845,4 @@ export {
1845
1845
  EmbedSkeleton,
1846
1846
  DocsHubPage
1847
1847
  };
1848
- //# sourceMappingURL=chunk-ROUWF3PD.js.map
1848
+ //# sourceMappingURL=chunk-SBXQW72U.js.map
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  ArticleAuthorByline,
4
4
  DetailPageSkeleton
5
- } from "./chunk-CGDMM2IJ.js";
5
+ } from "./chunk-5VICQQM7.js";
6
6
  import {
7
7
  DocSearchBar,
8
8
  useDocSearch
9
- } from "./chunk-4VALLDF7.js";
9
+ } from "./chunk-RMGX6BVQ.js";
10
10
  import {
11
11
  useSelfFetch
12
12
  } from "./chunk-OR67WC32.js";
@@ -26,7 +26,7 @@ import {
26
26
  toStripProfile,
27
27
  useCaptions,
28
28
  useVideoWarmup
29
- } from "./chunk-3XOICFFT.js";
29
+ } from "./chunk-ZGLMZM2I.js";
30
30
  import {
31
31
  buildOgPlaceholderUrl
32
32
  } from "./chunk-VGLDA77S.js";
@@ -398,4 +398,4 @@ export {
398
398
  useOnboardingGuide,
399
399
  useOnboardingGuideSections
400
400
  };
401
- //# sourceMappingURL=chunk-P4OGKQYQ.js.map
401
+ //# sourceMappingURL=chunk-U72YYXBT.js.map
@@ -10,7 +10,7 @@
10
10
 
11
11
 
12
12
 
13
- var _chunkM3SHPHQ7cjs = require('./chunk-M3SHPHQ7.cjs');
13
+ var _chunk2HWN6CYIcjs = require('./chunk-2HWN6CYI.cjs');
14
14
 
15
15
 
16
16
 
@@ -30,7 +30,7 @@ var _chunkKI4EGKNVcjs = require('./chunk-KI4EGKNV.cjs');
30
30
  var _react = require('react');
31
31
  var _jsxruntime = require('react/jsx-runtime');
32
32
  function DevSectionView({ sectionKey, hero, preControls, children, showHeading = true }) {
33
- const section = _chunkM3SHPHQ7cjs.OPENFRAME_DEV_SECTIONS[sectionKey];
33
+ const section = _chunk2HWN6CYIcjs.OPENFRAME_DEV_SECTIONS[sectionKey];
34
34
  const router = _chunkSSJNN5BScjs.useRouter.call(void 0, );
35
35
  const pathname = _chunkSSJNN5BScjs.usePathname.call(void 0, );
36
36
  const searchParams = _chunkSSJNN5BScjs.useSearchParams.call(void 0, );
@@ -45,9 +45,18 @@ function DevSectionView({ sectionKey, hero, preControls, children, showHeading =
45
45
  const handleSearchSubmit = (value) => {
46
46
  if (!search) return;
47
47
  const params = new URLSearchParams(searchParams.toString());
48
- if (value.trim()) params.set(search.paramKey, value.trim());
49
- else params.delete(search.paramKey);
50
- router.replace(`${pathname}?${params.toString()}`, { scroll: false });
48
+ if (value.trim()) {
49
+ params.set(search.paramKey, value.trim());
50
+ } else {
51
+ params.delete(search.paramKey);
52
+ for (const key of _nullishCoalesce(search.clearParamKeys, () => ( []))) params.delete(key);
53
+ }
54
+ const qs = params.toString();
55
+ router.replace(qs ? `${pathname}?${qs}` : pathname, { scroll: false });
56
+ };
57
+ const handleSearchChange = (value) => {
58
+ setSearchValue(value);
59
+ if (value === "" && currentSearch !== "") handleSearchSubmit("");
51
60
  };
52
61
  const handleFilterChange = (value) => {
53
62
  if (!filter) return;
@@ -70,17 +79,17 @@ function DevSectionView({ sectionKey, hero, preControls, children, showHeading =
70
79
  preControls,
71
80
  (search || filter) && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-4", children: [
72
81
  search && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
73
- _chunkM3SHPHQ7cjs.SearchInput,
82
+ _chunk2HWN6CYIcjs.SearchInput,
74
83
  {
75
84
  showDropdown: false,
76
85
  placeholder: search.placeholder,
77
86
  value: searchValue,
78
- onChange: setSearchValue,
87
+ onChange: handleSearchChange,
79
88
  onSubmit: handleSearchSubmit
80
89
  }
81
90
  ),
82
91
  filter && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
83
- _chunkM3SHPHQ7cjs.StatusFilterComponent,
92
+ _chunk2HWN6CYIcjs.StatusFilterComponent,
84
93
  {
85
94
  selectedStatus: currentFilterValue,
86
95
  onStatusChange: handleFilterChange,
@@ -105,7 +114,7 @@ function DevSectionPage({
105
114
  shell = true
106
115
  }) {
107
116
  const router = _chunkSSJNN5BScjs.useRouter.call(void 0, );
108
- const section = _chunkM3SHPHQ7cjs.OPENFRAME_DEV_SECTIONS[sectionKey];
117
+ const section = _chunk2HWN6CYIcjs.OPENFRAME_DEV_SECTIONS[sectionKey];
109
118
  const backCfg = backButton === false ? void 0 : {
110
119
  label: _nullishCoalesce((backButton ? backButton.label : void 0), () => ( "Back to home")),
111
120
  onClick: () => router.push(_nullishCoalesce((backButton ? backButton.href : void 0), () => ( "/")))
@@ -118,7 +127,7 @@ function DevSectionPage({
118
127
  // duplicate title. (The hero icon is intentionally dropped: TitleBlock is
119
128
  // frozen and renders title-only.)
120
129
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
121
- _chunkM3SHPHQ7cjs.PageLayout,
130
+ _chunk2HWN6CYIcjs.PageLayout,
122
131
  {
123
132
  title: _nullishCoalesce(title, () => ( section.hero.title)),
124
133
  subtitle: _nullishCoalesce(subtitle, () => ( section.hero.description)),
@@ -129,7 +138,7 @@ function DevSectionPage({
129
138
  }
130
139
  )
131
140
  );
132
- return shell ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.PageShell, { children: inner }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "page-shell-content", children: inner });
141
+ return shell ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.PageShell, { children: inner }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "page-shell-content", children: inner });
133
142
  }
134
143
 
135
144
  // src/components/shared/dev-section/dev-card-row.tsx
@@ -194,9 +203,9 @@ function DeliveryRow({
194
203
  className
195
204
  }) {
196
205
  const taskType = item.taskType;
197
- const typeBadgeLabel = _chunkM3SHPHQ7cjs.TASK_TYPE_LABELS[taskType] || "TASK";
198
- const typeBadgeTextColor = _chunkM3SHPHQ7cjs.TASK_TYPE_TEXT_COLORS[taskType] || "";
199
- const statusBadgeScheme = _chunkM3SHPHQ7cjs.getStatusColorScheme.call(void 0, item.status);
206
+ const typeBadgeLabel = _chunk2HWN6CYIcjs.TASK_TYPE_LABELS[taskType] || "TASK";
207
+ const typeBadgeTextColor = _chunk2HWN6CYIcjs.TASK_TYPE_TEXT_COLORS[taskType] || "";
208
+ const statusBadgeScheme = _chunk2HWN6CYIcjs.getStatusColorScheme.call(void 0, item.status);
200
209
  const relativeTime = getRelativeTime(item.dateUpdated);
201
210
  const subtitle = `ACTIVE ${relativeTime}${item.listNames.length > 0 ? `, ${item.listNames.join(", ")}` : ""}, ${item.id}`;
202
211
  const inner = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col md:flex-row items-start justify-between gap-[12px] md:gap-[16px] w-full", children: [
@@ -208,7 +217,7 @@ function DeliveryRow({
208
217
  ] }),
209
218
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex-shrink-0 self-stretch md:self-start w-full md:w-auto flex flex-row md:flex-col items-center md:items-end gap-2", children: [
210
219
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
211
- _chunkM3SHPHQ7cjs.StatusBadge,
220
+ _chunk2HWN6CYIcjs.StatusBadge,
212
221
  {
213
222
  text: item.status.toUpperCase(),
214
223
  colorScheme: statusBadgeScheme,
@@ -217,7 +226,7 @@ function DeliveryRow({
217
226
  }
218
227
  ),
219
228
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
220
- _chunkM3SHPHQ7cjs.StatusBadge,
229
+ _chunk2HWN6CYIcjs.StatusBadge,
221
230
  {
222
231
  text: typeBadgeLabel,
223
232
  variant: "card",
@@ -225,7 +234,7 @@ function DeliveryRow({
225
234
  }
226
235
  ),
227
236
  item.assignees && item.assignees.length > 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
228
- _chunkM3SHPHQ7cjs.AvatarStack,
237
+ _chunk2HWN6CYIcjs.AvatarStack,
229
238
  {
230
239
  size: "xs",
231
240
  className: "ml-auto md:ml-0",
@@ -262,4 +271,4 @@ function DeliveryRow({
262
271
 
263
272
 
264
273
  exports.DevSectionView = DevSectionView; exports.DevSectionPage = DevSectionPage; exports.DevCardRowContent = DevCardRowContent; exports.DevCardRowSkeleton = DevCardRowSkeleton; exports.DevCardRowSkeletonList = DevCardRowSkeletonList; exports.DeliveryRow = DeliveryRow;
265
- //# sourceMappingURL=chunk-CUWKFI7Q.cjs.map
274
+ //# sourceMappingURL=chunk-ULH6JHWZ.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-CUWKFI7Q.cjs","../src/components/shared/dev-section/dev-section-view.tsx","../src/components/shared/dev-section/dev-section-page.tsx","../src/components/shared/dev-section/dev-card-row.tsx","../src/components/shared/delivery/delivery-row.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-CUWKFI7Q.cjs","sourcesContent":[null,"'use client';\n\n/**\n * DevSectionView — the canonical chrome for ANY dev-center section\n * (Roadmap / Delivery / Releases). One component, used in BOTH:\n *\n * - tabbed `/roadmap-and-releases` (compact title mode, no `hero`)\n * - full-page `/roadmap`, `/bug-fixes-and-enhancements`, `/releases`\n * (hero mode with icon + description + back link)\n *\n * Owns: title rendering, the inline search input, the filter pill row,\n * and the URL-param wiring that connects both. The list `children`\n * receive a clean URL contract — they read `?<paramKey>=...` via\n * `useSearchParams()` and refetch on change. No duplicated controls.\n */\n\nimport type { ReactNode } from 'react';\nimport { useState, useEffect } from 'react';\nimport { useRouter, useSearchParams, usePathname } from '../../../embed-shims';\nimport { SearchInput } from '../../ui';\nimport { StatusFilterComponent } from '../../features';\nimport {\n OPENFRAME_DEV_SECTIONS,\n type OpenframeDevSectionKey,\n} from '../../../utils/dev-sections/openframe-dev-sections';\n\nexport interface DevSectionViewProps {\n /** Which section to render — drives title, search, and filter\n * config via the `OPENFRAME_DEV_SECTIONS` registry. */\n sectionKey: OpenframeDevSectionKey;\n /** When set, renders the rich page-level hero (icon + h1 + description).\n * Omit for the compact tab-context heading. */\n hero?: {\n /** Pre-rendered icon JSX. Server components render the icon themselves\n * and pass the element here — function references can't cross the\n * server→client boundary, but React elements can. */\n icon: ReactNode;\n /** Hero title. Falls back to `OPENFRAME_DEV_SECTIONS[sectionKey].hero.title`\n * when omitted, so embedders can override the (OpenFrame-specific) default\n * copy without forking the registry. */\n title?: string;\n description: string;\n };\n /** Optional slot rendered BETWEEN the hero and the search/filter\n * controls. Use this for an entry-action surface that should sit\n * above the list (e.g. the Help Center's \"Open a new ticket\" form).\n * The slot is wrapped in the same `gap-10` flex column so spacing\n * matches the surrounding chrome — callers should NOT add their\n * own top/bottom margin. Renders `null` (no DOM) when omitted. */\n preControls?: ReactNode;\n /** The page-specific list body. Reads URL params written by this\n * component (search input + filter pills). */\n children: ReactNode;\n /** Render this component's own title heading (hero or compact `h2`). Default\n * `true` — the tab context renders the compact heading itself. Pass `false`\n * when the host already renders the title elsewhere (e.g. `DevSectionPage`\n * routes it through the unified `PageLayout` `TitleBlock`): then only the\n * search + filter controls + list render, with no duplicate heading. */\n showHeading?: boolean;\n}\n\nexport function DevSectionView({ sectionKey, hero, preControls, children, showHeading = true }: DevSectionViewProps) {\n const section = OPENFRAME_DEV_SECTIONS[sectionKey];\n const router = useRouter();\n const pathname = usePathname();\n const searchParams = useSearchParams();\n\n const search = section.search;\n const filter = section.filter;\n\n const currentSearch = search ? searchParams.get(search.paramKey) || '' : '';\n const currentFilterValue = filter\n ? searchParams.get(filter.paramKey) || filter.defaultValue\n : '';\n\n // Controlled search-input state — input commits to the URL only on\n // Enter (not on every keystroke), preserving the legacy behavior.\n // Lazy init from URL avoids a brief flash of stale value on first\n // paint after URL-driven re-render (e.g. tab switch).\n const [searchValue, setSearchValue] = useState(() => currentSearch);\n useEffect(() => {\n setSearchValue(currentSearch);\n }, [currentSearch]);\n\n const handleSearchSubmit = (value: string) => {\n if (!search) return;\n const params = new URLSearchParams(searchParams.toString());\n if (value.trim()) params.set(search.paramKey, value.trim());\n else params.delete(search.paramKey);\n router.replace(`${pathname}?${params.toString()}`, { scroll: false });\n };\n\n const handleFilterChange = (value: string) => {\n if (!filter) return;\n const params = new URLSearchParams(searchParams.toString());\n if (value === filter.defaultValue) params.delete(filter.paramKey);\n else params.set(filter.paramKey, value);\n router.replace(`${pathname}?${params.toString()}`, { scroll: false });\n };\n\n return (\n <div className=\"w-full flex flex-col gap-10\">\n {showHeading &&\n (hero ? (\n <div className=\"space-y-4\">\n <h1 className=\"text-h1 tracking-[-1.12px] text-ods-text-primary flex items-center gap-3\">\n {hero.icon}\n {hero.title ?? section.hero.title}\n </h1>\n <p className=\"text-h4 text-ods-text-secondary max-w-3xl\">\n {hero.description}\n </p>\n </div>\n ) : (\n <div className=\"flex items-center justify-between w-full\">\n <h2 className=\"font-['Azeret_Mono'] font-semibold text-[32px] md:text-[40px] lg:text-[48px] leading-[40px] md:leading-[48px] lg:leading-[56px] text-ods-text-primary tracking-[-0.64px] md:tracking-[-0.8px] lg:tracking-[-0.96px]\">\n {section.hero.title}\n <span className=\"text-ods-accent\">:</span>\n </h2>\n </div>\n ))}\n\n {preControls}\n\n {(search || filter) && (\n <div className=\"space-y-4\">\n {search && (\n <SearchInput\n showDropdown={false}\n placeholder={search.placeholder}\n value={searchValue}\n onChange={setSearchValue}\n onSubmit={handleSearchSubmit}\n />\n )}\n {filter && (\n <StatusFilterComponent\n selectedStatus={currentFilterValue}\n onStatusChange={handleFilterChange}\n statusOptions={[...filter.options]}\n />\n )}\n </div>\n )}\n\n {children}\n </div>\n );\n}\n","'use client';\n\n/**\n * DevSectionPage — full-page wrapper for a dev-center section\n * (`/roadmap`, `/bug-fixes-and-enhancements`, `/releases`).\n *\n * Mounts the lib's canonical `PageLayout` directly (no in-app wrapper)\n * so the back-button affordance stays in lockstep with whatever the\n * design system ships — any future lib change to BackButton / TitleBlock\n * propagates automatically.\n *\n * Composition: `PageShell` → `PageLayout` (back-to-home wired) →\n * `DevSectionView` (icon hero + search + filter pills) → list body.\n *\n * Adding a new section is one entry in `OPENFRAME_DEV_SECTIONS` plus a\n * single-line page file mounting this factory with the new key.\n */\n\nimport type { ReactNode } from 'react';\nimport { useRouter } from '../../../embed-shims/next-navigation';\nimport { PageShell, PageLayout } from '../../ui';\nimport { DevSectionView } from './dev-section-view';\nimport {\n OPENFRAME_DEV_SECTIONS,\n type OpenframeDevSectionKey,\n} from '../../../utils/dev-sections/openframe-dev-sections';\n\nexport interface DevSectionPageProps {\n sectionKey: OpenframeDevSectionKey;\n /** The page-specific list body (e.g. `<RoadmapList />`). */\n children: ReactNode;\n /** Optional slot rendered BETWEEN the hero and search/filter — see\n * `DevSectionView.preControls`. Used by surfaces that want an entry\n * action (e.g. Help Center's \"Open a new ticket\" form) above the\n * controls instead of below them. */\n preControls?: ReactNode;\n /** Back-button config — same shape as `LegalDocumentPage` /\n * `ReleaseDetailPage`. Pass `false` to hide entirely. Default\n * `{ label: 'Back to home', href: '/' }` — embedders whose \"home\" isn't `/`\n * should override `href`, or pass `false` if the embed has no home page. */\n backButton?: { label?: string; href?: string } | false;\n /** Override the hero title. Defaults to the (OpenFrame-specific) copy in\n * `OPENFRAME_DEV_SECTIONS[sectionKey].hero.title`. Set this to brand the\n * section for a non-OpenFrame embed. */\n title?: string;\n /** Override the hero subtitle/description. Defaults to\n * `OPENFRAME_DEV_SECTIONS[sectionKey].hero.description`. */\n subtitle?: string;\n /** Render the standalone `<PageShell>` (own `<main>` + bg + max-width). Default\n * `true` — the contract for marketing/hub surfaces with no app shell. Pass\n * `false` when the host layout already provides the page container (e.g.\n * openframe-frontend's `AppLayout` `<main>`): then only the\n * `page-shell-content` padding box is rendered, avoiding a nested `<main>`. */\n shell?: boolean;\n}\n\nexport function DevSectionPage({\n sectionKey,\n children,\n preControls,\n backButton,\n title,\n subtitle,\n shell = true,\n}: DevSectionPageProps) {\n const router = useRouter();\n const section = OPENFRAME_DEV_SECTIONS[sectionKey];\n\n // Back-button config — mirrors LegalDocumentPage / ReleaseDetailPage.\n // Default: { label: 'Back to home', href: '/' }. Pass `false` to hide.\n // After `backButton &&` narrowing, inner type is `{ label?, href? } |\n // undefined`; don't re-compare to `false` (TS2367).\n const backCfg =\n backButton === false\n ? undefined\n : {\n label: (backButton ? backButton.label : undefined) ?? 'Back to home',\n onClick: () => router.push((backButton ? backButton.href : undefined) ?? '/'),\n };\n\n const inner = (\n // Unified header: title/description route through the canonical (frozen)\n // `PageLayout` `TitleBlock` (text-h2) — same as FAQ / Legal / detail pages —\n // so every help-center surface shares one header. `DevSectionView` then\n // renders ONLY its search + filter controls (`showHeading={false}`), no\n // duplicate title. (The hero icon is intentionally dropped: TitleBlock is\n // frozen and renders title-only.)\n <PageLayout\n title={title ?? section.hero.title}\n subtitle={subtitle ?? section.hero.description}\n titleSize=\"h1\"\n titleWrap\n backButton={backCfg}\n >\n <DevSectionView sectionKey={sectionKey} showHeading={false} preControls={preControls}>\n {children}\n </DevSectionView>\n </PageLayout>\n );\n\n // `shell` true → standalone `<PageShell>` (own <main> + bg + max-width).\n // false → padding-only box (no nested <main>) for hosts whose layout already\n // provides the container; both consume the host's `--page-shell-*` vars.\n return shell ? <PageShell>{inner}</PageShell> : <div className=\"page-shell-content\">{inner}</div>;\n}\n","'use client';\n\n/**\n * Shared row chrome for any `DevSectionPage` list (delivery, tickets,\n * future sections). One source of truth for the layout that every\n * dev-section card row uses:\n * left column → title (h3) / subtitle (h5 uppercase) / description\n * (h4 line-clamp-3), each in a fixed min-height block\n * so rows align across the grid\n * right column → caller-supplied stacked badges\n *\n * Surface stays small on purpose — `rightBadges` is a `ReactNode` so\n * the caller decides how many badges (delivery: 2, tickets: 1-2,\n * future: anything). No behavior baked in: the caller wraps the row\n * in a `<div>` (static, like delivery) or `<button>` (clickable, like\n * tickets) and renders the row content via this component.\n *\n * Pair with `DevCardRowSkeletonList` for the loading state — the\n * skeleton mirrors the same min-heights so the in-flight UI doesn't\n * shift the layout when real data lands.\n *\n * NOTE: the ticket conversation row is NOT here — it renders the shared\n * `<ChatMessageRow>` (`components/chat/chat-message-row.tsx`), the SAME\n * component the OpenMSP Slack-community feed uses, so the two surfaces stay\n * pixel-identical by construction.\n */\n\nimport type { ReactNode } from 'react';\n\nexport interface DevCardRowContentProps {\n title: string;\n /** Single-line uppercase metadata (e.g. \"UPDATED today, #4271, Code review\"). */\n subtitle: string;\n /** 3-line description block. Empty string renders the fallback. */\n description: string;\n /** Fallback copy when `description` is empty. Defaults to a generic\n * string; ticket / delivery surfaces override. */\n emptyDescription?: string;\n /** Right column — caller renders its own stacked badges. */\n rightBadges: ReactNode;\n}\n\nexport function DevCardRowContent({\n title,\n subtitle,\n description,\n emptyDescription = 'No description provided',\n rightBadges,\n}: DevCardRowContentProps) {\n return (\n <div className=\"flex flex-col md:flex-row items-start justify-between gap-[12px] md:gap-[16px] w-full\">\n <div className=\"flex-1 min-w-0 w-full md:w-auto flex flex-col gap-[12px] md:gap-[16px]\">\n <div className=\"min-h-[24px] flex items-center\">\n <h3 className=\"text-h3 text-ods-text-primary tracking-[-0.36px] flex-1 line-clamp-2 md:truncate break-words\">\n {title}\n </h3>\n </div>\n <div className=\"min-h-[20px] flex items-center\">\n <p className=\"text-h5 text-ods-text-secondary uppercase tracking-[-0.28px] truncate\">\n {subtitle}\n </p>\n </div>\n <div className=\"min-h-[72px] flex items-center\">\n <p className=\"text-h4 text-ods-text-secondary line-clamp-3 break-words\">\n {description || emptyDescription}\n </p>\n </div>\n </div>\n <div className=\"flex-shrink-0 self-start flex flex-col gap-2\">\n {rightBadges}\n </div>\n </div>\n );\n}\n\n/**\n * Skeleton rendering for a single row — the bars mirror the same\n * min-heights as `DevCardRowContent` so the loading→loaded swap\n * doesn't reflow.\n */\nexport function DevCardRowSkeleton() {\n return (\n <div className=\"border-b border-ods-border last:border-b-0 p-[12px] md:p-[16px]\">\n <div className=\"flex flex-col md:flex-row items-start justify-between gap-[12px] md:gap-[16px] w-full\">\n <div className=\"flex-1 min-w-0 w-full md:w-auto flex flex-col gap-[12px] md:gap-[16px]\">\n <div className=\"min-h-[24px] flex items-center\">\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-full\" />\n </div>\n <div className=\"min-h-[20px] flex items-center\">\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-1/2\" />\n </div>\n <div className=\"min-h-[72px] flex items-center\">\n <div className=\"flex-1 space-y-1\">\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-full\" />\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-full\" />\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-2/3\" />\n </div>\n </div>\n </div>\n <div className=\"flex-shrink-0 self-start flex flex-col gap-2\">\n <div className=\"h-[32px] w-[100px] bg-ods-border rounded animate-pulse\" />\n <div className=\"h-[32px] w-[120px] bg-ods-border rounded animate-pulse\" />\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * The standard \"5 skeleton rows inside a bordered card\" loading state\n * used by every list shell. Both delivery (`delivery-table.tsx`) and\n * tickets (`tickets-list.tsx`) mount this directly.\n */\nexport function DevCardRowSkeletonList({ rows = 5 }: { rows?: number }) {\n return (\n <div className=\"bg-ods-card border border-ods-border rounded-[6px] overflow-hidden w-full\">\n {Array.from({ length: rows }, (_, i) => (\n <DevCardRowSkeleton key={i} />\n ))}\n </div>\n );\n}\n","'use client'\n\n/**\n * `<DeliveryRow />` — canonical single-row presentation for a ClickUp\n * delivery item.\n *\n * Single source of truth: both the `/bug-fixes-and-enhancements` page\n * (via `DeliveryTable`) AND the linked-delivery card on a HubSpot ticket\n * (via `TicketLinkedDeliveryCard`) compose this primitive. Visual parity\n * across those two surfaces is the design goal — the user reads the\n * card on their ticket and recognises it as a row from the public\n * delivery list.\n *\n * Behaviors:\n * - `href` set → outer element is an `<a>`, the whole row becomes\n * clickable (used by the linked-card surface to deep-link into\n * `/bug-fixes-and-enhancements?focus=<id>`).\n * - `id` set → outer element gets that DOM id so the consuming page\n * can `scrollIntoView` to it when the URL carries `?focus=<id>`.\n * - `highlighted` true → brief accent border + background pulse\n * (`animate-flash-focus` keyframe defined in `tailwind.config.ts`).\n * - `caption` set → small uppercase label rendered above the title\n * (\"LINKED DELIVERY\" on the ticket-side variant). Omitted on the\n * standard list rendering.\n */\n\nimport * as React from 'react'\nimport Link from '../../../embed-shims/next-link'\nimport { StatusBadge } from '../../ui/status-badge'\nimport { AvatarStack } from '../../ui/avatar-stack'\nimport { getStatusColorScheme } from '../../../utils'\nimport {\n type DeliveryItem,\n TASK_TYPE_LABELS,\n TASK_TYPE_TEXT_COLORS,\n} from '../../../types/delivery'\nimport { cn } from '../../../utils/cn'\n\n/** Same heuristic as DeliveryTable's local helper. Inlined so the row\n * primitive owns its complete rendering contract. */\nfunction getRelativeTime(timestamp: number): string {\n const now = Date.now()\n const diff = now - timestamp\n const days = Math.floor(diff / (1000 * 60 * 60 * 24))\n const weeks = Math.floor(days / 7)\n const months = Math.floor(days / 30)\n if (months > 0) return months === 1 ? 'last month' : `${months} months ago`\n if (weeks > 0) return weeks === 1 ? 'last week' : `${weeks} weeks ago`\n if (days > 0) return days === 1 ? 'yesterday' : `${days} days ago`\n return 'today'\n}\n\nexport interface DeliveryRowProps {\n item: DeliveryItem\n /** When set, the row becomes a clickable anchor. The ticket-side\n * linked-card composes this from `buildDevSectionUrl('delivery', id)`\n * which carries `?search=<id>` — the delivery list filters to that\n * exact task on landing (canonical deep-link mechanism, same one\n * the chat-inline delivery card uses). */\n href?: string\n /** Small uppercase caption rendered above the title. Used by the\n * linked-delivery card variant (\"LINKED DELIVERY\"). */\n caption?: string\n /** DOM `id` applied to the row's outer element. `DeliveryTable`\n * always sets `delivery-<external_id>` so chat-card deep-links\n * (`?search=<id>#delivery-<id>`) and the ticket linked-card path\n * both have a target for `useScrollToHash` to scroll to. Always\n * paired with `scroll-mt-24` on the outer element so the row lands\n * BELOW the sticky chrome after the scroll. */\n id?: string\n className?: string\n}\n\nexport function DeliveryRow({\n item,\n href,\n caption,\n id,\n className,\n}: DeliveryRowProps) {\n const taskType = item.taskType as keyof typeof TASK_TYPE_LABELS\n const typeBadgeLabel = TASK_TYPE_LABELS[taskType] || 'TASK'\n const typeBadgeTextColor = TASK_TYPE_TEXT_COLORS[taskType] || ''\n const statusBadgeScheme = getStatusColorScheme(item.status)\n const relativeTime = getRelativeTime(item.dateUpdated)\n const subtitle = `ACTIVE ${relativeTime}${item.listNames.length > 0 ? `, ${item.listNames.join(', ')}` : ''}, ${item.id}`\n\n const inner = (\n <div className=\"flex flex-col md:flex-row items-start justify-between gap-[12px] md:gap-[16px] w-full\">\n {/* Left: caption (optional) + title + subtitle + description */}\n <div className=\"flex-1 min-w-0 w-full md:w-auto flex flex-col gap-[12px] md:gap-[16px]\">\n {caption && (\n <p className=\"text-h5 text-ods-text-secondary\">\n {caption}\n </p>\n )}\n <div className=\"min-h-[24px] md:min-h-[24px] flex items-center\">\n <h3 className=\"text-h3 text-ods-text-primary tracking-[-0.36px] flex-1 line-clamp-2 md:truncate break-words\">\n {item.title}\n </h3>\n </div>\n <div className=\"min-h-[20px] flex items-center\">\n <p className=\"text-h5 text-ods-text-secondary uppercase tracking-[-0.28px] truncate\">\n {subtitle}\n </p>\n </div>\n <div className=\"min-h-[72px] flex items-center\">\n <p className=\"text-h4 text-ods-text-secondary line-clamp-3 break-words\">\n {item.description || 'No description provided'}\n </p>\n </div>\n </div>\n\n {/* Right (desktop): badges + assignee stack in a right-aligned\n column. Mobile: ONE horizontal footer row — badges lead,\n avatars anchor the right edge (kanban-card convention: the\n assignee owns the trailing slot of the meta row; a column of\n right-aligned orphans under the description reads as clutter\n on small screens). */}\n <div className=\"flex-shrink-0 self-stretch md:self-start w-full md:w-auto flex flex-row md:flex-col items-center md:items-end gap-2\">\n <StatusBadge\n text={item.status.toUpperCase()}\n colorScheme={statusBadgeScheme}\n variant=\"card\"\n className=\"border border-ods-border\"\n />\n <StatusBadge\n text={typeBadgeLabel}\n variant=\"card\"\n className={`border border-ods-border ${typeBadgeTextColor}`}\n />\n {item.assignees && item.assignees.length > 0 ? (\n <AvatarStack\n size=\"xs\"\n className=\"ml-auto md:ml-0\"\n people={item.assignees.map((a) => ({\n key: a.id,\n name: a.name ?? 'Unknown',\n avatarUrl: a.avatarUrl,\n }))}\n />\n ) : null}\n </div>\n </div>\n )\n\n const baseClass = cn(\n 'block p-[12px] md:p-[16px] no-underline text-inherit transition-colors duration-150',\n // `scroll-mt-24` is paid for whether `id` is set or not (it's a\n // single Tailwind utility, no runtime cost). Keeping it\n // unconditional means a future caller adding `id` doesn't also\n // have to remember to ask for the offset.\n 'scroll-mt-24',\n href && 'hover:bg-ods-bg-hover cursor-pointer',\n className,\n )\n\n if (href) {\n // `Link` is the env-aware embed-shim — delegates to `next/link` on\n // a Next.js host (soft RSC nav, back-button restores the previous\n // page's React state intact), falls back to a plain `<a>` on\n // non-Next embedders. A raw `<a href>` was hard-navigating +\n // losing TanStack-Query state on back, leaving /tickets stuck on\n // its skeleton.\n return (\n <Link href={href} id={id} className={baseClass} prefetch={false}>\n {inner}\n </Link>\n )\n }\n\n return <div id={id} className={baseClass}>{inner}</div>\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-ULH6JHWZ.cjs","../src/components/shared/dev-section/dev-section-view.tsx","../src/components/shared/dev-section/dev-section-page.tsx","../src/components/shared/dev-section/dev-card-row.tsx","../src/components/shared/delivery/delivery-row.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-ULH6JHWZ.cjs","sourcesContent":[null,"'use client';\n\n/**\n * DevSectionView — the canonical chrome for ANY dev-center section\n * (Roadmap / Delivery / Releases). One component, used in BOTH:\n *\n * - tabbed `/roadmap-and-releases` (compact title mode, no `hero`)\n * - full-page `/roadmap`, `/bug-fixes-and-enhancements`, `/releases`\n * (hero mode with icon + description + back link)\n *\n * Owns: title rendering, the inline search input, the filter pill row,\n * and the URL-param wiring that connects both. The list `children`\n * receive a clean URL contract — they read `?<paramKey>=...` via\n * `useSearchParams()` and refetch on change. No duplicated controls.\n */\n\nimport type { ReactNode } from 'react';\nimport { useState, useEffect } from 'react';\nimport { useRouter, useSearchParams, usePathname } from '../../../embed-shims';\nimport { SearchInput } from '../../ui';\nimport { StatusFilterComponent } from '../../features';\nimport {\n OPENFRAME_DEV_SECTIONS,\n type OpenframeDevSection,\n type OpenframeDevSectionKey,\n} from '../../../utils/dev-sections/openframe-dev-sections';\n\nexport interface DevSectionViewProps {\n /** Which section to render — drives title, search, and filter\n * config via the `OPENFRAME_DEV_SECTIONS` registry. */\n sectionKey: OpenframeDevSectionKey;\n /** When set, renders the rich page-level hero (icon + h1 + description).\n * Omit for the compact tab-context heading. */\n hero?: {\n /** Pre-rendered icon JSX. Server components render the icon themselves\n * and pass the element here — function references can't cross the\n * server→client boundary, but React elements can. */\n icon: ReactNode;\n /** Hero title. Falls back to `OPENFRAME_DEV_SECTIONS[sectionKey].hero.title`\n * when omitted, so embedders can override the (OpenFrame-specific) default\n * copy without forking the registry. */\n title?: string;\n description: string;\n };\n /** Optional slot rendered BETWEEN the hero and the search/filter\n * controls. Use this for an entry-action surface that should sit\n * above the list (e.g. the Help Center's \"Open a new ticket\" form).\n * The slot is wrapped in the same `gap-10` flex column so spacing\n * matches the surrounding chrome — callers should NOT add their\n * own top/bottom margin. Renders `null` (no DOM) when omitted. */\n preControls?: ReactNode;\n /** The page-specific list body. Reads URL params written by this\n * component (search input + filter pills). */\n children: ReactNode;\n /** Render this component's own title heading (hero or compact `h2`). Default\n * `true` — the tab context renders the compact heading itself. Pass `false`\n * when the host already renders the title elsewhere (e.g. `DevSectionPage`\n * routes it through the unified `PageLayout` `TitleBlock`): then only the\n * search + filter controls + list render, with no duplicate heading. */\n showHeading?: boolean;\n}\n\nexport function DevSectionView({ sectionKey, hero, preControls, children, showHeading = true }: DevSectionViewProps) {\n const section = OPENFRAME_DEV_SECTIONS[sectionKey];\n const router = useRouter();\n const pathname = usePathname();\n const searchParams = useSearchParams();\n\n // Widen from the registry's per-section literal types to the interface —\n // optional fields (`clearParamKeys`) exist on the interface but not on\n // every section's inferred literal. Sound: the registry `satisfies`\n // `Record<string, OpenframeDevSection>`.\n const search: OpenframeDevSection['search'] = section.search;\n const filter = section.filter;\n\n const currentSearch = search ? searchParams.get(search.paramKey) || '' : '';\n const currentFilterValue = filter\n ? searchParams.get(filter.paramKey) || filter.defaultValue\n : '';\n\n // Controlled search-input state — TYPING commits to the URL only on\n // Enter (not on every keystroke), but CLEARING commits immediately:\n // an emptied input must not keep filtering the list (the URL param —\n // and any linked `clearParamKeys` companion like the tickets\n // `?ticket=` drawer param — previously survived until Enter).\n // Lazy init from URL avoids a brief flash of stale value on first\n // paint after URL-driven re-render (e.g. tab switch).\n const [searchValue, setSearchValue] = useState(() => currentSearch);\n useEffect(() => {\n setSearchValue(currentSearch);\n }, [currentSearch]);\n\n const handleSearchSubmit = (value: string) => {\n if (!search) return;\n const params = new URLSearchParams(searchParams.toString());\n if (value.trim()) {\n params.set(search.paramKey, value.trim());\n } else {\n params.delete(search.paramKey);\n // Empty commit tears down the search's linked companion params too\n // (deep links set them TOGETHER — see the config field docs).\n for (const key of search.clearParamKeys ?? []) params.delete(key);\n }\n const qs = params.toString();\n router.replace(qs ? `${pathname}?${qs}` : pathname, { scroll: false });\n };\n\n const handleSearchChange = (value: string) => {\n setSearchValue(value);\n if (value === '' && currentSearch !== '') handleSearchSubmit('');\n };\n\n const handleFilterChange = (value: string) => {\n if (!filter) return;\n const params = new URLSearchParams(searchParams.toString());\n if (value === filter.defaultValue) params.delete(filter.paramKey);\n else params.set(filter.paramKey, value);\n router.replace(`${pathname}?${params.toString()}`, { scroll: false });\n };\n\n return (\n <div className=\"w-full flex flex-col gap-10\">\n {showHeading &&\n (hero ? (\n <div className=\"space-y-4\">\n <h1 className=\"text-h1 tracking-[-1.12px] text-ods-text-primary flex items-center gap-3\">\n {hero.icon}\n {hero.title ?? section.hero.title}\n </h1>\n <p className=\"text-h4 text-ods-text-secondary max-w-3xl\">\n {hero.description}\n </p>\n </div>\n ) : (\n <div className=\"flex items-center justify-between w-full\">\n <h2 className=\"font-['Azeret_Mono'] font-semibold text-[32px] md:text-[40px] lg:text-[48px] leading-[40px] md:leading-[48px] lg:leading-[56px] text-ods-text-primary tracking-[-0.64px] md:tracking-[-0.8px] lg:tracking-[-0.96px]\">\n {section.hero.title}\n <span className=\"text-ods-accent\">:</span>\n </h2>\n </div>\n ))}\n\n {preControls}\n\n {(search || filter) && (\n <div className=\"space-y-4\">\n {search && (\n <SearchInput\n showDropdown={false}\n placeholder={search.placeholder}\n value={searchValue}\n onChange={handleSearchChange}\n onSubmit={handleSearchSubmit}\n />\n )}\n {filter && (\n <StatusFilterComponent\n selectedStatus={currentFilterValue}\n onStatusChange={handleFilterChange}\n statusOptions={[...filter.options]}\n />\n )}\n </div>\n )}\n\n {children}\n </div>\n );\n}\n","'use client';\n\n/**\n * DevSectionPage — full-page wrapper for a dev-center section\n * (`/roadmap`, `/bug-fixes-and-enhancements`, `/releases`).\n *\n * Mounts the lib's canonical `PageLayout` directly (no in-app wrapper)\n * so the back-button affordance stays in lockstep with whatever the\n * design system ships — any future lib change to BackButton / TitleBlock\n * propagates automatically.\n *\n * Composition: `PageShell` → `PageLayout` (back-to-home wired) →\n * `DevSectionView` (icon hero + search + filter pills) → list body.\n *\n * Adding a new section is one entry in `OPENFRAME_DEV_SECTIONS` plus a\n * single-line page file mounting this factory with the new key.\n */\n\nimport type { ReactNode } from 'react';\nimport { useRouter } from '../../../embed-shims/next-navigation';\nimport { PageShell, PageLayout } from '../../ui';\nimport { DevSectionView } from './dev-section-view';\nimport {\n OPENFRAME_DEV_SECTIONS,\n type OpenframeDevSectionKey,\n} from '../../../utils/dev-sections/openframe-dev-sections';\n\nexport interface DevSectionPageProps {\n sectionKey: OpenframeDevSectionKey;\n /** The page-specific list body (e.g. `<RoadmapList />`). */\n children: ReactNode;\n /** Optional slot rendered BETWEEN the hero and search/filter — see\n * `DevSectionView.preControls`. Used by surfaces that want an entry\n * action (e.g. Help Center's \"Open a new ticket\" form) above the\n * controls instead of below them. */\n preControls?: ReactNode;\n /** Back-button config — same shape as `LegalDocumentPage` /\n * `ReleaseDetailPage`. Pass `false` to hide entirely. Default\n * `{ label: 'Back to home', href: '/' }` — embedders whose \"home\" isn't `/`\n * should override `href`, or pass `false` if the embed has no home page. */\n backButton?: { label?: string; href?: string } | false;\n /** Override the hero title. Defaults to the (OpenFrame-specific) copy in\n * `OPENFRAME_DEV_SECTIONS[sectionKey].hero.title`. Set this to brand the\n * section for a non-OpenFrame embed. */\n title?: string;\n /** Override the hero subtitle/description. Defaults to\n * `OPENFRAME_DEV_SECTIONS[sectionKey].hero.description`. */\n subtitle?: string;\n /** Render the standalone `<PageShell>` (own `<main>` + bg + max-width). Default\n * `true` — the contract for marketing/hub surfaces with no app shell. Pass\n * `false` when the host layout already provides the page container (e.g.\n * openframe-frontend's `AppLayout` `<main>`): then only the\n * `page-shell-content` padding box is rendered, avoiding a nested `<main>`. */\n shell?: boolean;\n}\n\nexport function DevSectionPage({\n sectionKey,\n children,\n preControls,\n backButton,\n title,\n subtitle,\n shell = true,\n}: DevSectionPageProps) {\n const router = useRouter();\n const section = OPENFRAME_DEV_SECTIONS[sectionKey];\n\n // Back-button config — mirrors LegalDocumentPage / ReleaseDetailPage.\n // Default: { label: 'Back to home', href: '/' }. Pass `false` to hide.\n // After `backButton &&` narrowing, inner type is `{ label?, href? } |\n // undefined`; don't re-compare to `false` (TS2367).\n const backCfg =\n backButton === false\n ? undefined\n : {\n label: (backButton ? backButton.label : undefined) ?? 'Back to home',\n onClick: () => router.push((backButton ? backButton.href : undefined) ?? '/'),\n };\n\n const inner = (\n // Unified header: title/description route through the canonical (frozen)\n // `PageLayout` `TitleBlock` (text-h2) — same as FAQ / Legal / detail pages —\n // so every help-center surface shares one header. `DevSectionView` then\n // renders ONLY its search + filter controls (`showHeading={false}`), no\n // duplicate title. (The hero icon is intentionally dropped: TitleBlock is\n // frozen and renders title-only.)\n <PageLayout\n title={title ?? section.hero.title}\n subtitle={subtitle ?? section.hero.description}\n titleSize=\"h1\"\n titleWrap\n backButton={backCfg}\n >\n <DevSectionView sectionKey={sectionKey} showHeading={false} preControls={preControls}>\n {children}\n </DevSectionView>\n </PageLayout>\n );\n\n // `shell` true → standalone `<PageShell>` (own <main> + bg + max-width).\n // false → padding-only box (no nested <main>) for hosts whose layout already\n // provides the container; both consume the host's `--page-shell-*` vars.\n return shell ? <PageShell>{inner}</PageShell> : <div className=\"page-shell-content\">{inner}</div>;\n}\n","'use client';\n\n/**\n * Shared row chrome for any `DevSectionPage` list (delivery, tickets,\n * future sections). One source of truth for the layout that every\n * dev-section card row uses:\n * left column → title (h3) / subtitle (h5 uppercase) / description\n * (h4 line-clamp-3), each in a fixed min-height block\n * so rows align across the grid\n * right column → caller-supplied stacked badges\n *\n * Surface stays small on purpose — `rightBadges` is a `ReactNode` so\n * the caller decides how many badges (delivery: 2, tickets: 1-2,\n * future: anything). No behavior baked in: the caller wraps the row\n * in a `<div>` (static, like delivery) or `<button>` (clickable, like\n * tickets) and renders the row content via this component.\n *\n * Pair with `DevCardRowSkeletonList` for the loading state — the\n * skeleton mirrors the same min-heights so the in-flight UI doesn't\n * shift the layout when real data lands.\n *\n * NOTE: the ticket conversation row is NOT here — it renders the shared\n * `<ChatMessageRow>` (`components/chat/chat-message-row.tsx`), the SAME\n * component the OpenMSP Slack-community feed uses, so the two surfaces stay\n * pixel-identical by construction.\n */\n\nimport type { ReactNode } from 'react';\n\nexport interface DevCardRowContentProps {\n title: string;\n /** Single-line uppercase metadata (e.g. \"UPDATED today, #4271, Code review\"). */\n subtitle: string;\n /** 3-line description block. Empty string renders the fallback. */\n description: string;\n /** Fallback copy when `description` is empty. Defaults to a generic\n * string; ticket / delivery surfaces override. */\n emptyDescription?: string;\n /** Right column — caller renders its own stacked badges. */\n rightBadges: ReactNode;\n}\n\nexport function DevCardRowContent({\n title,\n subtitle,\n description,\n emptyDescription = 'No description provided',\n rightBadges,\n}: DevCardRowContentProps) {\n return (\n <div className=\"flex flex-col md:flex-row items-start justify-between gap-[12px] md:gap-[16px] w-full\">\n <div className=\"flex-1 min-w-0 w-full md:w-auto flex flex-col gap-[12px] md:gap-[16px]\">\n <div className=\"min-h-[24px] flex items-center\">\n <h3 className=\"text-h3 text-ods-text-primary tracking-[-0.36px] flex-1 line-clamp-2 md:truncate break-words\">\n {title}\n </h3>\n </div>\n <div className=\"min-h-[20px] flex items-center\">\n <p className=\"text-h5 text-ods-text-secondary uppercase tracking-[-0.28px] truncate\">\n {subtitle}\n </p>\n </div>\n <div className=\"min-h-[72px] flex items-center\">\n <p className=\"text-h4 text-ods-text-secondary line-clamp-3 break-words\">\n {description || emptyDescription}\n </p>\n </div>\n </div>\n <div className=\"flex-shrink-0 self-start flex flex-col gap-2\">\n {rightBadges}\n </div>\n </div>\n );\n}\n\n/**\n * Skeleton rendering for a single row — the bars mirror the same\n * min-heights as `DevCardRowContent` so the loading→loaded swap\n * doesn't reflow.\n */\nexport function DevCardRowSkeleton() {\n return (\n <div className=\"border-b border-ods-border last:border-b-0 p-[12px] md:p-[16px]\">\n <div className=\"flex flex-col md:flex-row items-start justify-between gap-[12px] md:gap-[16px] w-full\">\n <div className=\"flex-1 min-w-0 w-full md:w-auto flex flex-col gap-[12px] md:gap-[16px]\">\n <div className=\"min-h-[24px] flex items-center\">\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-full\" />\n </div>\n <div className=\"min-h-[20px] flex items-center\">\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-1/2\" />\n </div>\n <div className=\"min-h-[72px] flex items-center\">\n <div className=\"flex-1 space-y-1\">\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-full\" />\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-full\" />\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-2/3\" />\n </div>\n </div>\n </div>\n <div className=\"flex-shrink-0 self-start flex flex-col gap-2\">\n <div className=\"h-[32px] w-[100px] bg-ods-border rounded animate-pulse\" />\n <div className=\"h-[32px] w-[120px] bg-ods-border rounded animate-pulse\" />\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * The standard \"5 skeleton rows inside a bordered card\" loading state\n * used by every list shell. Both delivery (`delivery-table.tsx`) and\n * tickets (`tickets-list.tsx`) mount this directly.\n */\nexport function DevCardRowSkeletonList({ rows = 5 }: { rows?: number }) {\n return (\n <div className=\"bg-ods-card border border-ods-border rounded-[6px] overflow-hidden w-full\">\n {Array.from({ length: rows }, (_, i) => (\n <DevCardRowSkeleton key={i} />\n ))}\n </div>\n );\n}\n","'use client'\n\n/**\n * `<DeliveryRow />` — canonical single-row presentation for a ClickUp\n * delivery item.\n *\n * Single source of truth: both the `/bug-fixes-and-enhancements` page\n * (via `DeliveryTable`) AND the linked-delivery card on a HubSpot ticket\n * (via `TicketLinkedDeliveryCard`) compose this primitive. Visual parity\n * across those two surfaces is the design goal — the user reads the\n * card on their ticket and recognises it as a row from the public\n * delivery list.\n *\n * Behaviors:\n * - `href` set → outer element is an `<a>`, the whole row becomes\n * clickable (used by the linked-card surface to deep-link into\n * `/bug-fixes-and-enhancements?focus=<id>`).\n * - `id` set → outer element gets that DOM id so the consuming page\n * can `scrollIntoView` to it when the URL carries `?focus=<id>`.\n * - `highlighted` true → brief accent border + background pulse\n * (`animate-flash-focus` keyframe defined in `tailwind.config.ts`).\n * - `caption` set → small uppercase label rendered above the title\n * (\"LINKED DELIVERY\" on the ticket-side variant). Omitted on the\n * standard list rendering.\n */\n\nimport * as React from 'react'\nimport Link from '../../../embed-shims/next-link'\nimport { StatusBadge } from '../../ui/status-badge'\nimport { AvatarStack } from '../../ui/avatar-stack'\nimport { getStatusColorScheme } from '../../../utils'\nimport {\n type DeliveryItem,\n TASK_TYPE_LABELS,\n TASK_TYPE_TEXT_COLORS,\n} from '../../../types/delivery'\nimport { cn } from '../../../utils/cn'\n\n/** Same heuristic as DeliveryTable's local helper. Inlined so the row\n * primitive owns its complete rendering contract. */\nfunction getRelativeTime(timestamp: number): string {\n const now = Date.now()\n const diff = now - timestamp\n const days = Math.floor(diff / (1000 * 60 * 60 * 24))\n const weeks = Math.floor(days / 7)\n const months = Math.floor(days / 30)\n if (months > 0) return months === 1 ? 'last month' : `${months} months ago`\n if (weeks > 0) return weeks === 1 ? 'last week' : `${weeks} weeks ago`\n if (days > 0) return days === 1 ? 'yesterday' : `${days} days ago`\n return 'today'\n}\n\nexport interface DeliveryRowProps {\n item: DeliveryItem\n /** When set, the row becomes a clickable anchor. The ticket-side\n * linked-card composes this from `buildDevSectionUrl('delivery', id)`\n * which carries `?search=<id>` — the delivery list filters to that\n * exact task on landing (canonical deep-link mechanism, same one\n * the chat-inline delivery card uses). */\n href?: string\n /** Small uppercase caption rendered above the title. Used by the\n * linked-delivery card variant (\"LINKED DELIVERY\"). */\n caption?: string\n /** DOM `id` applied to the row's outer element. `DeliveryTable`\n * always sets `delivery-<external_id>` so chat-card deep-links\n * (`?search=<id>#delivery-<id>`) and the ticket linked-card path\n * both have a target for `useScrollToHash` to scroll to. Always\n * paired with `scroll-mt-24` on the outer element so the row lands\n * BELOW the sticky chrome after the scroll. */\n id?: string\n className?: string\n}\n\nexport function DeliveryRow({\n item,\n href,\n caption,\n id,\n className,\n}: DeliveryRowProps) {\n const taskType = item.taskType as keyof typeof TASK_TYPE_LABELS\n const typeBadgeLabel = TASK_TYPE_LABELS[taskType] || 'TASK'\n const typeBadgeTextColor = TASK_TYPE_TEXT_COLORS[taskType] || ''\n const statusBadgeScheme = getStatusColorScheme(item.status)\n const relativeTime = getRelativeTime(item.dateUpdated)\n const subtitle = `ACTIVE ${relativeTime}${item.listNames.length > 0 ? `, ${item.listNames.join(', ')}` : ''}, ${item.id}`\n\n const inner = (\n <div className=\"flex flex-col md:flex-row items-start justify-between gap-[12px] md:gap-[16px] w-full\">\n {/* Left: caption (optional) + title + subtitle + description */}\n <div className=\"flex-1 min-w-0 w-full md:w-auto flex flex-col gap-[12px] md:gap-[16px]\">\n {caption && (\n <p className=\"text-h5 text-ods-text-secondary\">\n {caption}\n </p>\n )}\n <div className=\"min-h-[24px] md:min-h-[24px] flex items-center\">\n <h3 className=\"text-h3 text-ods-text-primary tracking-[-0.36px] flex-1 line-clamp-2 md:truncate break-words\">\n {item.title}\n </h3>\n </div>\n <div className=\"min-h-[20px] flex items-center\">\n <p className=\"text-h5 text-ods-text-secondary uppercase tracking-[-0.28px] truncate\">\n {subtitle}\n </p>\n </div>\n <div className=\"min-h-[72px] flex items-center\">\n <p className=\"text-h4 text-ods-text-secondary line-clamp-3 break-words\">\n {item.description || 'No description provided'}\n </p>\n </div>\n </div>\n\n {/* Right (desktop): badges + assignee stack in a right-aligned\n column. Mobile: ONE horizontal footer row — badges lead,\n avatars anchor the right edge (kanban-card convention: the\n assignee owns the trailing slot of the meta row; a column of\n right-aligned orphans under the description reads as clutter\n on small screens). */}\n <div className=\"flex-shrink-0 self-stretch md:self-start w-full md:w-auto flex flex-row md:flex-col items-center md:items-end gap-2\">\n <StatusBadge\n text={item.status.toUpperCase()}\n colorScheme={statusBadgeScheme}\n variant=\"card\"\n className=\"border border-ods-border\"\n />\n <StatusBadge\n text={typeBadgeLabel}\n variant=\"card\"\n className={`border border-ods-border ${typeBadgeTextColor}`}\n />\n {item.assignees && item.assignees.length > 0 ? (\n <AvatarStack\n size=\"xs\"\n className=\"ml-auto md:ml-0\"\n people={item.assignees.map((a) => ({\n key: a.id,\n name: a.name ?? 'Unknown',\n avatarUrl: a.avatarUrl,\n }))}\n />\n ) : null}\n </div>\n </div>\n )\n\n const baseClass = cn(\n 'block p-[12px] md:p-[16px] no-underline text-inherit transition-colors duration-150',\n // `scroll-mt-24` is paid for whether `id` is set or not (it's a\n // single Tailwind utility, no runtime cost). Keeping it\n // unconditional means a future caller adding `id` doesn't also\n // have to remember to ask for the offset.\n 'scroll-mt-24',\n href && 'hover:bg-ods-bg-hover cursor-pointer',\n className,\n )\n\n if (href) {\n // `Link` is the env-aware embed-shim — delegates to `next/link` on\n // a Next.js host (soft RSC nav, back-button restores the previous\n // page's React state intact), falls back to a plain `<a>` on\n // non-Next embedders. A raw `<a href>` was hard-navigating +\n // losing TanStack-Query state on back, leaving /tickets stuck on\n // its skeleton.\n return (\n <Link href={href} id={id} className={baseClass} prefetch={false}>\n {inner}\n </Link>\n )\n }\n\n return <div id={id} className={baseClass}>{inner}</div>\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  Pagination,
4
4
  init_pagination
5
- } from "./chunk-3XOICFFT.js";
5
+ } from "./chunk-ZGLMZM2I.js";
6
6
  import {
7
7
  init_next_navigation,
8
8
  usePathname,
@@ -210,4 +210,4 @@ export {
210
210
  unified_pagination_exports,
211
211
  init_unified_pagination
212
212
  };
213
- //# sourceMappingURL=chunk-6PH34GDC.js.map
213
+ //# sourceMappingURL=chunk-VIDE4YOQ.js.map
@@ -35,15 +35,15 @@ var _chunkQ6RC3YGWcjs = require('./chunk-Q6RC3YGW.cjs');
35
35
 
36
36
 
37
37
 
38
- var _chunkM3SHPHQ7cjs = require('./chunk-M3SHPHQ7.cjs');
38
+ var _chunk2HWN6CYIcjs = require('./chunk-2HWN6CYI.cjs');
39
39
 
40
40
  // src/components/related-content/related-content-section.tsx
41
41
  var _react = require('react');
42
- _chunkM3SHPHQ7cjs.init_pagination.call(void 0, );
42
+ _chunk2HWN6CYIcjs.init_pagination.call(void 0, );
43
43
  var _jsxruntime = require('react/jsx-runtime');
44
44
  function DefaultLinkPropsProvider({ href, targetPlatform, children }) {
45
45
  if (!href) return children(null);
46
- const newTab = _chunkM3SHPHQ7cjs.decideNewTab.call(void 0, { href, targetPlatform, currentSource: "" });
46
+ const newTab = _chunk2HWN6CYIcjs.decideNewTab.call(void 0, { href, targetPlatform, currentSource: "" });
47
47
  return children(
48
48
  newTab ? { href, target: "_blank", rel: "noopener noreferrer" } : { href }
49
49
  );
@@ -55,31 +55,31 @@ function renderSkeletonForType(type, size, adminCampaignCard) {
55
55
  const legacySize = size === "sm" ? "sm" : "default";
56
56
  switch (type) {
57
57
  case "blog_post_existing":
58
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.BlogCardSkeleton, { size: legacySize });
58
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.BlogCardSkeleton, { size: legacySize });
59
59
  case "case_study":
60
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.CaseStudyCardSkeleton, { size: legacySize });
60
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.CaseStudyCardSkeleton, { size: legacySize });
61
61
  case "customer_interview":
62
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.CustomerInterviewCardSkeleton, { size: legacySize });
62
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.CustomerInterviewCardSkeleton, { size: legacySize });
63
63
  case "product_release":
64
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.ProductReleaseCardSkeleton, { size: size === "sm" ? "sm" : "lg" });
64
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.ProductReleaseCardSkeleton, { size: size === "sm" ? "sm" : "lg" });
65
65
  case "podcast":
66
66
  case "webinar":
67
67
  case "event":
68
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.ProgramCardSkeleton, { size: legacySize });
68
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.ProgramCardSkeleton, { size: legacySize });
69
69
  case "investor_update":
70
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.InvestorUpdateCardSkeleton, { size: legacySize });
70
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.InvestorUpdateCardSkeleton, { size: legacySize });
71
71
  case "onboarding_guide":
72
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.OnboardingGuideCardSkeleton, { size: size === "sm" ? "sm" : "catalog" });
72
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.OnboardingGuideCardSkeleton, { size: size === "sm" ? "sm" : "catalog" });
73
73
  case "what_i_shipped":
74
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.WhatIShippedCardSkeleton, {});
74
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.WhatIShippedCardSkeleton, {});
75
75
  case "how_i_work":
76
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.HowIWorkCardSkeleton, {});
76
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.HowIWorkCardSkeleton, {});
77
77
  case "marketing_campaign":
78
78
  return adminCampaignCard ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, adminCampaignCard.Skeleton, { size: legacySize }) : null;
79
79
  case "roadmap_item":
80
80
  case "delivery_item":
81
81
  case "internal_task":
82
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.RoadmapCardSkeleton, { size: legacySize });
82
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.RoadmapCardSkeleton, { size: legacySize });
83
83
  default:
84
84
  return null;
85
85
  }
@@ -99,17 +99,17 @@ function CardForType({
99
99
  const anchorAttrs = linkProps ? { target: linkProps.target, rel: linkProps.rel } : {};
100
100
  switch (type) {
101
101
  case "blog_post_existing":
102
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.BlogCard, { post: item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs });
102
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.BlogCard, { post: item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs });
103
103
  case "case_study":
104
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.CaseStudyCard, { study: item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs });
104
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.CaseStudyCard, { study: item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs });
105
105
  case "customer_interview":
106
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.CustomerInterviewCard, { interview: item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs });
106
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.CustomerInterviewCard, { interview: item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs });
107
107
  case "product_release": {
108
108
  const releaseSize = size === "sm" ? "sm" : "lg";
109
- const buildReleaseProps = _nullishCoalesce(_optionalChain([extras, 'optionalAccess', _5 => _5.buildProductReleaseCardProps]), () => ( _chunkM3SHPHQ7cjs.buildProductReleaseCardProps));
109
+ const buildReleaseProps = _nullishCoalesce(_optionalChain([extras, 'optionalAccess', _5 => _5.buildProductReleaseCardProps]), () => ( _chunk2HWN6CYIcjs.buildProductReleaseCardProps));
110
110
  const releaseProps = buildReleaseProps(item);
111
111
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
112
- _chunkM3SHPHQ7cjs.ProductReleaseCard,
112
+ _chunk2HWN6CYIcjs.ProductReleaseCard,
113
113
  {
114
114
  size: releaseSize,
115
115
  title: item.title,
@@ -121,18 +121,18 @@ function CardForType({
121
121
  );
122
122
  }
123
123
  case "podcast":
124
- return _optionalChain([extras, 'optionalAccess', _6 => _6.programConfigs, 'optionalAccess', _7 => _7.podcast]) ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.ProgramCard, { config: extras.programConfigs.podcast, item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs }) : null;
124
+ return _optionalChain([extras, 'optionalAccess', _6 => _6.programConfigs, 'optionalAccess', _7 => _7.podcast]) ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.ProgramCard, { config: extras.programConfigs.podcast, item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs }) : null;
125
125
  case "webinar":
126
- return _optionalChain([extras, 'optionalAccess', _8 => _8.programConfigs, 'optionalAccess', _9 => _9.webinar]) ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.ProgramCard, { config: extras.programConfigs.webinar, item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs }) : null;
126
+ return _optionalChain([extras, 'optionalAccess', _8 => _8.programConfigs, 'optionalAccess', _9 => _9.webinar]) ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.ProgramCard, { config: extras.programConfigs.webinar, item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs }) : null;
127
127
  case "event":
128
- return _optionalChain([extras, 'optionalAccess', _10 => _10.programConfigs, 'optionalAccess', _11 => _11.event]) ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.ProgramCard, { config: extras.programConfigs.event, item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs }) : null;
128
+ return _optionalChain([extras, 'optionalAccess', _10 => _10.programConfigs, 'optionalAccess', _11 => _11.event]) ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.ProgramCard, { config: extras.programConfigs.event, item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs }) : null;
129
129
  case "investor_update":
130
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.InvestorUpdateCard, { update: item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs });
130
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.InvestorUpdateCard, { update: item, size: legacySize, href, targetPlatform, placeholderUrl, ...anchorAttrs });
131
131
  case "onboarding_guide":
132
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.OnboardingGuideCard, { guide: item, size: size === "sm" ? "sm" : "catalog", href, targetPlatform, placeholderUrl, ...anchorAttrs });
132
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.OnboardingGuideCard, { guide: item, size: size === "sm" ? "sm" : "catalog", href, targetPlatform, placeholderUrl, ...anchorAttrs });
133
133
  case "what_i_shipped":
134
134
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
135
- _chunkM3SHPHQ7cjs.WhatIShippedCard,
135
+ _chunk2HWN6CYIcjs.WhatIShippedCard,
136
136
  {
137
137
  entry: item,
138
138
  placeholderUrl,
@@ -141,7 +141,7 @@ function CardForType({
141
141
  );
142
142
  case "how_i_work":
143
143
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
144
- _chunkM3SHPHQ7cjs.HowIWorkCard,
144
+ _chunk2HWN6CYIcjs.HowIWorkCard,
145
145
  {
146
146
  entry: item,
147
147
  placeholderUrl,
@@ -154,7 +154,7 @@ function CardForType({
154
154
  case "delivery_item":
155
155
  case "internal_task":
156
156
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
157
- _chunkM3SHPHQ7cjs.RoadmapCard,
157
+ _chunk2HWN6CYIcjs.RoadmapCard,
158
158
  {
159
159
  item,
160
160
  href: _nullishCoalesce(href, () => ( "")),
@@ -175,14 +175,14 @@ function useGroupItems(type, refs, buildUrl) {
175
175
  const ids = refs.map((r) => r.id);
176
176
  const url = ids.length > 0 ? buildUrl(type, ids) : null;
177
177
  const { data, isLoading } = _chunkQ6RC3YGWcjs.useSelfFetch.call(void 0, url);
178
- const items = data != null ? _chunkM3SHPHQ7cjs.extractItems.call(void 0, data) : null;
178
+ const items = data != null ? _chunk2HWN6CYIcjs.extractItems.call(void 0, data) : null;
179
179
  return { items, isLoading };
180
180
  }
181
181
  function gridClassFor(columns) {
182
182
  return columns === 3 ? "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6" : "grid grid-cols-1 sm:grid-cols-2 gap-6";
183
183
  }
184
184
  function resolveGroupConfig(type) {
185
- return _nullishCoalesce(_chunkM3SHPHQ7cjs.CONTENT_REF_GROUPS[type], () => ( {
185
+ return _nullishCoalesce(_chunk2HWN6CYIcjs.CONTENT_REF_GROUPS[type], () => ( {
186
186
  label: type,
187
187
  order: 999,
188
188
  layout: "grid",
@@ -217,7 +217,7 @@ function ContentGroup({
217
217
  const totalGroupPages = Math.max(1, Math.ceil(refs.length / GROUP_PAGE_SIZE));
218
218
  const safePage = Math.min(page, totalGroupPages);
219
219
  const visibleGroupRefs = refs.length > GROUP_PAGE_SIZE ? refs.slice((safePage - 1) * GROUP_PAGE_SIZE, safePage * GROUP_PAGE_SIZE) : refs;
220
- const groupPagination = totalGroupPages > 1 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkM3SHPHQ7cjs.Pagination, { currentPage: safePage, totalPages: totalGroupPages, onPageChange: setPage }) : null;
220
+ const groupPagination = totalGroupPages > 1 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk2HWN6CYIcjs.Pagination, { currentPage: safePage, totalPages: totalGroupPages, onPageChange: setPage }) : null;
221
221
  if (isLoading && !items) {
222
222
  const skeletons = visibleGroupRefs.map((r) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { children: renderSkeletonForType(type, cardSize, adminCampaignCard) }, r.id));
223
223
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-4", children: [
@@ -227,7 +227,7 @@ function ContentGroup({
227
227
  }
228
228
  if (!items || items.length === 0) return null;
229
229
  const itemById = new Map(
230
- items.map((it) => [_nullishCoalesce(_chunkM3SHPHQ7cjs.extractItemId.call(void 0, type, it), () => ( String(_optionalChain([it, 'optionalAccess', _12 => _12.id])))), it])
230
+ items.map((it) => [_nullishCoalesce(_chunk2HWN6CYIcjs.extractItemId.call(void 0, type, it), () => ( String(_optionalChain([it, 'optionalAccess', _12 => _12.id])))), it])
231
231
  );
232
232
  const cards = visibleGroupRefs.map((contentRef) => {
233
233
  const itemId = String(contentRef.id);
@@ -292,11 +292,11 @@ function RelatedContentSection({
292
292
  types: includeTypes !== void 0 ? includeTypes.join(",") : void 0,
293
293
  excludeTypes: excludeTypes && excludeTypes.length > 0 ? excludeTypes.join(",") : void 0
294
294
  };
295
- const authorUrl = contentRefs === void 0 && authorId && !suggestionsDisabled ? _chunkM3SHPHQ7cjs.buildSuggestionUrl.call(void 0, "/api/related-content", {
295
+ const authorUrl = contentRefs === void 0 && authorId && !suggestionsDisabled ? _chunk2HWN6CYIcjs.buildSuggestionUrl.call(void 0, "/api/related-content", {
296
296
  apiBaseUrl,
297
297
  extraParams: { authorId, ...typeFilterParams }
298
298
  }) : null;
299
- const suggestUrl = _nullishCoalesce(authorUrl, () => ( (contentRefs === void 0 && entityType && entityId !== void 0 && entityId !== null && entityId !== "" && !suggestionsDisabled ? _chunkM3SHPHQ7cjs.buildSuggestionUrl.call(void 0, "/api/related-content", {
299
+ const suggestUrl = _nullishCoalesce(authorUrl, () => ( (contentRefs === void 0 && entityType && entityId !== void 0 && entityId !== null && entityId !== "" && !suggestionsDisabled ? _chunk2HWN6CYIcjs.buildSuggestionUrl.call(void 0, "/api/related-content", {
300
300
  apiBaseUrl,
301
301
  entityType,
302
302
  entityId,
@@ -315,7 +315,7 @@ function RelatedContentSection({
315
315
  );
316
316
  const { data, isLoading } = _chunkQ6RC3YGWcjs.useSelfFetch.call(void 0, suggestUrl, { initialData });
317
317
  const effectiveBuildListUrl = _react.useMemo.call(void 0,
318
- () => _nullishCoalesce(buildListUrl2, () => ( ((type, ids) => _chunkM3SHPHQ7cjs.buildListUrl.call(void 0, type, ids, apiBaseUrl)))),
318
+ () => _nullishCoalesce(buildListUrl2, () => ( ((type, ids) => _chunk2HWN6CYIcjs.buildListUrl.call(void 0, type, ids, apiBaseUrl)))),
319
319
  [buildListUrl2, apiBaseUrl]
320
320
  );
321
321
  const refs = _nullishCoalesce(_nullishCoalesce(contentRefs, () => ( _optionalChain([data, 'optionalAccess', _14 => _14.refs]))), () => ( []));
@@ -340,12 +340,12 @@ function RelatedContentSection({
340
340
  if (!grouped[ref.type]) grouped[ref.type] = [];
341
341
  grouped[ref.type].push(ref);
342
342
  }
343
- let orderedTypes = _chunkM3SHPHQ7cjs.orderContentRefTypes.call(void 0, Object.keys(grouped));
343
+ let orderedTypes = _chunk2HWN6CYIcjs.orderContentRefTypes.call(void 0, Object.keys(grouped));
344
344
  if (entityType) {
345
- const canonicalEntityType = _chunkM3SHPHQ7cjs.canonicalContentRefType.call(void 0, entityType);
346
- const sameType = orderedTypes.filter((t) => _chunkM3SHPHQ7cjs.canonicalContentRefType.call(void 0, t) === canonicalEntityType);
345
+ const canonicalEntityType = _chunk2HWN6CYIcjs.canonicalContentRefType.call(void 0, entityType);
346
+ const sameType = orderedTypes.filter((t) => _chunk2HWN6CYIcjs.canonicalContentRefType.call(void 0, t) === canonicalEntityType);
347
347
  if (sameType.length > 0) {
348
- orderedTypes = [...sameType, ...orderedTypes.filter((t) => _chunkM3SHPHQ7cjs.canonicalContentRefType.call(void 0, t) !== canonicalEntityType)];
348
+ orderedTypes = [...sameType, ...orderedTypes.filter((t) => _chunk2HWN6CYIcjs.canonicalContentRefType.call(void 0, t) !== canonicalEntityType)];
349
349
  }
350
350
  }
351
351
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-8", children: [
@@ -361,7 +361,7 @@ function RelatedContentSection({
361
361
  LinkProvider,
362
362
  extras,
363
363
  adminCampaignCard,
364
- heading: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { className: "text-h5 font-semibold text-ods-text-secondary", children: _chunkM3SHPHQ7cjs.getContentRefLabelOrTitleCase.call(void 0, type) })
364
+ heading: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { className: "text-h5 font-semibold text-ods-text-secondary", children: _chunk2HWN6CYIcjs.getContentRefLabelOrTitleCase.call(void 0, type) })
365
365
  },
366
366
  type
367
367
  ))
@@ -372,4 +372,4 @@ function RelatedContentSection({
372
372
 
373
373
 
374
374
  exports.GROUP_PAGE_SIZE = GROUP_PAGE_SIZE; exports.RelatedContentSection = RelatedContentSection;
375
- //# sourceMappingURL=chunk-CCV3SQ4W.cjs.map
375
+ //# sourceMappingURL=chunk-WZWQWZFJ.cjs.map