@flamingo-stack/openframe-frontend-core 0.0.315 → 0.0.316
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.
- package/dist/{chunk-7U4YFQX2.js → chunk-2Y4DLBFO.js} +96 -92
- package/dist/{chunk-7U4YFQX2.js.map → chunk-2Y4DLBFO.js.map} +1 -1
- package/dist/{chunk-HATCNFQL.cjs → chunk-4MCMPYEM.cjs} +12 -12
- package/dist/{chunk-HATCNFQL.cjs.map → chunk-4MCMPYEM.cjs.map} +1 -1
- package/dist/{chunk-VY5YF4B7.js → chunk-4NVA6W3J.js} +27 -22
- package/dist/chunk-4NVA6W3J.js.map +1 -0
- package/dist/chunk-4V3TCOFC.cjs +394 -0
- package/dist/chunk-4V3TCOFC.cjs.map +1 -0
- package/dist/{chunk-A2H6TFS4.cjs → chunk-63A53WQN.cjs} +33 -33
- package/dist/{chunk-A2H6TFS4.cjs.map → chunk-63A53WQN.cjs.map} +1 -1
- package/dist/{chunk-6W54MBU2.js → chunk-64DZ2J7Q.js} +5 -5
- package/dist/{chunk-47JZOP7Y.js → chunk-6KERXOFE.js} +3 -3
- package/dist/{chunk-JALO4TAZ.js → chunk-AI5X5JTD.js} +4 -4
- package/dist/chunk-CSLMCBZV.js +1464 -0
- package/dist/chunk-CSLMCBZV.js.map +1 -0
- package/dist/{chunk-BSAFGQVW.cjs → chunk-CUNMBP3A.cjs} +13 -13
- package/dist/{chunk-BSAFGQVW.cjs.map → chunk-CUNMBP3A.cjs.map} +1 -1
- package/dist/{chunk-TVNILN2F.cjs → chunk-DHVL36CA.cjs} +40 -40
- package/dist/{chunk-TVNILN2F.cjs.map → chunk-DHVL36CA.cjs.map} +1 -1
- package/dist/chunk-FCEVVNWY.cjs +1916 -0
- package/dist/chunk-FCEVVNWY.cjs.map +1 -0
- package/dist/chunk-FOVX3W3C.cjs +1464 -0
- package/dist/chunk-FOVX3W3C.cjs.map +1 -0
- package/dist/{chunk-4D37W55K.js → chunk-GHVVOST5.js} +95 -116
- package/dist/chunk-GHVVOST5.js.map +1 -0
- package/dist/{chunk-TRSDXD23.js → chunk-JAZM3A7E.js} +2 -2
- package/dist/{chunk-TK6OABYF.js → chunk-JEBL5PQK.js} +21 -35
- package/dist/{chunk-TK6OABYF.js.map → chunk-JEBL5PQK.js.map} +1 -1
- package/dist/{chunk-5ATH263N.cjs → chunk-L5JSGNT3.cjs} +35 -35
- package/dist/{chunk-5ATH263N.cjs.map → chunk-L5JSGNT3.cjs.map} +1 -1
- package/dist/{chunk-TQ7CMFSY.cjs → chunk-LAMDFGE3.cjs} +41 -36
- package/dist/chunk-LAMDFGE3.cjs.map +1 -0
- package/dist/{chunk-V4IIBNTA.js → chunk-LQHMXPOJ.js} +5 -5
- package/dist/{chunk-LGLPNWS6.cjs → chunk-LWNPMLIH.cjs} +3 -3
- package/dist/{chunk-LGLPNWS6.cjs.map → chunk-LWNPMLIH.cjs.map} +1 -1
- package/dist/chunk-M3NULYCR.js +1916 -0
- package/dist/chunk-M3NULYCR.js.map +1 -0
- package/dist/{chunk-MOOV4ORG.js → chunk-OKGZK6TT.js} +3 -3
- package/dist/{chunk-WFHNXCI3.cjs → chunk-OLEW7FYZ.cjs} +123 -144
- package/dist/chunk-OLEW7FYZ.cjs.map +1 -0
- package/dist/chunk-PIJ4JLJU.js +394 -0
- package/dist/chunk-PIJ4JLJU.js.map +1 -0
- package/dist/{chunk-E4CQ4RUG.js → chunk-Q4AMYLKX.js} +11 -11
- package/dist/{chunk-FQOTC3UU.cjs → chunk-QJGRP2YE.cjs} +4 -4
- package/dist/{chunk-FQOTC3UU.cjs.map → chunk-QJGRP2YE.cjs.map} +1 -1
- package/dist/{chunk-ZPK5HW7B.cjs → chunk-UGDGUO26.cjs} +3 -3
- package/dist/{chunk-ZPK5HW7B.cjs.map → chunk-UGDGUO26.cjs.map} +1 -1
- package/dist/{chunk-QW6OL4NY.cjs → chunk-VCE3ZEN3.cjs} +5 -5
- package/dist/{chunk-QW6OL4NY.cjs.map → chunk-VCE3ZEN3.cjs.map} +1 -1
- package/dist/{chunk-2JPSWDSM.cjs → chunk-XAQJ4ZLY.cjs} +447 -443
- package/dist/{chunk-2JPSWDSM.cjs.map → chunk-XAQJ4ZLY.cjs.map} +1 -1
- package/dist/{chunk-2MLMZAK4.js → chunk-YFGDZFUG.js} +4 -4
- package/dist/{chunk-VFIWQGJZ.js → chunk-Z3YORGG4.js} +2 -2
- package/dist/{chunk-OSEKWT6X.cjs → chunk-ZYGVJXJ5.cjs} +33 -47
- package/dist/chunk-ZYGVJXJ5.cjs.map +1 -0
- package/dist/components/case-studies/index.cjs +18 -18
- package/dist/components/case-studies/index.cjs.map +1 -1
- package/dist/components/case-studies/index.js +8 -8
- package/dist/components/chat/index.cjs +8 -8
- package/dist/components/chat/index.js +7 -7
- package/dist/components/contact/index.cjs +9 -9
- package/dist/components/contact/index.js +8 -8
- package/dist/components/docs/doc-viewer.d.ts +4 -0
- package/dist/components/docs/doc-viewer.d.ts.map +1 -1
- package/dist/components/docs/index.cjs +11 -11
- package/dist/components/docs/index.js +10 -10
- package/dist/components/embeds/index.cjs +9 -9
- package/dist/components/embeds/index.js +8 -8
- package/dist/components/faq/faq-document-page.d.ts +18 -20
- package/dist/components/faq/faq-document-page.d.ts.map +1 -1
- package/dist/components/faq/index.cjs +10 -10
- package/dist/components/faq/index.js +9 -9
- package/dist/components/features/index.cjs +8 -8
- package/dist/components/features/index.js +7 -7
- package/dist/components/help-center-pages/delivery-page.d.ts +27 -0
- package/dist/components/help-center-pages/delivery-page.d.ts.map +1 -0
- package/dist/components/help-center-pages/index.cjs +164 -0
- package/dist/components/help-center-pages/index.cjs.map +1 -0
- package/dist/components/help-center-pages/index.d.ts +25 -0
- package/dist/components/help-center-pages/index.d.ts.map +1 -0
- package/dist/components/help-center-pages/index.js +164 -0
- package/dist/components/help-center-pages/index.js.map +1 -0
- package/dist/components/help-center-pages/onboarding-guides-catalog-page.d.ts +41 -0
- package/dist/components/help-center-pages/onboarding-guides-catalog-page.d.ts.map +1 -0
- package/dist/components/help-center-pages/product-releases-list-page.d.ts +34 -0
- package/dist/components/help-center-pages/product-releases-list-page.d.ts.map +1 -0
- package/dist/components/help-center-pages/roadmap-page.d.ts +40 -0
- package/dist/components/help-center-pages/roadmap-page.d.ts.map +1 -0
- package/dist/components/icons/index.cjs +3 -3
- package/dist/components/icons/index.js +2 -2
- package/dist/components/index.cjs +177 -1555
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +348 -1726
- package/dist/components/index.js.map +1 -1
- package/dist/components/layout/page-layout.d.ts +4 -1
- package/dist/components/layout/page-layout.d.ts.map +1 -1
- package/dist/components/layout/title-block.d.ts +5 -1
- package/dist/components/layout/title-block.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +8 -8
- package/dist/components/navigation/index.js +7 -7
- package/dist/components/onboarding-guides/index.cjs +15 -364
- package/dist/components/onboarding-guides/index.cjs.map +1 -1
- package/dist/components/onboarding-guides/index.js +20 -369
- package/dist/components/onboarding-guides/index.js.map +1 -1
- package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts +9 -1
- package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
- package/dist/components/related-content/index.cjs +10 -10
- package/dist/components/related-content/index.js +9 -9
- package/dist/components/shared/dev-section/dev-section-page.d.ts +7 -1
- package/dist/components/shared/dev-section/dev-section-page.d.ts.map +1 -1
- package/dist/components/shared/dev-section/dev-section-view.d.ts +7 -1
- package/dist/components/shared/dev-section/dev-section-view.d.ts.map +1 -1
- package/dist/components/shared/legal-document/legal-document-page.d.ts +5 -1
- package/dist/components/shared/legal-document/legal-document-page.d.ts.map +1 -1
- package/dist/components/shared/product-release/release-detail-page.d.ts +11 -2
- package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
- package/dist/components/tickets/help-center-list.d.ts +5 -1
- package/dist/components/tickets/help-center-list.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +15 -1882
- package/dist/components/tickets/index.cjs.map +1 -1
- package/dist/components/tickets/index.js +28 -1895
- package/dist/components/tickets/index.js.map +1 -1
- package/dist/components/ui/file-manager/index.cjs +53 -53
- package/dist/components/ui/file-manager/index.cjs.map +1 -1
- package/dist/components/ui/file-manager/index.js +4 -4
- package/dist/components/ui/index.cjs +8 -8
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +7 -7
- package/dist/hooks/index.cjs +5 -5
- package/dist/hooks/index.js +4 -4
- package/dist/index.cjs +10 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +9 -9
- package/package.json +7 -1
- package/src/components/docs/doc-viewer.tsx +21 -34
- package/src/components/faq/faq-document-page.tsx +33 -60
- package/src/components/help-center-pages/delivery-page.tsx +45 -0
- package/src/components/help-center-pages/index.ts +41 -0
- package/src/components/help-center-pages/onboarding-guides-catalog-page.tsx +66 -0
- package/src/components/help-center-pages/product-releases-list-page.tsx +58 -0
- package/src/components/help-center-pages/roadmap-page.tsx +68 -0
- package/src/components/layout/page-layout.tsx +11 -0
- package/src/components/layout/title-block.tsx +15 -2
- package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +30 -19
- package/src/components/shared/dev-section/dev-section-page.tsx +29 -19
- package/src/components/shared/dev-section/dev-section-view.tsx +26 -19
- package/src/components/shared/legal-document/legal-document-page.tsx +19 -23
- package/src/components/shared/product-release/release-detail-page.tsx +36 -36
- package/src/components/tickets/help-center-list.tsx +11 -3
- package/dist/chunk-4D37W55K.js.map +0 -1
- package/dist/chunk-OSEKWT6X.cjs.map +0 -1
- package/dist/chunk-TQ7CMFSY.cjs.map +0 -1
- package/dist/chunk-VY5YF4B7.js.map +0 -1
- package/dist/chunk-WFHNXCI3.cjs.map +0 -1
- /package/dist/{chunk-6W54MBU2.js.map → chunk-64DZ2J7Q.js.map} +0 -0
- /package/dist/{chunk-47JZOP7Y.js.map → chunk-6KERXOFE.js.map} +0 -0
- /package/dist/{chunk-JALO4TAZ.js.map → chunk-AI5X5JTD.js.map} +0 -0
- /package/dist/{chunk-TRSDXD23.js.map → chunk-JAZM3A7E.js.map} +0 -0
- /package/dist/{chunk-V4IIBNTA.js.map → chunk-LQHMXPOJ.js.map} +0 -0
- /package/dist/{chunk-MOOV4ORG.js.map → chunk-OKGZK6TT.js.map} +0 -0
- /package/dist/{chunk-E4CQ4RUG.js.map → chunk-Q4AMYLKX.js.map} +0 -0
- /package/dist/{chunk-2MLMZAK4.js.map → chunk-YFGDZFUG.js.map} +0 -0
- /package/dist/{chunk-VFIWQGJZ.js.map → chunk-Z3YORGG4.js.map} +0 -0
|
@@ -0,0 +1,1464 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
DeliveryRow,
|
|
4
|
+
EmptyState,
|
|
5
|
+
init_unified_pagination,
|
|
6
|
+
unified_pagination_exports
|
|
7
|
+
} from "./chunk-4NVA6W3J.js";
|
|
8
|
+
import {
|
|
9
|
+
DetailPageSkeleton
|
|
10
|
+
} from "./chunk-Z3YORGG4.js";
|
|
11
|
+
import {
|
|
12
|
+
useSelfFetch
|
|
13
|
+
} from "./chunk-JAZM3A7E.js";
|
|
14
|
+
import {
|
|
15
|
+
Accordion,
|
|
16
|
+
AccordionContent,
|
|
17
|
+
AccordionItem,
|
|
18
|
+
AccordionTrigger,
|
|
19
|
+
Card,
|
|
20
|
+
CardContent,
|
|
21
|
+
DEV_SECTION_PARAM_KEYS,
|
|
22
|
+
EntityMetadataAuthorCell,
|
|
23
|
+
EntityTagBadges,
|
|
24
|
+
ImageGalleryModal,
|
|
25
|
+
LoadError,
|
|
26
|
+
PageLayout,
|
|
27
|
+
PageShell,
|
|
28
|
+
ProductReleaseCard,
|
|
29
|
+
ProductReleaseCardSkeleton,
|
|
30
|
+
ReleaseChangelogSection,
|
|
31
|
+
RichMarkdownRenderer,
|
|
32
|
+
RoadmapCard,
|
|
33
|
+
Video,
|
|
34
|
+
buildProductReleaseCardProps,
|
|
35
|
+
devSectionAnchorId,
|
|
36
|
+
executeNavigationImperative,
|
|
37
|
+
formatLegalDate,
|
|
38
|
+
formatReleaseDate,
|
|
39
|
+
isModifierClick,
|
|
40
|
+
resolveContentHref,
|
|
41
|
+
useEntityCardLink
|
|
42
|
+
} from "./chunk-2Y4DLBFO.js";
|
|
43
|
+
import {
|
|
44
|
+
STICKY_HEADER_OFFSET_PX,
|
|
45
|
+
contentFetch,
|
|
46
|
+
useScrollToHash
|
|
47
|
+
} from "./chunk-Q4AMYLKX.js";
|
|
48
|
+
import {
|
|
49
|
+
useChatRuntime
|
|
50
|
+
} from "./chunk-2FI3USTC.js";
|
|
51
|
+
import {
|
|
52
|
+
init_next_navigation,
|
|
53
|
+
usePathname,
|
|
54
|
+
useRouter,
|
|
55
|
+
useSearchParams
|
|
56
|
+
} from "./chunk-PLJLE4A4.js";
|
|
57
|
+
import {
|
|
58
|
+
init_next_link,
|
|
59
|
+
next_link_default
|
|
60
|
+
} from "./chunk-OHPI2HRK.js";
|
|
61
|
+
import {
|
|
62
|
+
GitHubIcon
|
|
63
|
+
} from "./chunk-LQHMXPOJ.js";
|
|
64
|
+
import {
|
|
65
|
+
cn,
|
|
66
|
+
init_cn
|
|
67
|
+
} from "./chunk-XTCBRQN2.js";
|
|
68
|
+
import {
|
|
69
|
+
__toCommonJS
|
|
70
|
+
} from "./chunk-GGWZFCYS.js";
|
|
71
|
+
|
|
72
|
+
// src/components/persistent-pagination.tsx
|
|
73
|
+
init_cn();
|
|
74
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
75
|
+
function PersistentPagination({
|
|
76
|
+
isLoading,
|
|
77
|
+
children,
|
|
78
|
+
currentPage,
|
|
79
|
+
totalPages,
|
|
80
|
+
className,
|
|
81
|
+
disabledOpacity = 0.5,
|
|
82
|
+
transitionDuration = 300,
|
|
83
|
+
showLoadingState = true
|
|
84
|
+
}) {
|
|
85
|
+
return /* @__PURE__ */ jsxs(
|
|
86
|
+
"div",
|
|
87
|
+
{
|
|
88
|
+
className: cn(
|
|
89
|
+
"relative transition-all ease-in-out",
|
|
90
|
+
"flex justify-center items-center",
|
|
91
|
+
isLoading && "pointer-events-none",
|
|
92
|
+
className
|
|
93
|
+
),
|
|
94
|
+
style: {
|
|
95
|
+
opacity: isLoading ? disabledOpacity : 1,
|
|
96
|
+
transitionDuration: `${transitionDuration}ms`
|
|
97
|
+
},
|
|
98
|
+
role: "navigation",
|
|
99
|
+
"aria-label": "Pagination",
|
|
100
|
+
"aria-busy": isLoading,
|
|
101
|
+
"data-loading": isLoading,
|
|
102
|
+
children: [
|
|
103
|
+
isLoading && /* @__PURE__ */ jsxs(
|
|
104
|
+
"div",
|
|
105
|
+
{
|
|
106
|
+
className: "sr-only",
|
|
107
|
+
role: "status",
|
|
108
|
+
"aria-live": "polite",
|
|
109
|
+
children: [
|
|
110
|
+
"Pagination temporarily disabled while loading page ",
|
|
111
|
+
currentPage,
|
|
112
|
+
" of ",
|
|
113
|
+
totalPages
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
),
|
|
117
|
+
/* @__PURE__ */ jsx(
|
|
118
|
+
"div",
|
|
119
|
+
{
|
|
120
|
+
className: cn(
|
|
121
|
+
"relative transition-all ease-in-out",
|
|
122
|
+
isLoading && "cursor-not-allowed"
|
|
123
|
+
),
|
|
124
|
+
style: {
|
|
125
|
+
transitionDuration: `${transitionDuration}ms`
|
|
126
|
+
},
|
|
127
|
+
"aria-hidden": isLoading,
|
|
128
|
+
children
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
function usePaginationLoading(isLoading, currentPage, totalPages) {
|
|
136
|
+
const shouldShowPagination = totalPages > 1 || isLoading;
|
|
137
|
+
const paginationProps = {
|
|
138
|
+
"aria-busy": isLoading,
|
|
139
|
+
"data-loading": isLoading,
|
|
140
|
+
"data-current-page": currentPage,
|
|
141
|
+
"data-total-pages": totalPages
|
|
142
|
+
};
|
|
143
|
+
const getLoadingMessage = () => {
|
|
144
|
+
if (isLoading) {
|
|
145
|
+
return `Loading page ${currentPage} of ${totalPages}`;
|
|
146
|
+
}
|
|
147
|
+
return `Page ${currentPage} of ${totalPages}`;
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
shouldShowPagination,
|
|
151
|
+
paginationProps,
|
|
152
|
+
getLoadingMessage
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function PersistentPaginationWrapper({
|
|
156
|
+
isLoading,
|
|
157
|
+
currentPage,
|
|
158
|
+
totalPages,
|
|
159
|
+
onPageChange,
|
|
160
|
+
className,
|
|
161
|
+
variant = "vendor"
|
|
162
|
+
}) {
|
|
163
|
+
const { getLoadingMessage } = usePaginationLoading(
|
|
164
|
+
isLoading,
|
|
165
|
+
currentPage,
|
|
166
|
+
totalPages
|
|
167
|
+
);
|
|
168
|
+
const hasResults = totalPages > 0;
|
|
169
|
+
const displayTotalPages = hasResults ? totalPages : 1;
|
|
170
|
+
const displayCurrentPage = hasResults ? currentPage : 1;
|
|
171
|
+
const isPaginationDisabled = isLoading;
|
|
172
|
+
const hasNoResults = !hasResults && !isLoading;
|
|
173
|
+
const PaginationComponent = (init_unified_pagination(), __toCommonJS(unified_pagination_exports)).UnifiedPagination;
|
|
174
|
+
return /* @__PURE__ */ jsxs(
|
|
175
|
+
"div",
|
|
176
|
+
{
|
|
177
|
+
className: cn(
|
|
178
|
+
"relative transition-all ease-in-out flex justify-center items-center",
|
|
179
|
+
(isPaginationDisabled || hasNoResults) && "pointer-events-none",
|
|
180
|
+
className
|
|
181
|
+
),
|
|
182
|
+
style: {
|
|
183
|
+
opacity: isPaginationDisabled ? 0.3 : hasNoResults ? 0.5 : 1,
|
|
184
|
+
transitionDuration: "300ms"
|
|
185
|
+
},
|
|
186
|
+
role: "navigation",
|
|
187
|
+
"aria-label": "Pagination",
|
|
188
|
+
"aria-busy": isLoading,
|
|
189
|
+
"data-loading": isLoading,
|
|
190
|
+
"data-has-results": hasResults,
|
|
191
|
+
children: [
|
|
192
|
+
(isLoading || !hasResults) && /* @__PURE__ */ jsx(
|
|
193
|
+
"div",
|
|
194
|
+
{
|
|
195
|
+
className: "sr-only",
|
|
196
|
+
role: "status",
|
|
197
|
+
"aria-live": "polite",
|
|
198
|
+
children: isLoading ? `Pagination temporarily disabled while loading page ${displayCurrentPage} of ${displayTotalPages}` : `No results available - pagination disabled`
|
|
199
|
+
}
|
|
200
|
+
),
|
|
201
|
+
/* @__PURE__ */ jsx(
|
|
202
|
+
"div",
|
|
203
|
+
{
|
|
204
|
+
className: cn(
|
|
205
|
+
"relative transition-all ease-in-out",
|
|
206
|
+
isPaginationDisabled && "cursor-not-allowed"
|
|
207
|
+
),
|
|
208
|
+
style: {
|
|
209
|
+
transitionDuration: "300ms"
|
|
210
|
+
},
|
|
211
|
+
"aria-hidden": isPaginationDisabled,
|
|
212
|
+
children: /* @__PURE__ */ jsx(
|
|
213
|
+
PaginationComponent,
|
|
214
|
+
{
|
|
215
|
+
currentPage: displayCurrentPage,
|
|
216
|
+
totalPages: displayTotalPages,
|
|
217
|
+
onPageChange: hasResults ? onPageChange : () => {
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/components/shared/product-release/product-releases-view.tsx
|
|
229
|
+
init_cn();
|
|
230
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
231
|
+
var DEFAULT_ENDPOINT = "/api/releases";
|
|
232
|
+
var DEFAULT_SEARCH_PARAM_KEY = DEV_SECTION_PARAM_KEYS.search;
|
|
233
|
+
var DEFAULT_STATUS_PARAM_KEY = DEV_SECTION_PARAM_KEYS.releaseStatus;
|
|
234
|
+
var DEFAULT_PAGE_PARAM_KEY = "page";
|
|
235
|
+
function ReleaseRow({
|
|
236
|
+
release,
|
|
237
|
+
basePath,
|
|
238
|
+
buildCardProps
|
|
239
|
+
}) {
|
|
240
|
+
const runtime = useChatRuntime();
|
|
241
|
+
const router = useRouter();
|
|
242
|
+
const cta = resolveContentHref(runtime?.composeContentUrl, {
|
|
243
|
+
type: "product_release",
|
|
244
|
+
slug: release.slug,
|
|
245
|
+
basePath,
|
|
246
|
+
platforms: release.product_release_platforms
|
|
247
|
+
});
|
|
248
|
+
const { target, rel } = useEntityCardLink({ href: cta.href, targetPlatform: cta.targetPlatform });
|
|
249
|
+
const onClick = (e) => {
|
|
250
|
+
if (e.defaultPrevented || isModifierClick(e) || target === "_blank") return;
|
|
251
|
+
e.preventDefault();
|
|
252
|
+
executeNavigationImperative({
|
|
253
|
+
runtime,
|
|
254
|
+
href: cta.href,
|
|
255
|
+
targetPlatform: cta.targetPlatform,
|
|
256
|
+
fallbackNavigate: router.push
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
return /* @__PURE__ */ jsx2(
|
|
260
|
+
ProductReleaseCard,
|
|
261
|
+
{
|
|
262
|
+
size: "lg",
|
|
263
|
+
title: release.title,
|
|
264
|
+
summary: release.summary,
|
|
265
|
+
version: release.version,
|
|
266
|
+
...buildCardProps(release),
|
|
267
|
+
anchorProps: { href: cta.href, target, rel, onClick }
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
function ProductReleasesView({
|
|
272
|
+
endpoint = DEFAULT_ENDPOINT,
|
|
273
|
+
initialData,
|
|
274
|
+
itemsPerPage = 5,
|
|
275
|
+
basePath = "/releases",
|
|
276
|
+
buildCardProps = buildProductReleaseCardProps,
|
|
277
|
+
searchParamKey = DEFAULT_SEARCH_PARAM_KEY,
|
|
278
|
+
statusParamKey = DEFAULT_STATUS_PARAM_KEY,
|
|
279
|
+
pageParamKey = DEFAULT_PAGE_PARAM_KEY,
|
|
280
|
+
className
|
|
281
|
+
} = {}) {
|
|
282
|
+
const searchParams = useSearchParams();
|
|
283
|
+
const router = useRouter();
|
|
284
|
+
const pathname = usePathname();
|
|
285
|
+
const search = searchParams.get(searchParamKey) || "";
|
|
286
|
+
const status = searchParams.get(statusParamKey) || "all";
|
|
287
|
+
const currentPage = Math.max(1, parseInt(searchParams.get(pageParamKey) || "1", 10) || 1);
|
|
288
|
+
const offset = (currentPage - 1) * itemsPerPage;
|
|
289
|
+
const listParams = new URLSearchParams({ limit: String(itemsPerPage), offset: String(offset) });
|
|
290
|
+
if (search) listParams.set(searchParamKey, search);
|
|
291
|
+
if (status && status !== "all") listParams.set(statusParamKey, status);
|
|
292
|
+
const { data, isLoading, error, reload } = useSelfFetch(
|
|
293
|
+
`${endpoint}?${listParams.toString()}`,
|
|
294
|
+
{ initialData }
|
|
295
|
+
);
|
|
296
|
+
const releases = data?.data ?? [];
|
|
297
|
+
const totalCount = data?.count ?? 0;
|
|
298
|
+
const totalPages = Math.ceil(totalCount / itemsPerPage);
|
|
299
|
+
const hasActiveFilters = search !== "" || status !== "all";
|
|
300
|
+
const showEmpty = !isLoading && !error && releases.length === 0;
|
|
301
|
+
const goToPage = (page) => {
|
|
302
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
303
|
+
params.set(pageParamKey, String(page));
|
|
304
|
+
router.replace(`${pathname}?${params.toString()}`, { scroll: false });
|
|
305
|
+
};
|
|
306
|
+
const resetFilters = () => {
|
|
307
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
308
|
+
params.delete(searchParamKey);
|
|
309
|
+
params.delete(statusParamKey);
|
|
310
|
+
params.delete(pageParamKey);
|
|
311
|
+
router.replace(`${pathname}?${params.toString()}`, { scroll: false });
|
|
312
|
+
};
|
|
313
|
+
if (error) {
|
|
314
|
+
return /* @__PURE__ */ jsx2("div", { className: cn("w-full", className), children: /* @__PURE__ */ jsx2(LoadError, { message: "Failed to load releases.", onRetry: reload }) });
|
|
315
|
+
}
|
|
316
|
+
return /* @__PURE__ */ jsx2("div", { className: cn("w-full flex flex-col gap-[40px]", className), children: /* @__PURE__ */ jsx2("div", { className: "min-h-[600px]", children: showEmpty ? /* @__PURE__ */ jsx2("div", { className: "h-[600px] flex items-center justify-center", children: hasActiveFilters ? /* @__PURE__ */ jsx2(
|
|
317
|
+
EmptyState,
|
|
318
|
+
{
|
|
319
|
+
type: "search",
|
|
320
|
+
title: "No releases found",
|
|
321
|
+
description: "No releases match your current filters. Try adjusting your search or status filter.",
|
|
322
|
+
showCTA: true,
|
|
323
|
+
ctaText: "Reset Filters",
|
|
324
|
+
onCtaClick: resetFilters
|
|
325
|
+
}
|
|
326
|
+
) : /* @__PURE__ */ jsx2(
|
|
327
|
+
EmptyState,
|
|
328
|
+
{
|
|
329
|
+
type: "generic",
|
|
330
|
+
title: "No releases available",
|
|
331
|
+
description: "Check back soon for product updates!",
|
|
332
|
+
showCTA: false
|
|
333
|
+
}
|
|
334
|
+
) }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
335
|
+
/* @__PURE__ */ jsx2("div", { className: "flex flex-col gap-6", children: Array.from({ length: itemsPerPage }).map((_, i) => {
|
|
336
|
+
const release = releases[i];
|
|
337
|
+
const hasData = !!release;
|
|
338
|
+
return /* @__PURE__ */ jsx2(
|
|
339
|
+
"div",
|
|
340
|
+
{
|
|
341
|
+
style: { visibility: isLoading || hasData ? "visible" : "hidden" },
|
|
342
|
+
children: isLoading ? /* @__PURE__ */ jsx2(ProductReleaseCardSkeleton, { size: "lg" }) : release ? /* @__PURE__ */ jsx2(ReleaseRow, { release, basePath, buildCardProps }) : /* @__PURE__ */ jsx2(ProductReleaseCardSkeleton, { size: "lg" })
|
|
343
|
+
},
|
|
344
|
+
release?.id ?? `slot-${i}`
|
|
345
|
+
);
|
|
346
|
+
}) }),
|
|
347
|
+
/* @__PURE__ */ jsx2("div", { className: "mt-6 md:mt-8 flex justify-center", children: isLoading ? /* @__PURE__ */ jsx2("div", { className: "h-12 m-3 w-64" }) : releases.length > 0 && totalPages > 1 ? /* @__PURE__ */ jsx2(
|
|
348
|
+
PersistentPaginationWrapper,
|
|
349
|
+
{
|
|
350
|
+
isLoading: false,
|
|
351
|
+
currentPage,
|
|
352
|
+
totalPages,
|
|
353
|
+
onPageChange: goToPage,
|
|
354
|
+
variant: "blog"
|
|
355
|
+
}
|
|
356
|
+
) : /* @__PURE__ */ jsx2("div", { className: "h-12 m-3 w-64", style: { visibility: "hidden" } }) })
|
|
357
|
+
] }) }) });
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/components/shared/media-gallery-strip.tsx
|
|
361
|
+
import { useState } from "react";
|
|
362
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
363
|
+
function isGalleryImage(mediaType) {
|
|
364
|
+
return mediaType !== "video" && mediaType !== "demo";
|
|
365
|
+
}
|
|
366
|
+
function MediaGalleryStrip({ items, maxDisplay, className }) {
|
|
367
|
+
const [galleryOpen, setGalleryOpen] = useState(false);
|
|
368
|
+
const [galleryIndex, setGalleryIndex] = useState(0);
|
|
369
|
+
if (!items || items.length === 0) return null;
|
|
370
|
+
const display = typeof maxDisplay === "number" ? items.slice(0, Math.max(0, maxDisplay)) : items;
|
|
371
|
+
const galleryImages = display.filter((m) => isGalleryImage(m.media_type)).map((m) => m.media_url);
|
|
372
|
+
const tileClass = "shrink-0 w-[240px] h-[200px] rounded-md overflow-hidden border border-ods-border bg-black transition-opacity";
|
|
373
|
+
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
374
|
+
/* @__PURE__ */ jsx3("div", { className: `flex gap-6 overflow-x-auto w-full ${className ?? ""}`, children: display.map((mediaItem, index) => {
|
|
375
|
+
if (isGalleryImage(mediaItem.media_type)) {
|
|
376
|
+
return /* @__PURE__ */ jsx3(
|
|
377
|
+
"button",
|
|
378
|
+
{
|
|
379
|
+
type: "button",
|
|
380
|
+
className: `${tileClass} cursor-pointer hover:opacity-80`,
|
|
381
|
+
"aria-label": `Open ${mediaItem.title || `media ${index + 1}`} in gallery`,
|
|
382
|
+
onClick: () => {
|
|
383
|
+
setGalleryIndex(display.slice(0, index).filter((m) => isGalleryImage(m.media_type)).length);
|
|
384
|
+
setGalleryOpen(true);
|
|
385
|
+
},
|
|
386
|
+
children: /* @__PURE__ */ jsx3("img", { src: mediaItem.media_url, alt: mediaItem.title || `Media ${index + 1}`, className: "w-full h-full object-cover" })
|
|
387
|
+
},
|
|
388
|
+
mediaItem.id || index
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
return /* @__PURE__ */ jsx3("div", { className: tileClass, children: /* @__PURE__ */ jsx3(Video, { url: mediaItem.media_url, layout: "native" }) }, mediaItem.id || index);
|
|
392
|
+
}) }),
|
|
393
|
+
galleryImages.length > 0 && /* @__PURE__ */ jsx3(
|
|
394
|
+
ImageGalleryModal,
|
|
395
|
+
{
|
|
396
|
+
images: galleryImages,
|
|
397
|
+
isOpen: galleryOpen,
|
|
398
|
+
onClose: () => setGalleryOpen(false),
|
|
399
|
+
initialIndex: galleryIndex
|
|
400
|
+
}
|
|
401
|
+
)
|
|
402
|
+
] });
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// src/components/shared/product-release/release-detail-page.tsx
|
|
406
|
+
init_next_link();
|
|
407
|
+
init_next_navigation();
|
|
408
|
+
import { useState as useState2, useEffect } from "react";
|
|
409
|
+
import { AlertTriangle, ExternalLink, BookMarked, Sparkles, TrendingUp, Wrench } from "lucide-react";
|
|
410
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
411
|
+
var DefaultMarkdownRenderer = RichMarkdownRenderer;
|
|
412
|
+
function ReleaseDetailPage({
|
|
413
|
+
authorHref,
|
|
414
|
+
slug,
|
|
415
|
+
initialData,
|
|
416
|
+
useRelease,
|
|
417
|
+
MarkdownRenderer = DefaultMarkdownRenderer,
|
|
418
|
+
RoadmapSection,
|
|
419
|
+
DeliverySection,
|
|
420
|
+
VideoSection,
|
|
421
|
+
VideoDisplaySection,
|
|
422
|
+
roadmapApiEndpoint = "/api/roadmap",
|
|
423
|
+
deliveryApiEndpoint = "/api/delivery",
|
|
424
|
+
backButton,
|
|
425
|
+
relatedContent,
|
|
426
|
+
shell = true
|
|
427
|
+
}) {
|
|
428
|
+
const router = useRouter();
|
|
429
|
+
const renderShell = (node) => shell ? /* @__PURE__ */ jsx4(PageShell, { children: node }) : /* @__PURE__ */ jsx4("div", { className: "page-shell-content", children: node });
|
|
430
|
+
const { data: fetchedRelease, error, isLoading } = useRelease(initialData ? void 0 : slug);
|
|
431
|
+
const release = initialData || fetchedRelease;
|
|
432
|
+
const showBackButton = backButton !== false;
|
|
433
|
+
const backLabel = (backButton ? backButton.label : void 0) ?? "Back to home";
|
|
434
|
+
const backHref = (backButton ? backButton.href : void 0) ?? "/";
|
|
435
|
+
const [roadmapTasks, setRoadmapTasks] = useState2([]);
|
|
436
|
+
const [deliveryData, setDeliveryData] = useState2(null);
|
|
437
|
+
const [roadmapLoading, setRoadmapLoading] = useState2(false);
|
|
438
|
+
const [deliveryLoading, setDeliveryLoading] = useState2(false);
|
|
439
|
+
useEffect(() => {
|
|
440
|
+
async function fetchLinkedTasks() {
|
|
441
|
+
if (!release) return;
|
|
442
|
+
try {
|
|
443
|
+
const roadmapTasksData = release.clickup_roadmap_tasks;
|
|
444
|
+
if (roadmapTasksData && roadmapTasksData.length > 0 && RoadmapSection) {
|
|
445
|
+
setRoadmapLoading(true);
|
|
446
|
+
const roadmapIds = roadmapTasksData.map((t) => t.clickup_task_id).join(",");
|
|
447
|
+
const roadmapResponse = await contentFetch(`${roadmapApiEndpoint}?task_ids=${roadmapIds}`);
|
|
448
|
+
const roadmapData = await roadmapResponse.json();
|
|
449
|
+
setRoadmapTasks(roadmapData.items || []);
|
|
450
|
+
setRoadmapLoading(false);
|
|
451
|
+
}
|
|
452
|
+
const deliveryTasksData = release.clickup_delivery_tasks;
|
|
453
|
+
if (deliveryTasksData && deliveryTasksData.length > 0 && DeliverySection) {
|
|
454
|
+
setDeliveryLoading(true);
|
|
455
|
+
const deliveryIds = deliveryTasksData.map((t) => t.clickup_task_id).join(",");
|
|
456
|
+
const deliveryResponse = await contentFetch(`${deliveryApiEndpoint}?task_ids=${deliveryIds}`);
|
|
457
|
+
const deliveryResponseData = await deliveryResponse.json();
|
|
458
|
+
setDeliveryData(deliveryResponseData);
|
|
459
|
+
setDeliveryLoading(false);
|
|
460
|
+
}
|
|
461
|
+
} catch (err) {
|
|
462
|
+
console.error("Error fetching linked tasks:", err);
|
|
463
|
+
setRoadmapLoading(false);
|
|
464
|
+
setDeliveryLoading(false);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
fetchLinkedTasks();
|
|
468
|
+
}, [release, RoadmapSection, DeliverySection, roadmapApiEndpoint, deliveryApiEndpoint]);
|
|
469
|
+
if (!initialData && isLoading) {
|
|
470
|
+
return renderShell(
|
|
471
|
+
// Match the loaded page's top offset (TitleBlock's
|
|
472
|
+
// `pt-[var(--spacing-system-l)]`) so content doesn't jump on load.
|
|
473
|
+
/* @__PURE__ */ jsx4("div", { className: "pt-[var(--spacing-system-l)]", children: /* @__PURE__ */ jsx4(DetailPageSkeleton, { bare: true, metadataColumns: 4, showImageGallery: true }) })
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
if (error || !release) {
|
|
477
|
+
return renderShell(
|
|
478
|
+
/* @__PURE__ */ jsxs4("div", { className: "text-center py-16", children: [
|
|
479
|
+
/* @__PURE__ */ jsx4("h1", { className: "text-4xl font-bold text-ods-text-primary mb-4", children: "Release Not Found" }),
|
|
480
|
+
/* @__PURE__ */ jsx4("p", { className: "text-xl text-ods-text-secondary", children: "The release you're looking for doesn't exist." })
|
|
481
|
+
] })
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
const hasBreakingChanges = Array.isArray(release.breaking_changes) && release.breaking_changes.length > 0;
|
|
485
|
+
const releaseTitle = release.title;
|
|
486
|
+
const releaseVersion = release.version;
|
|
487
|
+
const releaseSummary = release.summary;
|
|
488
|
+
const releaseContent = release.content;
|
|
489
|
+
const releaseDate = release.release_date;
|
|
490
|
+
const releaseType = release.release_type;
|
|
491
|
+
const releaseStatus = release.release_status;
|
|
492
|
+
const releaseMedia = release.release_media;
|
|
493
|
+
const author = release.author;
|
|
494
|
+
const githubReleases = release.github_releases;
|
|
495
|
+
const knowledgeBaseLinks = release.knowledge_base_links;
|
|
496
|
+
const migrationGuideUrl = release.migration_guide_url;
|
|
497
|
+
const documentationUrl = release.documentation_url;
|
|
498
|
+
const youtubeUrl = release.youtube_url;
|
|
499
|
+
const mainVideoUrl = release.main_video_url;
|
|
500
|
+
const videoBites = release.video_bites;
|
|
501
|
+
const highlightVideoUrl = release.highlight_video_url;
|
|
502
|
+
const highlightVideoThumbnail = release.highlight_video_thumbnail;
|
|
503
|
+
const breakingChanges = release.breaking_changes;
|
|
504
|
+
const featuresAdded = release.features_added;
|
|
505
|
+
const bugFixed = release.bugs_fixed;
|
|
506
|
+
const improvements = release.improvements;
|
|
507
|
+
return renderShell(
|
|
508
|
+
/* @__PURE__ */ jsxs4(
|
|
509
|
+
PageLayout,
|
|
510
|
+
{
|
|
511
|
+
title: releaseTitle,
|
|
512
|
+
subtitle: `Version: ${releaseVersion}`,
|
|
513
|
+
titleSize: "h1",
|
|
514
|
+
backButton: showBackButton ? { label: backLabel, onClick: () => router.push(backHref) } : void 0,
|
|
515
|
+
children: [
|
|
516
|
+
/* @__PURE__ */ jsxs4("div", { className: "space-y-6 md:space-y-8", children: [
|
|
517
|
+
/* @__PURE__ */ jsx4(EntityTagBadges, { tags: release.product_release_tags }),
|
|
518
|
+
/* @__PURE__ */ jsxs4("div", { className: "grid grid-cols-1 md:grid-cols-4 border border-ods-border rounded-md overflow-hidden w-full", children: [
|
|
519
|
+
/* @__PURE__ */ jsx4("div", { className: "bg-ods-card border-b md:border-b-0 md:border-r border-ods-border p-4 flex flex-col gap-3", children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-0", children: [
|
|
520
|
+
/* @__PURE__ */ jsx4("p", { className: "text-h4 text-ods-text-primary", children: releaseType.toLocaleUpperCase() }),
|
|
521
|
+
/* @__PURE__ */ jsx4("p", { className: "font-['DM_Sans'] font-medium text-[14px] leading-[20px] text-ods-text-secondary", children: "Release Type" })
|
|
522
|
+
] }) }),
|
|
523
|
+
/* @__PURE__ */ jsx4("div", { className: "bg-ods-card border-b md:border-b-0 md:border-r border-ods-border p-4 flex flex-col gap-3", children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-0", children: [
|
|
524
|
+
/* @__PURE__ */ jsx4("p", { className: "text-h4 text-ods-text-primary", children: releaseStatus.toLocaleUpperCase() }),
|
|
525
|
+
/* @__PURE__ */ jsx4("p", { className: "font-['DM_Sans'] font-medium text-[14px] leading-[20px] text-ods-text-secondary", children: "Release Status" })
|
|
526
|
+
] }) }),
|
|
527
|
+
/* @__PURE__ */ jsx4("div", { className: "bg-ods-card border-b md:border-b-0 md:border-r border-ods-border p-4 flex flex-col gap-3", children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-0", children: [
|
|
528
|
+
/* @__PURE__ */ jsx4("p", { className: "text-h4 text-ods-text-primary", children: formatReleaseDate(releaseDate) }),
|
|
529
|
+
/* @__PURE__ */ jsx4("p", { className: "font-['DM_Sans'] font-medium text-[14px] leading-[20px] text-ods-text-secondary", children: "Release Date" })
|
|
530
|
+
] }) }),
|
|
531
|
+
/* @__PURE__ */ jsx4(
|
|
532
|
+
EntityMetadataAuthorCell,
|
|
533
|
+
{
|
|
534
|
+
author: author ?? { full_name: null, avatar_url: null },
|
|
535
|
+
authorHref: author?.full_name ? authorHref : void 0
|
|
536
|
+
}
|
|
537
|
+
)
|
|
538
|
+
] }),
|
|
539
|
+
/* @__PURE__ */ jsx4(MediaGalleryStrip, { items: releaseMedia ?? [], maxDisplay: 5 }),
|
|
540
|
+
releaseSummary && /* @__PURE__ */ jsx4("div", { className: "text-h4 text-ods-text-primary", children: /* @__PURE__ */ jsx4("p", { children: releaseSummary }) }),
|
|
541
|
+
VideoDisplaySection ? /* @__PURE__ */ jsx4(
|
|
542
|
+
VideoDisplaySection,
|
|
543
|
+
{
|
|
544
|
+
mainVideoUrl,
|
|
545
|
+
youtubeUrl,
|
|
546
|
+
highlightVideoUrl,
|
|
547
|
+
highlightVideoThumbnail,
|
|
548
|
+
title: releaseTitle,
|
|
549
|
+
videoBites,
|
|
550
|
+
bitesTitle: "Video Clips",
|
|
551
|
+
filterPublishedBites: true,
|
|
552
|
+
srtContent: release?.srt_content,
|
|
553
|
+
captionsUrl: release?.captionsUrl
|
|
554
|
+
}
|
|
555
|
+
) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
556
|
+
youtubeUrl && /* @__PURE__ */ jsx4(
|
|
557
|
+
Video,
|
|
558
|
+
{
|
|
559
|
+
kind: "youtube",
|
|
560
|
+
url: youtubeUrl,
|
|
561
|
+
title: `${releaseTitle} - Video`,
|
|
562
|
+
layout: "native"
|
|
563
|
+
}
|
|
564
|
+
),
|
|
565
|
+
!youtubeUrl && mainVideoUrl && /* @__PURE__ */ jsx4(
|
|
566
|
+
Video,
|
|
567
|
+
{
|
|
568
|
+
url: mainVideoUrl,
|
|
569
|
+
srtContent: release?.srt_content,
|
|
570
|
+
captionsUrl: release?.captionsUrl,
|
|
571
|
+
layout: "centered"
|
|
572
|
+
}
|
|
573
|
+
),
|
|
574
|
+
highlightVideoUrl && /* @__PURE__ */ jsx4(
|
|
575
|
+
Video,
|
|
576
|
+
{
|
|
577
|
+
url: highlightVideoUrl,
|
|
578
|
+
poster: highlightVideoThumbnail,
|
|
579
|
+
layout: "centered"
|
|
580
|
+
}
|
|
581
|
+
)
|
|
582
|
+
] }),
|
|
583
|
+
releaseContent && /* @__PURE__ */ jsx4("div", { className: "text-h4 text-ods-text-primary", children: /* @__PURE__ */ jsx4(MarkdownRenderer, { content: releaseContent }) }),
|
|
584
|
+
hasBreakingChanges && /* @__PURE__ */ jsx4(Card, { className: "border-red-500 bg-red-500/10", children: /* @__PURE__ */ jsx4(CardContent, { className: "p-6", children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-3", children: [
|
|
585
|
+
/* @__PURE__ */ jsx4(AlertTriangle, { className: "h-6 w-6 text-red-500" }),
|
|
586
|
+
/* @__PURE__ */ jsxs4("div", { children: [
|
|
587
|
+
/* @__PURE__ */ jsx4("h3", { className: "font-bold text-red-500 text-lg", children: "Breaking Changes" }),
|
|
588
|
+
/* @__PURE__ */ jsx4("p", { className: "text-ods-text-secondary", children: "This release contains breaking changes. Review carefully before upgrading." })
|
|
589
|
+
] })
|
|
590
|
+
] }) }) }),
|
|
591
|
+
/* @__PURE__ */ jsx4(
|
|
592
|
+
ReleaseChangelogSection,
|
|
593
|
+
{
|
|
594
|
+
title: "Breaking Changes",
|
|
595
|
+
entries: breakingChanges || [],
|
|
596
|
+
isBreaking: true,
|
|
597
|
+
hideTitle: true,
|
|
598
|
+
icon: /* @__PURE__ */ jsx4(AlertTriangle, { className: "h-6 w-6" }),
|
|
599
|
+
SimpleMarkdownRenderer: MarkdownRenderer
|
|
600
|
+
}
|
|
601
|
+
),
|
|
602
|
+
/* @__PURE__ */ jsx4(
|
|
603
|
+
ReleaseChangelogSection,
|
|
604
|
+
{
|
|
605
|
+
title: "Features Added",
|
|
606
|
+
entries: featuresAdded || [],
|
|
607
|
+
icon: /* @__PURE__ */ jsx4(Sparkles, { className: "h-6 w-6" }),
|
|
608
|
+
previewFirst: true,
|
|
609
|
+
SimpleMarkdownRenderer: MarkdownRenderer
|
|
610
|
+
}
|
|
611
|
+
),
|
|
612
|
+
/* @__PURE__ */ jsx4(
|
|
613
|
+
ReleaseChangelogSection,
|
|
614
|
+
{
|
|
615
|
+
title: "Bugs Fixed",
|
|
616
|
+
entries: bugFixed || [],
|
|
617
|
+
icon: /* @__PURE__ */ jsx4(Wrench, { className: "h-6 w-6" }),
|
|
618
|
+
previewFirst: true,
|
|
619
|
+
SimpleMarkdownRenderer: MarkdownRenderer
|
|
620
|
+
}
|
|
621
|
+
),
|
|
622
|
+
/* @__PURE__ */ jsx4(
|
|
623
|
+
ReleaseChangelogSection,
|
|
624
|
+
{
|
|
625
|
+
title: "Improvements",
|
|
626
|
+
entries: improvements || [],
|
|
627
|
+
icon: /* @__PURE__ */ jsx4(TrendingUp, { className: "h-6 w-6" }),
|
|
628
|
+
previewFirst: true,
|
|
629
|
+
SimpleMarkdownRenderer: MarkdownRenderer
|
|
630
|
+
}
|
|
631
|
+
),
|
|
632
|
+
!VideoDisplaySection && VideoSection && videoBites && videoBites.length > 0 && /* @__PURE__ */ jsx4(
|
|
633
|
+
VideoSection,
|
|
634
|
+
{
|
|
635
|
+
bites: videoBites,
|
|
636
|
+
title: "Video Clips",
|
|
637
|
+
filterPublished: true
|
|
638
|
+
}
|
|
639
|
+
),
|
|
640
|
+
RoadmapSection && (roadmapLoading || roadmapTasks.length > 0) && /* @__PURE__ */ jsxs4("div", { className: "space-y-4 w-full", children: [
|
|
641
|
+
/* @__PURE__ */ jsx4("p", { className: "text-h5 tracking-[-0.28px] text-ods-text-secondary", children: "Related Roadmap Items" }),
|
|
642
|
+
/* @__PURE__ */ jsx4(
|
|
643
|
+
RoadmapSection,
|
|
644
|
+
{
|
|
645
|
+
items: roadmapTasks,
|
|
646
|
+
isLoading: roadmapLoading,
|
|
647
|
+
onItemUpdate: (updatedItem) => {
|
|
648
|
+
setRoadmapTasks(
|
|
649
|
+
(prevTasks) => prevTasks.map(
|
|
650
|
+
(task) => task.id === updatedItem.id ? updatedItem : task
|
|
651
|
+
)
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
)
|
|
656
|
+
] }),
|
|
657
|
+
DeliverySection && (deliveryLoading || deliveryData && (deliveryData.completed.length > 0 || deliveryData.inProgress.length > 0)) && /* @__PURE__ */ jsxs4("div", { className: "w-full space-y-4", children: [
|
|
658
|
+
/* @__PURE__ */ jsx4("p", { className: "text-h5 tracking-[-0.28px] text-ods-text-secondary", children: "Related Enhancements and Bug-fixes" }),
|
|
659
|
+
/* @__PURE__ */ jsx4(
|
|
660
|
+
DeliverySection,
|
|
661
|
+
{
|
|
662
|
+
data: deliveryData,
|
|
663
|
+
isLoading: deliveryLoading
|
|
664
|
+
}
|
|
665
|
+
)
|
|
666
|
+
] }),
|
|
667
|
+
(githubReleases?.length || knowledgeBaseLinks?.length || migrationGuideUrl || documentationUrl) && /* @__PURE__ */ jsxs4("div", { className: "space-y-1 w-full", children: [
|
|
668
|
+
/* @__PURE__ */ jsx4("p", { className: "text-h5 tracking-[-0.28px] text-ods-text-secondary", children: "Related Links" }),
|
|
669
|
+
/* @__PURE__ */ jsx4(Card, { className: "bg-ods-card border-ods-border p-6", children: /* @__PURE__ */ jsxs4("div", { className: "space-y-4", children: [
|
|
670
|
+
githubReleases && githubReleases.length > 0 && /* @__PURE__ */ jsx4(Fragment3, { children: githubReleases.map((ghRelease) => /* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-1", children: [
|
|
671
|
+
/* @__PURE__ */ jsx4(GitHubIcon, { className: "shrink-0", width: 24, height: 24, color: "var(--color-text-secondary)" }),
|
|
672
|
+
/* @__PURE__ */ jsx4("span", { className: "text-h4 text-ods-text-primary", children: "Github Release" }),
|
|
673
|
+
/* @__PURE__ */ jsx4(
|
|
674
|
+
"a",
|
|
675
|
+
{
|
|
676
|
+
href: ghRelease.github_release_url,
|
|
677
|
+
target: "_blank",
|
|
678
|
+
rel: "noopener noreferrer",
|
|
679
|
+
className: "text-h4 text-[#ffc008] hover:underline",
|
|
680
|
+
children: ghRelease.github_release_url.split("/").pop()
|
|
681
|
+
}
|
|
682
|
+
),
|
|
683
|
+
/* @__PURE__ */ jsx4(ExternalLink, { className: "h-6 w-6 text-[#ffc008] shrink-0" })
|
|
684
|
+
] }, ghRelease.id)) }),
|
|
685
|
+
knowledgeBaseLinks && knowledgeBaseLinks.length > 0 && /* @__PURE__ */ jsx4(Fragment3, { children: knowledgeBaseLinks.map((linkObj) => {
|
|
686
|
+
const path = typeof linkObj === "string" ? linkObj : linkObj.kb_article_path;
|
|
687
|
+
const linkId = typeof linkObj === "string" ? path : linkObj.id || path;
|
|
688
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-1", children: [
|
|
689
|
+
/* @__PURE__ */ jsx4(BookMarked, { className: "h-6 w-6 text-ods-text-secondary shrink-0" }),
|
|
690
|
+
/* @__PURE__ */ jsx4("span", { className: "text-h4 text-ods-text-primary", children: "Knowledge Base" }),
|
|
691
|
+
/* @__PURE__ */ jsx4(
|
|
692
|
+
next_link_default,
|
|
693
|
+
{
|
|
694
|
+
href: path.startsWith("http") ? path : `/knowledge-base${path.startsWith("/") ? "" : "/"}${path}`,
|
|
695
|
+
className: "text-h4 text-[#ffc008] hover:underline",
|
|
696
|
+
children: path.replace(/^\//, "").split("/").pop()?.replace(/-/g, " ") || "View Article"
|
|
697
|
+
}
|
|
698
|
+
),
|
|
699
|
+
/* @__PURE__ */ jsx4(ExternalLink, { className: "h-6 w-6 text-[#ffc008] shrink-0" })
|
|
700
|
+
] }, linkId);
|
|
701
|
+
}) }),
|
|
702
|
+
migrationGuideUrl && /* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-1", children: [
|
|
703
|
+
/* @__PURE__ */ jsx4(BookMarked, { className: "h-6 w-6 text-ods-text-secondary shrink-0" }),
|
|
704
|
+
/* @__PURE__ */ jsx4(
|
|
705
|
+
"a",
|
|
706
|
+
{
|
|
707
|
+
href: migrationGuideUrl,
|
|
708
|
+
target: "_blank",
|
|
709
|
+
rel: "noopener noreferrer",
|
|
710
|
+
className: "text-h4 text-[#ffc008] hover:underline",
|
|
711
|
+
children: "\u{1F4D6} Migration Guide"
|
|
712
|
+
}
|
|
713
|
+
),
|
|
714
|
+
/* @__PURE__ */ jsx4(ExternalLink, { className: "h-6 w-6 text-[#ffc008] shrink-0" })
|
|
715
|
+
] }),
|
|
716
|
+
documentationUrl && /* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-1", children: [
|
|
717
|
+
/* @__PURE__ */ jsx4(BookMarked, { className: "h-6 w-6 text-ods-text-secondary shrink-0" }),
|
|
718
|
+
/* @__PURE__ */ jsx4(
|
|
719
|
+
"a",
|
|
720
|
+
{
|
|
721
|
+
href: documentationUrl,
|
|
722
|
+
target: "_blank",
|
|
723
|
+
rel: "noopener noreferrer",
|
|
724
|
+
className: "text-h4 text-[#ffc008] hover:underline",
|
|
725
|
+
children: "\u{1F4DA} Documentation"
|
|
726
|
+
}
|
|
727
|
+
),
|
|
728
|
+
/* @__PURE__ */ jsx4(ExternalLink, { className: "h-6 w-6 text-[#ffc008] shrink-0" })
|
|
729
|
+
] })
|
|
730
|
+
] }) })
|
|
731
|
+
] })
|
|
732
|
+
] }),
|
|
733
|
+
relatedContent
|
|
734
|
+
]
|
|
735
|
+
}
|
|
736
|
+
)
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// src/components/shared/product-release/release-detail-skeleton.tsx
|
|
741
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
742
|
+
function ReleaseDetailSkeleton() {
|
|
743
|
+
return /* @__PURE__ */ jsx5(DetailPageSkeleton, { metadataColumns: 4, showImageGallery: true });
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// src/components/shared/roadmap/use-roadmap-voting.ts
|
|
747
|
+
import { useState as useState3, useEffect as useEffect2, useCallback } from "react";
|
|
748
|
+
var DEFAULT_VOTE_ENDPOINT = "/api/roadmap/vote";
|
|
749
|
+
var DEFAULT_STORAGE_KEY = "roadmap_votes_v1";
|
|
750
|
+
function useRoadmapVoting(options = {}) {
|
|
751
|
+
const voteApiEndpoint = options.voteApiEndpoint ?? DEFAULT_VOTE_ENDPOINT;
|
|
752
|
+
const storageKey = options.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
753
|
+
const [votes, setVotes] = useState3({});
|
|
754
|
+
const [isLoading, setIsLoading] = useState3(true);
|
|
755
|
+
useEffect2(() => {
|
|
756
|
+
setIsLoading(true);
|
|
757
|
+
setVotes({});
|
|
758
|
+
try {
|
|
759
|
+
const stored = localStorage.getItem(storageKey);
|
|
760
|
+
if (stored) {
|
|
761
|
+
setVotes(JSON.parse(stored));
|
|
762
|
+
}
|
|
763
|
+
} catch (error) {
|
|
764
|
+
console.error("[Voting] Error loading votes from localStorage:", error);
|
|
765
|
+
} finally {
|
|
766
|
+
setIsLoading(false);
|
|
767
|
+
}
|
|
768
|
+
}, [storageKey]);
|
|
769
|
+
useEffect2(() => {
|
|
770
|
+
if (!isLoading) {
|
|
771
|
+
try {
|
|
772
|
+
localStorage.setItem(storageKey, JSON.stringify(votes));
|
|
773
|
+
} catch (error) {
|
|
774
|
+
console.error("[Voting] Error saving votes to localStorage:", error);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}, [votes, isLoading, storageKey]);
|
|
778
|
+
const getVote = useCallback(
|
|
779
|
+
(taskId) => {
|
|
780
|
+
return votes[taskId] || null;
|
|
781
|
+
},
|
|
782
|
+
[votes]
|
|
783
|
+
);
|
|
784
|
+
const toggleVote = useCallback(
|
|
785
|
+
async (taskId, voteType) => {
|
|
786
|
+
const currentVote = votes[taskId];
|
|
787
|
+
let newVote = null;
|
|
788
|
+
let action = "add";
|
|
789
|
+
if (currentVote === voteType) {
|
|
790
|
+
newVote = null;
|
|
791
|
+
action = "remove";
|
|
792
|
+
} else {
|
|
793
|
+
if (currentVote) {
|
|
794
|
+
await contentFetch(voteApiEndpoint, {
|
|
795
|
+
method: "POST",
|
|
796
|
+
headers: { "Content-Type": "application/json" },
|
|
797
|
+
body: JSON.stringify({
|
|
798
|
+
taskId,
|
|
799
|
+
voteType: currentVote,
|
|
800
|
+
action: "remove"
|
|
801
|
+
})
|
|
802
|
+
}).catch((err) => console.error("[Voting] Error removing opposite vote:", err));
|
|
803
|
+
}
|
|
804
|
+
newVote = voteType;
|
|
805
|
+
action = "add";
|
|
806
|
+
}
|
|
807
|
+
setVotes((prev) => ({
|
|
808
|
+
...prev,
|
|
809
|
+
[taskId]: newVote
|
|
810
|
+
}));
|
|
811
|
+
try {
|
|
812
|
+
const response = await contentFetch(voteApiEndpoint, {
|
|
813
|
+
method: "POST",
|
|
814
|
+
headers: { "Content-Type": "application/json" },
|
|
815
|
+
body: JSON.stringify({ taskId, voteType, action })
|
|
816
|
+
});
|
|
817
|
+
if (!response.ok) {
|
|
818
|
+
throw new Error("Vote API request failed");
|
|
819
|
+
}
|
|
820
|
+
return { success: true, newVote, action };
|
|
821
|
+
} catch (error) {
|
|
822
|
+
console.error("[Voting] API error:", error);
|
|
823
|
+
setVotes((prev) => ({
|
|
824
|
+
...prev,
|
|
825
|
+
[taskId]: currentVote
|
|
826
|
+
}));
|
|
827
|
+
return { success: false, newVote: currentVote, action };
|
|
828
|
+
}
|
|
829
|
+
},
|
|
830
|
+
[votes, voteApiEndpoint]
|
|
831
|
+
);
|
|
832
|
+
const clearVotes = useCallback(() => {
|
|
833
|
+
setVotes({});
|
|
834
|
+
localStorage.removeItem(storageKey);
|
|
835
|
+
}, [storageKey]);
|
|
836
|
+
return {
|
|
837
|
+
votes,
|
|
838
|
+
isLoading,
|
|
839
|
+
getVote,
|
|
840
|
+
toggleVote,
|
|
841
|
+
clearVotes
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// src/components/shared/roadmap/roadmap-grid.tsx
|
|
846
|
+
import { useEffect as useEffect3, useRef, useState as useState4 } from "react";
|
|
847
|
+
init_cn();
|
|
848
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
849
|
+
var DEFAULT_BUILD_REFRESH_URL = (taskId) => `/api/roadmap/${taskId}`;
|
|
850
|
+
var BACKLOG = "Backlog";
|
|
851
|
+
function getStatusPriority(status) {
|
|
852
|
+
const s = (status || "").toLowerCase();
|
|
853
|
+
if (s.includes("complete") || s.includes("done")) return 1;
|
|
854
|
+
if (s.includes("working") || s.includes("progress")) return 2;
|
|
855
|
+
if (s.includes("review")) return 3;
|
|
856
|
+
if (s.includes("to do") || s.includes("plan")) return 4;
|
|
857
|
+
return 5;
|
|
858
|
+
}
|
|
859
|
+
function parseQuarterString(q) {
|
|
860
|
+
const match = q.match(/Q(\d+)\s+(\d+)/);
|
|
861
|
+
if (!match) return null;
|
|
862
|
+
return { quarter: parseInt(match[1], 10), year: parseInt(match[2], 10) };
|
|
863
|
+
}
|
|
864
|
+
function compareQuarters(a, b) {
|
|
865
|
+
if (a.year !== b.year) return a.year - b.year;
|
|
866
|
+
return a.quarter - b.quarter;
|
|
867
|
+
}
|
|
868
|
+
function getCurrentQuarter() {
|
|
869
|
+
const now = /* @__PURE__ */ new Date();
|
|
870
|
+
return { quarter: Math.floor(now.getMonth() / 3) + 1, year: now.getFullYear() };
|
|
871
|
+
}
|
|
872
|
+
function computeDefaultExpandedQuarters(quarters, quartersToKeepClosed) {
|
|
873
|
+
const currentQ = getCurrentQuarter();
|
|
874
|
+
const out = [];
|
|
875
|
+
for (const q of quarters) {
|
|
876
|
+
if (q === BACKLOG) continue;
|
|
877
|
+
const parsed = parseQuarterString(q);
|
|
878
|
+
if (!parsed) continue;
|
|
879
|
+
const diff = compareQuarters(parsed, currentQ);
|
|
880
|
+
if (diff >= 0) {
|
|
881
|
+
out.push(q);
|
|
882
|
+
} else {
|
|
883
|
+
const quartersAgo = currentQ.year * 4 + currentQ.quarter - (parsed.year * 4 + parsed.quarter);
|
|
884
|
+
if (quartersAgo < quartersToKeepClosed) out.push(q);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
if (quarters.includes(BACKLOG)) out.push(BACKLOG);
|
|
888
|
+
return out;
|
|
889
|
+
}
|
|
890
|
+
function RoadmapGridSingle({
|
|
891
|
+
items,
|
|
892
|
+
showLeftMargin,
|
|
893
|
+
getVote,
|
|
894
|
+
onVote,
|
|
895
|
+
votingTasks
|
|
896
|
+
}) {
|
|
897
|
+
return /* @__PURE__ */ jsx6("div", { className: `grid grid-cols-1 md:grid-cols-2 gap-6 ${showLeftMargin ? "md:ml-[120px]" : ""}`, children: items.map((item) => (
|
|
898
|
+
// DOM id + sticky-header scroll offset live ON RoadmapCard's own
|
|
899
|
+
// outer element (no wrapper div). Anchor mirrors
|
|
900
|
+
// `buildDevSectionUrl('roadmap', <id>)` → `#roadmap-<external_id>`;
|
|
901
|
+
// `useScrollToHash` in `roadmap-view.tsx` finds the card by id
|
|
902
|
+
// and scrolls.
|
|
903
|
+
/* @__PURE__ */ jsx6(
|
|
904
|
+
RoadmapCard,
|
|
905
|
+
{
|
|
906
|
+
item,
|
|
907
|
+
id: devSectionAnchorId("roadmap", item.id),
|
|
908
|
+
userVote: getVote(item.id),
|
|
909
|
+
onVote: (voteType) => onVote(item.id, voteType),
|
|
910
|
+
isVoting: votingTasks.has(item.id)
|
|
911
|
+
},
|
|
912
|
+
item.id
|
|
913
|
+
)
|
|
914
|
+
)) });
|
|
915
|
+
}
|
|
916
|
+
function RoadmapGrid({
|
|
917
|
+
items,
|
|
918
|
+
onItemUpdate,
|
|
919
|
+
showLeftMargin = true,
|
|
920
|
+
buildRefreshUrl = DEFAULT_BUILD_REFRESH_URL,
|
|
921
|
+
votingOptions,
|
|
922
|
+
groupByQuarter = false,
|
|
923
|
+
hasActiveFilters = false,
|
|
924
|
+
quartersToKeepClosed = 2
|
|
925
|
+
}) {
|
|
926
|
+
const { getVote, toggleVote } = useRoadmapVoting(votingOptions);
|
|
927
|
+
const [votingTasks, setVotingTasks] = useState4(/* @__PURE__ */ new Set());
|
|
928
|
+
const handleVote = async (taskId, voteType) => {
|
|
929
|
+
if (votingTasks.has(taskId)) return;
|
|
930
|
+
setVotingTasks((prev) => new Set(prev).add(taskId));
|
|
931
|
+
try {
|
|
932
|
+
const result = await toggleVote(taskId, voteType);
|
|
933
|
+
if (result.success) {
|
|
934
|
+
const response = await contentFetch(buildRefreshUrl(taskId));
|
|
935
|
+
if (response.ok) {
|
|
936
|
+
const data = await response.json();
|
|
937
|
+
if (data.item && onItemUpdate) onItemUpdate(data.item);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
} finally {
|
|
941
|
+
setVotingTasks((prev) => {
|
|
942
|
+
const next = new Set(prev);
|
|
943
|
+
next.delete(taskId);
|
|
944
|
+
return next;
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
const itemsByQuarter = items.reduce((acc, item) => {
|
|
949
|
+
const q = item.quarter || BACKLOG;
|
|
950
|
+
(acc[q] || (acc[q] = [])).push(item);
|
|
951
|
+
return acc;
|
|
952
|
+
}, {});
|
|
953
|
+
for (const q of Object.keys(itemsByQuarter)) {
|
|
954
|
+
itemsByQuarter[q].sort((a, b) => getStatusPriority(a.status) - getStatusPriority(b.status));
|
|
955
|
+
}
|
|
956
|
+
const sortedQuarters = Object.keys(itemsByQuarter).sort((a, b) => {
|
|
957
|
+
if (a === BACKLOG) return 1;
|
|
958
|
+
if (b === BACKLOG) return -1;
|
|
959
|
+
const aD = parseQuarterString(a) ?? { quarter: 0, year: 0 };
|
|
960
|
+
const bD = parseQuarterString(b) ?? { quarter: 0, year: 0 };
|
|
961
|
+
return compareQuarters(aD, bD);
|
|
962
|
+
});
|
|
963
|
+
const sortedQuartersKey = sortedQuarters.join(",");
|
|
964
|
+
const [expandedQuarters, setExpandedQuarters] = useState4([]);
|
|
965
|
+
const [isInitialized, setIsInitialized] = useState4(false);
|
|
966
|
+
const hasSetInitialState = useRef(false);
|
|
967
|
+
const prevItemsLength = useRef(0);
|
|
968
|
+
useEffect3(() => {
|
|
969
|
+
const itemsJustLoaded = prevItemsLength.current === 0 && items.length > 0;
|
|
970
|
+
prevItemsLength.current = items.length;
|
|
971
|
+
if (sortedQuarters.length > 0 && (!hasSetInitialState.current || itemsJustLoaded)) {
|
|
972
|
+
hasSetInitialState.current = true;
|
|
973
|
+
setExpandedQuarters(
|
|
974
|
+
hasActiveFilters ? [...sortedQuarters] : computeDefaultExpandedQuarters(sortedQuarters, quartersToKeepClosed)
|
|
975
|
+
);
|
|
976
|
+
setIsInitialized(true);
|
|
977
|
+
}
|
|
978
|
+
}, [sortedQuarters.length, items.length]);
|
|
979
|
+
useEffect3(() => {
|
|
980
|
+
if (!isInitialized || sortedQuarters.length === 0) return;
|
|
981
|
+
setExpandedQuarters(
|
|
982
|
+
hasActiveFilters ? [...sortedQuarters] : computeDefaultExpandedQuarters(sortedQuarters, quartersToKeepClosed)
|
|
983
|
+
);
|
|
984
|
+
}, [hasActiveFilters, sortedQuartersKey, isInitialized, quartersToKeepClosed]);
|
|
985
|
+
if (items.length === 0) {
|
|
986
|
+
return /* @__PURE__ */ jsx6(
|
|
987
|
+
EmptyState,
|
|
988
|
+
{
|
|
989
|
+
type: "generic",
|
|
990
|
+
title: "No roadmap items",
|
|
991
|
+
description: "Check back soon for upcoming features and improvements!"
|
|
992
|
+
}
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
if (!groupByQuarter) {
|
|
996
|
+
return /* @__PURE__ */ jsx6(
|
|
997
|
+
RoadmapGridSingle,
|
|
998
|
+
{
|
|
999
|
+
items,
|
|
1000
|
+
showLeftMargin,
|
|
1001
|
+
getVote,
|
|
1002
|
+
onVote: handleVote,
|
|
1003
|
+
votingTasks
|
|
1004
|
+
}
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
return /* @__PURE__ */ jsx6(
|
|
1008
|
+
Accordion,
|
|
1009
|
+
{
|
|
1010
|
+
type: "multiple",
|
|
1011
|
+
value: expandedQuarters,
|
|
1012
|
+
onValueChange: setExpandedQuarters,
|
|
1013
|
+
className: "flex flex-col gap-10",
|
|
1014
|
+
children: sortedQuarters.map((quarter) => {
|
|
1015
|
+
const itemCount = itemsByQuarter[quarter]?.length || 0;
|
|
1016
|
+
const isExpanded = expandedQuarters.includes(quarter);
|
|
1017
|
+
return /* @__PURE__ */ jsxs5(
|
|
1018
|
+
AccordionItem,
|
|
1019
|
+
{
|
|
1020
|
+
value: quarter,
|
|
1021
|
+
id: `quarter-${quarter.replace(/\s+/g, "-").toLowerCase()}`,
|
|
1022
|
+
className: "border-0",
|
|
1023
|
+
children: [
|
|
1024
|
+
/* @__PURE__ */ jsx6(AccordionTrigger, { className: "w-full p-0 hover:no-underline [&>svg]:h-5 [&>svg]:w-5 [&>svg]:text-ods-text-secondary [&>svg]:ml-auto [&>svg]:shrink-0", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3", children: [
|
|
1025
|
+
/* @__PURE__ */ jsxs5(
|
|
1026
|
+
"h3",
|
|
1027
|
+
{
|
|
1028
|
+
className: cn(
|
|
1029
|
+
"font-['Azeret_Mono'] font-semibold text-[24px] md:text-[28px] lg:text-[32px] leading-[32px] md:leading-[36px] lg:leading-[40px] text-ods-text-primary tracking-[-0.48px] md:tracking-[-0.56px] lg:tracking-[-0.64px] transition-opacity",
|
|
1030
|
+
isExpanded ? "opacity-100" : "opacity-60"
|
|
1031
|
+
),
|
|
1032
|
+
children: [
|
|
1033
|
+
quarter,
|
|
1034
|
+
/* @__PURE__ */ jsx6("span", { className: "text-ods-accent", children: ":" })
|
|
1035
|
+
]
|
|
1036
|
+
}
|
|
1037
|
+
),
|
|
1038
|
+
/* @__PURE__ */ jsxs5(
|
|
1039
|
+
"span",
|
|
1040
|
+
{
|
|
1041
|
+
className: cn(
|
|
1042
|
+
"text-sm font-medium transition-opacity",
|
|
1043
|
+
isExpanded ? "text-ods-text-secondary opacity-100" : "text-ods-text-tertiary opacity-60"
|
|
1044
|
+
),
|
|
1045
|
+
children: [
|
|
1046
|
+
itemCount,
|
|
1047
|
+
" ",
|
|
1048
|
+
itemCount === 1 ? "item" : "items",
|
|
1049
|
+
isInitialized && !isExpanded && /* @__PURE__ */ jsx6("span", { className: "ml-2 text-ods-accent", children: "Click to expand" })
|
|
1050
|
+
]
|
|
1051
|
+
}
|
|
1052
|
+
)
|
|
1053
|
+
] }) }),
|
|
1054
|
+
/* @__PURE__ */ jsx6(AccordionContent, { className: "pt-4 pb-0 overflow-hidden data-[state=closed]:animate-none data-[state=open]:animate-none", children: /* @__PURE__ */ jsx6(
|
|
1055
|
+
RoadmapGridSingle,
|
|
1056
|
+
{
|
|
1057
|
+
items: itemsByQuarter[quarter],
|
|
1058
|
+
showLeftMargin,
|
|
1059
|
+
getVote,
|
|
1060
|
+
onVote: handleVote,
|
|
1061
|
+
votingTasks
|
|
1062
|
+
}
|
|
1063
|
+
) })
|
|
1064
|
+
]
|
|
1065
|
+
},
|
|
1066
|
+
quarter
|
|
1067
|
+
);
|
|
1068
|
+
})
|
|
1069
|
+
}
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// src/components/shared/roadmap/roadmap-grid-skeleton.tsx
|
|
1074
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1075
|
+
function RoadmapCardSkeleton() {
|
|
1076
|
+
return /* @__PURE__ */ jsxs6("div", { className: "bg-ods-card border border-ods-border rounded-[6px] p-[24px] flex flex-col gap-[16px] min-h-[340px] relative", children: [
|
|
1077
|
+
/* @__PURE__ */ jsx7("div", { className: "absolute top-[24px] right-[24px]", children: /* @__PURE__ */ jsx7("div", { className: "h-[20px] w-[80px] bg-ods-border rounded animate-pulse" }) }),
|
|
1078
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-[16px] pr-[120px]", children: [
|
|
1079
|
+
/* @__PURE__ */ jsx7("div", { className: "w-[80px] h-[80px] bg-ods-border rounded-lg flex-shrink-0 animate-pulse" }),
|
|
1080
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex-1 min-w-0 flex flex-col gap-1", children: [
|
|
1081
|
+
/* @__PURE__ */ jsx7("div", { className: "min-h-[48px] flex items-center", children: /* @__PURE__ */ jsx7("div", { className: "h-[24px] w-full bg-ods-border rounded animate-pulse" }) }),
|
|
1082
|
+
/* @__PURE__ */ jsx7("div", { className: "min-h-[20px] flex items-center", children: /* @__PURE__ */ jsx7("div", { className: "h-[14px] w-1/2 bg-ods-border rounded animate-pulse" }) })
|
|
1083
|
+
] })
|
|
1084
|
+
] }),
|
|
1085
|
+
/* @__PURE__ */ jsx7("div", { className: "min-h-[72px] flex items-center", children: /* @__PURE__ */ jsxs6("div", { className: "w-full space-y-2", children: [
|
|
1086
|
+
/* @__PURE__ */ jsx7("div", { className: "h-[24px] bg-ods-border rounded animate-pulse" }),
|
|
1087
|
+
/* @__PURE__ */ jsx7("div", { className: "h-[24px] bg-ods-border rounded animate-pulse" }),
|
|
1088
|
+
/* @__PURE__ */ jsx7("div", { className: "h-[24px] w-4/5 bg-ods-border rounded animate-pulse" })
|
|
1089
|
+
] }) }),
|
|
1090
|
+
/* @__PURE__ */ jsx7("div", { className: "flex-1" }),
|
|
1091
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
|
|
1092
|
+
/* @__PURE__ */ jsx7("div", { className: "h-[48px] w-[120px] bg-ods-border rounded animate-pulse" }),
|
|
1093
|
+
/* @__PURE__ */ jsx7("div", { className: "h-[32px] w-[100px] bg-ods-border rounded animate-pulse" })
|
|
1094
|
+
] })
|
|
1095
|
+
] });
|
|
1096
|
+
}
|
|
1097
|
+
function RoadmapGridSkeleton({ count = 4, showLeftMargin = true }) {
|
|
1098
|
+
return /* @__PURE__ */ jsx7("div", { className: `grid grid-cols-1 md:grid-cols-2 gap-6 ${showLeftMargin ? "md:ml-[120px]" : ""}`, children: Array.from({ length: count }).map((_, i) => /* @__PURE__ */ jsx7(RoadmapCardSkeleton, {}, i)) });
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// src/components/shared/roadmap/roadmap-view.tsx
|
|
1102
|
+
import { useMemo } from "react";
|
|
1103
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1104
|
+
var DEFAULT_ENDPOINT2 = "/api/roadmap";
|
|
1105
|
+
var DEFAULT_SEARCH_PARAM_KEY2 = DEV_SECTION_PARAM_KEYS.search;
|
|
1106
|
+
var DEFAULT_STATUS_PARAM_KEY2 = DEV_SECTION_PARAM_KEYS.status;
|
|
1107
|
+
function RoadmapView({
|
|
1108
|
+
endpoint = DEFAULT_ENDPOINT2,
|
|
1109
|
+
initialItems,
|
|
1110
|
+
showLeftMargin,
|
|
1111
|
+
buildRefreshUrl,
|
|
1112
|
+
votingOptions,
|
|
1113
|
+
searchParamKey = DEFAULT_SEARCH_PARAM_KEY2,
|
|
1114
|
+
statusParamKey = DEFAULT_STATUS_PARAM_KEY2
|
|
1115
|
+
} = {}) {
|
|
1116
|
+
const searchParams = useSearchParams();
|
|
1117
|
+
const search = searchParams.get(searchParamKey) || "";
|
|
1118
|
+
const status = searchParams.get(statusParamKey) || "all";
|
|
1119
|
+
const listParams = new URLSearchParams();
|
|
1120
|
+
if (search) listParams.set(searchParamKey, search);
|
|
1121
|
+
if (status && status !== "all") listParams.set(statusParamKey, status);
|
|
1122
|
+
const qs = listParams.toString();
|
|
1123
|
+
const url = qs ? `${endpoint}?${qs}` : endpoint;
|
|
1124
|
+
const initialData = useMemo(() => initialItems ? { items: initialItems } : void 0, [initialItems]);
|
|
1125
|
+
const { data, setData, isLoading, error, reload } = useSelfFetch(
|
|
1126
|
+
url,
|
|
1127
|
+
{ initialData }
|
|
1128
|
+
);
|
|
1129
|
+
const items = data?.items ?? [];
|
|
1130
|
+
useScrollToHash(data, { headerOffset: STICKY_HEADER_OFFSET_PX });
|
|
1131
|
+
if (error) {
|
|
1132
|
+
return /* @__PURE__ */ jsx8(LoadError, { message: "Failed to load roadmap.", onRetry: reload });
|
|
1133
|
+
}
|
|
1134
|
+
if (isLoading && !data) {
|
|
1135
|
+
return /* @__PURE__ */ jsx8(RoadmapGridSkeleton, { showLeftMargin });
|
|
1136
|
+
}
|
|
1137
|
+
return /* @__PURE__ */ jsx8(
|
|
1138
|
+
RoadmapGrid,
|
|
1139
|
+
{
|
|
1140
|
+
items,
|
|
1141
|
+
showLeftMargin,
|
|
1142
|
+
buildRefreshUrl,
|
|
1143
|
+
votingOptions,
|
|
1144
|
+
groupByQuarter: true,
|
|
1145
|
+
hasActiveFilters: search !== "" || status !== "all",
|
|
1146
|
+
onItemUpdate: (updated) => setData(
|
|
1147
|
+
(prev) => prev ? { ...prev, items: (prev.items ?? []).map((it) => it.id === updated.id ? updated : it) } : prev
|
|
1148
|
+
)
|
|
1149
|
+
}
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// src/components/shared/delivery/delivery-table.tsx
|
|
1154
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1155
|
+
function SkeletonRow() {
|
|
1156
|
+
return /* @__PURE__ */ jsx9("div", { className: "border-b border-ods-border last:border-b-0 p-[12px] md:p-[16px]", children: /* @__PURE__ */ jsxs7("div", { className: "flex flex-col md:flex-row items-start justify-between gap-[12px] md:gap-[16px] w-full", children: [
|
|
1157
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex-1 min-w-0 w-full md:w-auto flex flex-col gap-[12px] md:gap-[16px]", children: [
|
|
1158
|
+
/* @__PURE__ */ jsx9("div", { className: "min-h-[24px] flex items-center", children: /* @__PURE__ */ jsx9("div", { className: "h-[20px] bg-ods-border rounded animate-pulse w-full" }) }),
|
|
1159
|
+
/* @__PURE__ */ jsx9("div", { className: "min-h-[20px] flex items-center", children: /* @__PURE__ */ jsx9("div", { className: "h-[20px] bg-ods-border rounded animate-pulse w-1/2" }) }),
|
|
1160
|
+
/* @__PURE__ */ jsx9("div", { className: "min-h-[72px] flex items-center", children: /* @__PURE__ */ jsxs7("div", { className: "flex-1 space-y-1", children: [
|
|
1161
|
+
/* @__PURE__ */ jsx9("div", { className: "h-[20px] bg-ods-border rounded animate-pulse w-full" }),
|
|
1162
|
+
/* @__PURE__ */ jsx9("div", { className: "h-[20px] bg-ods-border rounded animate-pulse w-full" }),
|
|
1163
|
+
/* @__PURE__ */ jsx9("div", { className: "h-[20px] bg-ods-border rounded animate-pulse w-2/3" })
|
|
1164
|
+
] }) })
|
|
1165
|
+
] }),
|
|
1166
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex-shrink-0 self-start flex flex-col gap-2", children: [
|
|
1167
|
+
/* @__PURE__ */ jsx9("div", { className: "h-[32px] w-[100px] bg-ods-border rounded animate-pulse" }),
|
|
1168
|
+
/* @__PURE__ */ jsx9("div", { className: "h-[32px] w-[120px] bg-ods-border rounded animate-pulse" })
|
|
1169
|
+
] })
|
|
1170
|
+
] }) });
|
|
1171
|
+
}
|
|
1172
|
+
function DeliveryTable({ items, isLoading = false }) {
|
|
1173
|
+
if (isLoading) {
|
|
1174
|
+
return /* @__PURE__ */ jsx9("div", { className: "bg-ods-card border border-ods-border rounded-[6px] overflow-hidden w-full", children: /* @__PURE__ */ jsx9("div", { className: "w-full", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsx9(SkeletonRow, {}, i)) }) });
|
|
1175
|
+
}
|
|
1176
|
+
if (items.length === 0) {
|
|
1177
|
+
return /* @__PURE__ */ jsx9("div", { className: "bg-ods-card border border-ods-border rounded-[6px] p-[40px] text-center w-full", children: /* @__PURE__ */ jsx9("p", { className: "text-ods-text-secondary text-[14px] font-['DM_Sans'] font-medium", children: "No tasks available" }) });
|
|
1178
|
+
}
|
|
1179
|
+
return /* @__PURE__ */ jsx9("div", { className: "bg-ods-card border border-ods-border rounded-[6px] overflow-hidden w-full", children: /* @__PURE__ */ jsx9("div", { className: "w-full", children: items.map((item) => (
|
|
1180
|
+
// DOM id lives on DeliveryRow's own outer element (no wrapper
|
|
1181
|
+
// div). Anchor mirrors `buildDevSectionUrl('delivery', <id>)`
|
|
1182
|
+
// → `#delivery-<external_id>`; `useScrollToHash` in
|
|
1183
|
+
// `delivery-lists.tsx` finds the row by id and scrolls. The
|
|
1184
|
+
// outer wrapper here ONLY exists for the row separators.
|
|
1185
|
+
/* @__PURE__ */ jsx9(
|
|
1186
|
+
"div",
|
|
1187
|
+
{
|
|
1188
|
+
className: "border-b border-ods-border last:border-b-0",
|
|
1189
|
+
children: /* @__PURE__ */ jsx9(DeliveryRow, { item, id: devSectionAnchorId("delivery", item.id) })
|
|
1190
|
+
},
|
|
1191
|
+
item.id
|
|
1192
|
+
)
|
|
1193
|
+
)) }) });
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// src/components/shared/delivery/delivery-lists.tsx
|
|
1197
|
+
import { useEffect as useEffect4, useState as useState5 } from "react";
|
|
1198
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1199
|
+
var DEFAULT_COMPLETED_ENDPOINT = "/api/delivery/completed";
|
|
1200
|
+
var DEFAULT_IN_PROGRESS_ENDPOINT = "/api/delivery/in-progress";
|
|
1201
|
+
var DEFAULT_SEARCH_PARAM_KEY3 = DEV_SECTION_PARAM_KEYS.search;
|
|
1202
|
+
var DEFAULT_TASK_TYPE_PARAM_KEY = DEV_SECTION_PARAM_KEYS.deliveryTaskType;
|
|
1203
|
+
function DeliveryLists({
|
|
1204
|
+
completedApiEndpoint = DEFAULT_COMPLETED_ENDPOINT,
|
|
1205
|
+
inProgressApiEndpoint = DEFAULT_IN_PROGRESS_ENDPOINT,
|
|
1206
|
+
searchParamKey = DEFAULT_SEARCH_PARAM_KEY3,
|
|
1207
|
+
taskTypeParamKey = DEFAULT_TASK_TYPE_PARAM_KEY
|
|
1208
|
+
} = {}) {
|
|
1209
|
+
const searchParams = useSearchParams();
|
|
1210
|
+
const router = useRouter();
|
|
1211
|
+
const pathname = usePathname();
|
|
1212
|
+
const [data, setData] = useState5(null);
|
|
1213
|
+
const [isLoading, setIsLoading] = useState5(true);
|
|
1214
|
+
const [error, setError] = useState5(null);
|
|
1215
|
+
const searchQuery = searchParams.get(searchParamKey) || "";
|
|
1216
|
+
const taskTypeFilter = searchParams.get(taskTypeParamKey) || "all";
|
|
1217
|
+
useEffect4(() => {
|
|
1218
|
+
async function fetchDeliveryData() {
|
|
1219
|
+
try {
|
|
1220
|
+
setIsLoading(true);
|
|
1221
|
+
setError(null);
|
|
1222
|
+
const params = new URLSearchParams();
|
|
1223
|
+
if (searchQuery) {
|
|
1224
|
+
params.set(searchParamKey, searchQuery);
|
|
1225
|
+
}
|
|
1226
|
+
if (taskTypeFilter && taskTypeFilter !== "all") {
|
|
1227
|
+
params.set(taskTypeParamKey, taskTypeFilter);
|
|
1228
|
+
}
|
|
1229
|
+
const queryString = params.toString();
|
|
1230
|
+
const queryParam = queryString ? `?${queryString}` : "";
|
|
1231
|
+
const [completedResponse, inProgressResponse] = await Promise.all([
|
|
1232
|
+
contentFetch(`${completedApiEndpoint}${queryParam}`),
|
|
1233
|
+
contentFetch(`${inProgressApiEndpoint}${queryParam}`)
|
|
1234
|
+
]);
|
|
1235
|
+
if (!completedResponse.ok || !inProgressResponse.ok) {
|
|
1236
|
+
throw new Error("Failed to fetch delivery items");
|
|
1237
|
+
}
|
|
1238
|
+
const [completedResult, inProgressResult] = await Promise.all([
|
|
1239
|
+
completedResponse.json(),
|
|
1240
|
+
inProgressResponse.json()
|
|
1241
|
+
]);
|
|
1242
|
+
setData({
|
|
1243
|
+
completed: completedResult.items || [],
|
|
1244
|
+
inProgress: inProgressResult.items || []
|
|
1245
|
+
});
|
|
1246
|
+
} catch (err) {
|
|
1247
|
+
console.error("Error fetching delivery items:", err);
|
|
1248
|
+
setError("Failed to load delivery items. Please try again later.");
|
|
1249
|
+
} finally {
|
|
1250
|
+
setIsLoading(false);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
fetchDeliveryData();
|
|
1254
|
+
}, [searchQuery, taskTypeFilter, completedApiEndpoint, inProgressApiEndpoint, searchParamKey, taskTypeParamKey]);
|
|
1255
|
+
const filteredCompleted = data?.completed || [];
|
|
1256
|
+
const filteredInProgress = data?.inProgress || [];
|
|
1257
|
+
useScrollToHash(data, { headerOffset: STICKY_HEADER_OFFSET_PX });
|
|
1258
|
+
const showCompleted = true;
|
|
1259
|
+
const showInProgress = true;
|
|
1260
|
+
const hasActiveFilters = searchQuery !== "" || taskTypeFilter !== "all";
|
|
1261
|
+
const hasResults = showCompleted && filteredCompleted.length > 0 || showInProgress && filteredInProgress.length > 0;
|
|
1262
|
+
if (error) {
|
|
1263
|
+
return /* @__PURE__ */ jsx10("div", { className: "w-full", children: /* @__PURE__ */ jsx10(LoadError, { message: error, onRetry: () => window.location.reload() }) });
|
|
1264
|
+
}
|
|
1265
|
+
return /* @__PURE__ */ jsxs8("div", { className: "w-full flex flex-col gap-[40px]", children: [
|
|
1266
|
+
!isLoading && !hasResults && (hasActiveFilters ? /* @__PURE__ */ jsx10(
|
|
1267
|
+
EmptyState,
|
|
1268
|
+
{
|
|
1269
|
+
type: "search",
|
|
1270
|
+
title: "No tasks found",
|
|
1271
|
+
description: "No tasks match your current filters. Try adjusting your search or status filter.",
|
|
1272
|
+
showCTA: true,
|
|
1273
|
+
ctaText: "Reset Filters",
|
|
1274
|
+
onCtaClick: () => {
|
|
1275
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
1276
|
+
params.delete(searchParamKey);
|
|
1277
|
+
params.delete(taskTypeParamKey);
|
|
1278
|
+
router.replace(`${pathname}?${params.toString()}`, { scroll: false });
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
) : /* @__PURE__ */ jsx10(
|
|
1282
|
+
EmptyState,
|
|
1283
|
+
{
|
|
1284
|
+
type: "generic",
|
|
1285
|
+
title: "No tasks available",
|
|
1286
|
+
description: "Check back soon for upcoming tasks!",
|
|
1287
|
+
showCTA: false
|
|
1288
|
+
}
|
|
1289
|
+
)),
|
|
1290
|
+
showCompleted && (hasResults || isLoading) && /* @__PURE__ */ jsxs8("div", { className: "w-full", children: [
|
|
1291
|
+
/* @__PURE__ */ jsxs8("h3", { className: "text-h2 text-ods-text-primary tracking-[-0.48px] md:tracking-[-0.56px] lg:tracking-[-0.64px] mb-4", children: [
|
|
1292
|
+
"Recently Completed",
|
|
1293
|
+
/* @__PURE__ */ jsx10("span", { className: "text-ods-accent", children: ":" })
|
|
1294
|
+
] }),
|
|
1295
|
+
/* @__PURE__ */ jsx10(
|
|
1296
|
+
DeliveryTable,
|
|
1297
|
+
{
|
|
1298
|
+
items: filteredCompleted,
|
|
1299
|
+
isLoading
|
|
1300
|
+
}
|
|
1301
|
+
)
|
|
1302
|
+
] }),
|
|
1303
|
+
showInProgress && (hasResults || isLoading) && /* @__PURE__ */ jsxs8("div", { className: "w-full", children: [
|
|
1304
|
+
/* @__PURE__ */ jsxs8("h3", { className: "text-h2 text-ods-text-primary tracking-[-0.48px] md:tracking-[-0.56px] lg:tracking-[-0.64px] mb-4", children: [
|
|
1305
|
+
"Active Tasks",
|
|
1306
|
+
/* @__PURE__ */ jsx10("span", { className: "text-ods-accent", children: ":" })
|
|
1307
|
+
] }),
|
|
1308
|
+
/* @__PURE__ */ jsx10(
|
|
1309
|
+
DeliveryTable,
|
|
1310
|
+
{
|
|
1311
|
+
items: filteredInProgress,
|
|
1312
|
+
isLoading
|
|
1313
|
+
}
|
|
1314
|
+
)
|
|
1315
|
+
] })
|
|
1316
|
+
] });
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
// src/components/shared/legal-document/use-legal-docs.ts
|
|
1320
|
+
import { useState as useState6, useEffect as useEffect5, useCallback as useCallback2 } from "react";
|
|
1321
|
+
function useLegalDocs(docType, options = {}) {
|
|
1322
|
+
const { initialData = null, apiEndpoint } = options;
|
|
1323
|
+
const effectiveEndpoint = apiEndpoint ?? `/api/legal/${docType}`;
|
|
1324
|
+
const [data, setData] = useState6(initialData ?? null);
|
|
1325
|
+
const [isLoading, setIsLoading] = useState6(!initialData);
|
|
1326
|
+
const [error, setError] = useState6(null);
|
|
1327
|
+
const fetchDocument = useCallback2(async () => {
|
|
1328
|
+
try {
|
|
1329
|
+
setIsLoading(true);
|
|
1330
|
+
setError(null);
|
|
1331
|
+
const response = await contentFetch(effectiveEndpoint);
|
|
1332
|
+
if (!response.ok) {
|
|
1333
|
+
throw new Error(
|
|
1334
|
+
`Failed to fetch ${docType} document: ${response.status} ${response.statusText}`
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
const result = await response.json();
|
|
1338
|
+
if (!result.content) {
|
|
1339
|
+
throw new Error(`${docType} document content is empty`);
|
|
1340
|
+
}
|
|
1341
|
+
setData(result);
|
|
1342
|
+
} catch (err) {
|
|
1343
|
+
const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
|
|
1344
|
+
console.error("Error fetching legal document:", docType, err);
|
|
1345
|
+
setError(errorMessage);
|
|
1346
|
+
} finally {
|
|
1347
|
+
setIsLoading(false);
|
|
1348
|
+
}
|
|
1349
|
+
}, [docType, effectiveEndpoint]);
|
|
1350
|
+
useEffect5(() => {
|
|
1351
|
+
setData(initialData ?? null);
|
|
1352
|
+
setError(null);
|
|
1353
|
+
setIsLoading(!initialData);
|
|
1354
|
+
}, [docType, initialData]);
|
|
1355
|
+
useEffect5(() => {
|
|
1356
|
+
if (initialData) return;
|
|
1357
|
+
fetchDocument();
|
|
1358
|
+
}, [fetchDocument, initialData]);
|
|
1359
|
+
const refetch = () => {
|
|
1360
|
+
fetchDocument();
|
|
1361
|
+
};
|
|
1362
|
+
return {
|
|
1363
|
+
data,
|
|
1364
|
+
isLoading,
|
|
1365
|
+
error,
|
|
1366
|
+
refetch
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
// src/components/shared/legal-document/legal-document-page.tsx
|
|
1371
|
+
init_next_navigation();
|
|
1372
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1373
|
+
function LegalDocumentPage({
|
|
1374
|
+
docType,
|
|
1375
|
+
title,
|
|
1376
|
+
fallbackDescription,
|
|
1377
|
+
contactEmail,
|
|
1378
|
+
errorContactPrompt,
|
|
1379
|
+
errorTitle,
|
|
1380
|
+
emptyStateMessage,
|
|
1381
|
+
initialData = null,
|
|
1382
|
+
initialLastUpdatedLabel = null,
|
|
1383
|
+
apiEndpoint,
|
|
1384
|
+
MarkdownRenderer = RichMarkdownRenderer,
|
|
1385
|
+
backButton,
|
|
1386
|
+
shell = true
|
|
1387
|
+
}) {
|
|
1388
|
+
const router = useRouter();
|
|
1389
|
+
const { data, isLoading, error } = useLegalDocs(docType, { initialData, apiEndpoint });
|
|
1390
|
+
const backCfg = backButton === false ? void 0 : {
|
|
1391
|
+
label: backButton?.label ?? "Back to home",
|
|
1392
|
+
onClick: () => router.push(backButton?.href ?? "/")
|
|
1393
|
+
};
|
|
1394
|
+
const fallbackLastUpdatedLabel = data?.lastSynced != null ? formatLegalDate(data.lastSynced) : null;
|
|
1395
|
+
const effectiveLastUpdatedLabel = initialLastUpdatedLabel ?? fallbackLastUpdatedLabel;
|
|
1396
|
+
const subtitle = effectiveLastUpdatedLabel ? `Last Updated: ${effectiveLastUpdatedLabel}` : fallbackDescription;
|
|
1397
|
+
const inner = /* @__PURE__ */ jsxs9(PageLayout, { title, subtitle, backButton: backCfg, titleSize: "h1", children: [
|
|
1398
|
+
data?.sourceFile && /* @__PURE__ */ jsxs9("p", { className: "font-['DM_Sans'] text-sm text-ods-text-secondary opacity-75", children: [
|
|
1399
|
+
"Source: ",
|
|
1400
|
+
data.sourceFile
|
|
1401
|
+
] }),
|
|
1402
|
+
/* @__PURE__ */ jsx11("div", { className: "flex flex-col lg:flex-row gap-6 lg:gap-10 items-start flex-1", children: /* @__PURE__ */ jsx11("div", { className: "flex-1", children: /* @__PURE__ */ jsx11("div", { className: "w-full", children: /* @__PURE__ */ jsx11("article", { className: "space-y-2", children: isLoading ? (
|
|
1403
|
+
// Loading skeleton matching Knowledge Hub pattern
|
|
1404
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-6", children: [
|
|
1405
|
+
/* @__PURE__ */ jsx11("div", { className: "h-10 bg-ods-skeleton rounded-lg w-3/4 animate-pulse" }),
|
|
1406
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-4", children: [
|
|
1407
|
+
/* @__PURE__ */ jsx11("div", { className: "h-4 bg-ods-skeleton rounded w-full animate-pulse" }),
|
|
1408
|
+
/* @__PURE__ */ jsx11("div", { className: "h-4 bg-ods-skeleton rounded w-full animate-pulse" }),
|
|
1409
|
+
/* @__PURE__ */ jsx11("div", { className: "h-4 bg-ods-skeleton rounded w-5/6 animate-pulse" })
|
|
1410
|
+
] }),
|
|
1411
|
+
/* @__PURE__ */ jsx11("div", { className: "h-32 bg-ods-card border border-ods-border rounded-lg animate-pulse" }),
|
|
1412
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-4", children: [
|
|
1413
|
+
/* @__PURE__ */ jsx11("div", { className: "h-4 bg-ods-skeleton rounded w-full animate-pulse" }),
|
|
1414
|
+
/* @__PURE__ */ jsx11("div", { className: "h-4 bg-ods-skeleton rounded w-4/5 animate-pulse" })
|
|
1415
|
+
] })
|
|
1416
|
+
] })
|
|
1417
|
+
) : error ? /* @__PURE__ */ jsxs9("div", { className: "text-center space-y-4", children: [
|
|
1418
|
+
/* @__PURE__ */ jsxs9("div", { className: "bg-red-900/20 border border-red-700 rounded-lg p-6", children: [
|
|
1419
|
+
/* @__PURE__ */ jsx11("p", { className: "text-red-400 mb-2", children: errorTitle }),
|
|
1420
|
+
/* @__PURE__ */ jsx11("p", { className: "text-red-300 text-sm", children: error })
|
|
1421
|
+
] }),
|
|
1422
|
+
/* @__PURE__ */ jsxs9("div", { className: "text-ods-text-secondary", children: [
|
|
1423
|
+
/* @__PURE__ */ jsx11("p", { children: errorContactPrompt }),
|
|
1424
|
+
/* @__PURE__ */ jsx11("a", { href: `mailto:${contactEmail}`, className: "text-ods-accent hover:underline", children: contactEmail })
|
|
1425
|
+
] })
|
|
1426
|
+
] }) : data ? /* @__PURE__ */ jsx11(
|
|
1427
|
+
MarkdownRenderer,
|
|
1428
|
+
{
|
|
1429
|
+
content: data.content,
|
|
1430
|
+
sectionIds: data.sections || [],
|
|
1431
|
+
demoteMarkdownH1ToH2: true
|
|
1432
|
+
}
|
|
1433
|
+
) : /* @__PURE__ */ jsxs9("div", { className: "text-center text-ods-text-secondary py-16", children: [
|
|
1434
|
+
/* @__PURE__ */ jsx11("p", { className: "text-xl", children: emptyStateMessage }),
|
|
1435
|
+
/* @__PURE__ */ jsxs9("p", { className: "mt-2", children: [
|
|
1436
|
+
"Please contact",
|
|
1437
|
+
" ",
|
|
1438
|
+
/* @__PURE__ */ jsx11("a", { href: `mailto:${contactEmail}`, className: "text-ods-accent hover:underline", children: contactEmail }),
|
|
1439
|
+
" ",
|
|
1440
|
+
"for more information."
|
|
1441
|
+
] })
|
|
1442
|
+
] }) }) }) }) })
|
|
1443
|
+
] });
|
|
1444
|
+
return shell ? /* @__PURE__ */ jsx11(PageShell, { children: inner }) : /* @__PURE__ */ jsx11("div", { className: "page-shell-content", children: inner });
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
export {
|
|
1448
|
+
PersistentPagination,
|
|
1449
|
+
usePaginationLoading,
|
|
1450
|
+
PersistentPaginationWrapper,
|
|
1451
|
+
ProductReleasesView,
|
|
1452
|
+
MediaGalleryStrip,
|
|
1453
|
+
ReleaseDetailPage,
|
|
1454
|
+
ReleaseDetailSkeleton,
|
|
1455
|
+
useRoadmapVoting,
|
|
1456
|
+
RoadmapGrid,
|
|
1457
|
+
RoadmapGridSkeleton,
|
|
1458
|
+
RoadmapView,
|
|
1459
|
+
DeliveryTable,
|
|
1460
|
+
DeliveryLists,
|
|
1461
|
+
useLegalDocs,
|
|
1462
|
+
LegalDocumentPage
|
|
1463
|
+
};
|
|
1464
|
+
//# sourceMappingURL=chunk-CSLMCBZV.js.map
|