@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
|
@@ -2,19 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* FaqDocumentPage — the full `/faqs` page with chrome, so embedders drop in
|
|
5
|
-
* ONE component instead of hand-assembling `PageShell` + `PageLayout`
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* ONE component instead of hand-assembling `PageShell` + `PageLayout` around
|
|
6
|
+
* the bare `<FaqSection>`. Mirrors `LegalDocumentPage` / `DevSectionPage`: the
|
|
7
|
+
* page-level layout lives in the lib, the host page is a thin wrapper that
|
|
8
|
+
* passes only config + the SSR-resolved data.
|
|
9
9
|
*
|
|
10
|
-
* HERO:
|
|
11
|
-
*
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* nests its category headings as `<h2>` and the questions as `<h3>` (the
|
|
17
|
-
* SEO-recommended FAQ document outline).
|
|
10
|
+
* HERO: the title + subtitle route through the canonical (frozen) `PageLayout`
|
|
11
|
+
* `TitleBlock` with `titleSize="h1"` — the unified Help Center header (the
|
|
12
|
+
* `TitleBlock` element is always an `<h1>`; `titleSize` only sets its typography,
|
|
13
|
+
* here `text-h1`). The page owns that `<h1>`, so `<FaqSection heading={null}>`
|
|
14
|
+
* nests its category headings as `<h2>`/`<h3>` (the SEO-recommended FAQ document
|
|
15
|
+
* outline).
|
|
18
16
|
*
|
|
19
17
|
* DATA: pass `initialFaqs` (SSR-resolved, platform-scoped) so `FaqSection`
|
|
20
18
|
* skips its client self-fetch — the standalone page's static platform-only
|
|
@@ -22,27 +20,25 @@
|
|
|
22
20
|
* suggestion-fill instead.
|
|
23
21
|
*/
|
|
24
22
|
|
|
25
|
-
import React from 'react';
|
|
26
|
-
import { HelpCircle } from 'lucide-react';
|
|
27
23
|
import type { Faq } from '../../types/faq';
|
|
28
24
|
import { PageShell, PageLayout } from '../ui';
|
|
29
25
|
import { useRouter } from '../../embed-shims/next-navigation';
|
|
30
|
-
import { SECTION_HERO_ICON_CLASS } from '../../utils/page-header-constants';
|
|
31
26
|
import { FaqSection } from './faq-section';
|
|
32
27
|
import type { FaqSchemaOptions } from './json-ld';
|
|
33
28
|
|
|
34
29
|
export interface FaqDocumentPageProps {
|
|
35
|
-
/**
|
|
30
|
+
/** Page title (frozen `PageLayout` `TitleBlock`). Default "Frequently Asked Questions". */
|
|
36
31
|
title?: string;
|
|
37
|
-
/** Subtitle under the title
|
|
32
|
+
/** Subtitle under the title. */
|
|
38
33
|
subtitle?: string;
|
|
39
|
-
/** Icon rendered before the title. Default `<HelpCircle>` (the FAQ glyph). */
|
|
40
|
-
titleIcon?: React.ReactNode;
|
|
41
|
-
/** Render the yellow accent dot after the title (default true). */
|
|
42
|
-
accentDot?: boolean;
|
|
43
34
|
/** Back-button config — same pattern as `DevSectionPage` / `LegalDocumentPage`.
|
|
44
35
|
* Pass `false` to hide. Default `{ label: 'Back to home', href: '/' }`. */
|
|
45
36
|
backButton?: { label?: string; href?: string } | false;
|
|
37
|
+
/** Render the standalone `<PageShell>` (own `<main>` + bg + max-width). Default
|
|
38
|
+
* true. Pass false when the host layout already provides the page container
|
|
39
|
+
* (e.g. openframe-frontend's `AppLayout` `<main>`) — only the padding box
|
|
40
|
+
* renders, avoiding a nested `<main>`. */
|
|
41
|
+
shell?: boolean;
|
|
46
42
|
/** SSR-hydrate `FaqSection` (skips the client fetch — the platform-only
|
|
47
43
|
* contract for the standalone page). Omit for self-fetching embeds. */
|
|
48
44
|
initialFaqs?: Faq[];
|
|
@@ -63,8 +59,6 @@ export interface FaqDocumentPageProps {
|
|
|
63
59
|
export function FaqDocumentPage({
|
|
64
60
|
title = 'Frequently Asked Questions',
|
|
65
61
|
subtitle,
|
|
66
|
-
titleIcon = <HelpCircle className={SECTION_HERO_ICON_CLASS} />,
|
|
67
|
-
accentDot = true,
|
|
68
62
|
backButton,
|
|
69
63
|
initialFaqs,
|
|
70
64
|
emitJsonLd,
|
|
@@ -73,6 +67,7 @@ export function FaqDocumentPage({
|
|
|
73
67
|
entityType,
|
|
74
68
|
entityId,
|
|
75
69
|
minResults,
|
|
70
|
+
shell = true,
|
|
76
71
|
}: FaqDocumentPageProps) {
|
|
77
72
|
const router = useRouter();
|
|
78
73
|
|
|
@@ -87,42 +82,20 @@ export function FaqDocumentPage({
|
|
|
87
82
|
onClick: () => router.push(backButton?.href ?? '/'),
|
|
88
83
|
};
|
|
89
84
|
|
|
90
|
-
|
|
91
|
-
<
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
<span>
|
|
104
|
-
{title}
|
|
105
|
-
{accentDot && <span className="text-ods-accent">.</span>}
|
|
106
|
-
</span>
|
|
107
|
-
</h1>
|
|
108
|
-
<p className="font-['DM_Sans'] font-medium text-[18px] leading-[28px] text-ods-text-secondary max-w-3xl line-clamp-2 min-h-[56px]">
|
|
109
|
-
{subtitle || ' '}
|
|
110
|
-
</p>
|
|
111
|
-
</div>
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
114
|
-
<FaqSection
|
|
115
|
-
initialFaqs={initialFaqs}
|
|
116
|
-
heading={null}
|
|
117
|
-
emitJsonLd={emitJsonLd}
|
|
118
|
-
jsonLd={jsonLd}
|
|
119
|
-
apiBaseUrl={apiBaseUrl}
|
|
120
|
-
entityType={entityType}
|
|
121
|
-
entityId={entityId}
|
|
122
|
-
minResults={minResults}
|
|
123
|
-
/>
|
|
124
|
-
</div>
|
|
125
|
-
</PageLayout>
|
|
126
|
-
</PageShell>
|
|
85
|
+
const inner = (
|
|
86
|
+
<PageLayout title={title} subtitle={subtitle} backButton={backCfg} titleSize="h1">
|
|
87
|
+
<FaqSection
|
|
88
|
+
initialFaqs={initialFaqs}
|
|
89
|
+
heading={null}
|
|
90
|
+
emitJsonLd={emitJsonLd}
|
|
91
|
+
jsonLd={jsonLd}
|
|
92
|
+
apiBaseUrl={apiBaseUrl}
|
|
93
|
+
entityType={entityType}
|
|
94
|
+
entityId={entityId}
|
|
95
|
+
minResults={minResults}
|
|
96
|
+
/>
|
|
97
|
+
</PageLayout>
|
|
127
98
|
);
|
|
99
|
+
|
|
100
|
+
return shell ? <PageShell>{inner}</PageShell> : <div className="page-shell-content">{inner}</div>;
|
|
128
101
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<DeliveryPage>` — the full bug-fixes & enhancements (`delivery`) page:
|
|
5
|
+
* `DevSectionPage sectionKey="delivery"` chrome wrapping the self-contained
|
|
6
|
+
* `<DeliveryLists>` (Recently Completed + Active Tasks tables). Hosts configure
|
|
7
|
+
* only the two bucket api routes.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ReactNode } from 'react'
|
|
11
|
+
import { DevSectionPage } from '../shared/dev-section'
|
|
12
|
+
import { DeliveryLists } from '../shared/delivery'
|
|
13
|
+
|
|
14
|
+
export interface DeliveryPageProps {
|
|
15
|
+
/** GET endpoint for the "Recently Completed" bucket. Default `/api/delivery/completed`. */
|
|
16
|
+
completedEndpoint?: string
|
|
17
|
+
/** GET endpoint for the "Active Tasks" bucket. Default `/api/delivery/in-progress`. */
|
|
18
|
+
inProgressEndpoint?: string
|
|
19
|
+
/** Back-button config. Pass `false` to hide. Default `{ href: '/' }`. */
|
|
20
|
+
backButton?: { label?: string; href?: string } | false
|
|
21
|
+
title?: string
|
|
22
|
+
subtitle?: string
|
|
23
|
+
/** Optional slot rendered below the lists, inside the page chrome. */
|
|
24
|
+
belowContent?: ReactNode
|
|
25
|
+
/** Render the standalone `<PageShell>`. Default true. Pass false when the host
|
|
26
|
+
* layout already provides the page container (forwarded to `DevSectionPage`). */
|
|
27
|
+
shell?: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function DeliveryPage({
|
|
31
|
+
completedEndpoint,
|
|
32
|
+
inProgressEndpoint,
|
|
33
|
+
backButton,
|
|
34
|
+
title,
|
|
35
|
+
subtitle,
|
|
36
|
+
belowContent,
|
|
37
|
+
shell,
|
|
38
|
+
}: DeliveryPageProps) {
|
|
39
|
+
return (
|
|
40
|
+
<DevSectionPage sectionKey="delivery" backButton={backButton} title={title} subtitle={subtitle} shell={shell}>
|
|
41
|
+
<DeliveryLists completedApiEndpoint={completedEndpoint} inProgressApiEndpoint={inProgressEndpoint} />
|
|
42
|
+
{belowContent}
|
|
43
|
+
</DevSectionPage>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Help Center pages — ready-made, full-page components (each owns the canonical
|
|
5
|
+
* `PageShell` + `PageLayout` chrome) so a host route is a one-line mount with NO
|
|
6
|
+
* local wrapper. Shared by openframe-frontend (self-fetch via the `/content`
|
|
7
|
+
* proxy, mounted under `/help-center/*`) and multi-platform-hub (SSR with
|
|
8
|
+
* server-fetched `initialData` + `generateMetadata`/JSON-LD kept in the server
|
|
9
|
+
* route). Endpoint config + SSR data + optional slots are passed as props.
|
|
10
|
+
*
|
|
11
|
+
* This barrel ALSO re-exports the already-existing full-pages (FAQ / Legal /
|
|
12
|
+
* Release detail / Onboarding detail / Docs / Tickets) so consumers have ONE
|
|
13
|
+
* import site for "help center pages". Re-exports are NAMED (not `export *`) to
|
|
14
|
+
* avoid TS2308 ambiguous-re-export collisions, and this barrel is intentionally
|
|
15
|
+
* NOT merged into the top-level `components/index.ts`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// New combined pages. (The Help Center *index* landing is intentionally NOT
|
|
19
|
+
// here — it stays a host-local page; its links + icons are app-specific.)
|
|
20
|
+
export { RoadmapPage, type RoadmapPageProps } from './roadmap-page'
|
|
21
|
+
export { ProductReleasesListPage, type ProductReleasesListPageProps } from './product-releases-list-page'
|
|
22
|
+
export { DeliveryPage, type DeliveryPageProps } from './delivery-page'
|
|
23
|
+
export {
|
|
24
|
+
OnboardingGuidesCatalogPage,
|
|
25
|
+
type OnboardingGuidesCatalogPageProps,
|
|
26
|
+
} from './onboarding-guides-catalog-page'
|
|
27
|
+
|
|
28
|
+
// Existing full-pages re-exported for a single Help Center import site.
|
|
29
|
+
export { FaqDocumentPage, type FaqDocumentPageProps } from '../faq'
|
|
30
|
+
export { LegalDocumentPage, type LegalDocumentPageProps } from '../shared/legal-document'
|
|
31
|
+
export {
|
|
32
|
+
ReleaseDetailPage,
|
|
33
|
+
type ReleaseDetailPageProps,
|
|
34
|
+
type VideoDisplaySectionProps,
|
|
35
|
+
} from '../shared/product-release'
|
|
36
|
+
export {
|
|
37
|
+
OnboardingGuideDetailView,
|
|
38
|
+
type OnboardingGuideDetailViewProps,
|
|
39
|
+
} from '../onboarding-guides'
|
|
40
|
+
export { DocsHubPage } from '../docs'
|
|
41
|
+
export { HelpCenterList } from '../tickets'
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<OnboardingGuidesCatalogPage>` — the full `/onboarding-guides` catalog page:
|
|
5
|
+
* `DevSectionPage sectionKey="onboarding"` chrome wrapping the self-contained
|
|
6
|
+
* `<OnboardingGuidesCatalogView>` (RAG search + section pills + guide grid).
|
|
7
|
+
* Supports both SSR (`initialGuides`/`initialSections`) and self-fetch
|
|
8
|
+
* (`guidesEndpoint`/`sectionsEndpoint`) modes — forwarded to the view verbatim.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ReactNode } from 'react'
|
|
12
|
+
import type { OnboardingGuide } from '../chat/types/entities/onboarding-guide'
|
|
13
|
+
import { DevSectionPage } from '../shared/dev-section'
|
|
14
|
+
import { OnboardingGuidesCatalogView } from '../onboarding-guides'
|
|
15
|
+
|
|
16
|
+
type SectionSummary = { section: string; section_order: number; count: number }
|
|
17
|
+
|
|
18
|
+
export interface OnboardingGuidesCatalogPageProps {
|
|
19
|
+
/** Self-fetch: GET list endpoint (the api route). Appends `?section=`. */
|
|
20
|
+
guidesEndpoint?: string
|
|
21
|
+
/** Self-fetch: GET section-summary endpoint (the api route). */
|
|
22
|
+
sectionsEndpoint?: string
|
|
23
|
+
/** Controlled / SSR: server-fetched guides + sections + active section. */
|
|
24
|
+
initialGuides?: OnboardingGuide[]
|
|
25
|
+
initialSections?: SectionSummary[]
|
|
26
|
+
initialSection?: string
|
|
27
|
+
/** Base path the catalog is mounted under (card href prefix + `?section=` push). */
|
|
28
|
+
basePath?: string
|
|
29
|
+
/** Back-button config. Pass `false` to hide. Default `{ href: '/' }`. */
|
|
30
|
+
backButton?: { label?: string; href?: string } | false
|
|
31
|
+
title?: string
|
|
32
|
+
subtitle?: string
|
|
33
|
+
/** Optional slot rendered below the catalog, inside the page chrome. */
|
|
34
|
+
belowContent?: ReactNode
|
|
35
|
+
/** Render the standalone `<PageShell>`. Default true. Pass false when the host
|
|
36
|
+
* layout already provides the page container (forwarded to `DevSectionPage`). */
|
|
37
|
+
shell?: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function OnboardingGuidesCatalogPage({
|
|
41
|
+
guidesEndpoint,
|
|
42
|
+
sectionsEndpoint,
|
|
43
|
+
initialGuides,
|
|
44
|
+
initialSections,
|
|
45
|
+
initialSection,
|
|
46
|
+
basePath,
|
|
47
|
+
backButton,
|
|
48
|
+
title,
|
|
49
|
+
subtitle,
|
|
50
|
+
belowContent,
|
|
51
|
+
shell,
|
|
52
|
+
}: OnboardingGuidesCatalogPageProps) {
|
|
53
|
+
return (
|
|
54
|
+
<DevSectionPage sectionKey="onboarding" backButton={backButton} title={title} subtitle={subtitle} shell={shell}>
|
|
55
|
+
<OnboardingGuidesCatalogView
|
|
56
|
+
guidesEndpoint={guidesEndpoint}
|
|
57
|
+
sectionsEndpoint={sectionsEndpoint}
|
|
58
|
+
initialGuides={initialGuides}
|
|
59
|
+
initialSections={initialSections}
|
|
60
|
+
initialSection={initialSection}
|
|
61
|
+
basePath={basePath}
|
|
62
|
+
/>
|
|
63
|
+
{belowContent}
|
|
64
|
+
</DevSectionPage>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<ProductReleasesListPage>` — the full `/releases` list page: `DevSectionPage
|
|
5
|
+
* sectionKey="releases"` chrome wrapping the self-contained `<ProductReleasesView>`.
|
|
6
|
+
* Hosts configure the api route + detail `basePath` (+ optional SSR `initialData`
|
|
7
|
+
* and a custom card-prop builder). Card → detail nav flows through
|
|
8
|
+
* `runtime.composeContentUrl`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ReactNode } from 'react'
|
|
12
|
+
import type { ProductRelease, ProductReleaseListResponse } from '../../types/product-release'
|
|
13
|
+
import { DevSectionPage } from '../shared/dev-section'
|
|
14
|
+
import { ProductReleasesView, type ProductReleaseCardExtras } from '../shared/product-release'
|
|
15
|
+
|
|
16
|
+
export interface ProductReleasesListPageProps {
|
|
17
|
+
/** GET list endpoint (the api route). Default `/api/releases`. */
|
|
18
|
+
releasesEndpoint?: string
|
|
19
|
+
/** Fallback detail-href prefix when `composeContentUrl` is not wired. Default `/releases`. */
|
|
20
|
+
basePath?: string
|
|
21
|
+
/** Derive the per-card prop bundle (defaults to the lib's rich builder). */
|
|
22
|
+
buildCardProps?: (release: ProductRelease) => ProductReleaseCardExtras
|
|
23
|
+
/** Optional SSR hydrate for the first page (hub server-fetch). */
|
|
24
|
+
initialData?: ProductReleaseListResponse
|
|
25
|
+
/** Back-button config. Pass `false` to hide. Default `{ href: '/' }`. */
|
|
26
|
+
backButton?: { label?: string; href?: string } | false
|
|
27
|
+
title?: string
|
|
28
|
+
subtitle?: string
|
|
29
|
+
/** Optional slot rendered below the list, inside the page chrome. */
|
|
30
|
+
belowContent?: ReactNode
|
|
31
|
+
/** Render the standalone `<PageShell>`. Default true. Pass false when the host
|
|
32
|
+
* layout already provides the page container (forwarded to `DevSectionPage`). */
|
|
33
|
+
shell?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function ProductReleasesListPage({
|
|
37
|
+
releasesEndpoint,
|
|
38
|
+
basePath,
|
|
39
|
+
buildCardProps,
|
|
40
|
+
initialData,
|
|
41
|
+
backButton,
|
|
42
|
+
title,
|
|
43
|
+
subtitle,
|
|
44
|
+
belowContent,
|
|
45
|
+
shell,
|
|
46
|
+
}: ProductReleasesListPageProps) {
|
|
47
|
+
return (
|
|
48
|
+
<DevSectionPage sectionKey="releases" backButton={backButton} title={title} subtitle={subtitle} shell={shell}>
|
|
49
|
+
<ProductReleasesView
|
|
50
|
+
endpoint={releasesEndpoint}
|
|
51
|
+
basePath={basePath}
|
|
52
|
+
buildCardProps={buildCardProps}
|
|
53
|
+
initialData={initialData}
|
|
54
|
+
/>
|
|
55
|
+
{belowContent}
|
|
56
|
+
</DevSectionPage>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<RoadmapPage>` — the full `/roadmap` page: `DevSectionPage sectionKey="roadmap"`
|
|
5
|
+
* (PageShell + PageLayout + hero + search/status chrome) wrapping the
|
|
6
|
+
* self-contained `<RoadmapView>` list. Hosts configure only the api routes
|
|
7
|
+
* (+ optional SSR `initialItems`); both openframe-frontend and the hub mount
|
|
8
|
+
* this instead of hand-composing `DevSectionPage` + `RoadmapView`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ReactNode } from 'react'
|
|
12
|
+
import type { RoadmapItem } from '../chat/types/entities/roadmap-item'
|
|
13
|
+
import { DevSectionPage } from '../shared/dev-section'
|
|
14
|
+
import { RoadmapView } from '../shared/roadmap'
|
|
15
|
+
import type { UseRoadmapVotingOptions } from '../shared/roadmap'
|
|
16
|
+
|
|
17
|
+
export interface RoadmapPageProps {
|
|
18
|
+
/** GET list endpoint (the api route). Default `/api/roadmap`. */
|
|
19
|
+
roadmapEndpoint?: string
|
|
20
|
+
/** Per-task refresh URL builder (after a vote). Default `/api/roadmap/<id>`. */
|
|
21
|
+
buildRefreshUrl?: (taskId: string) => string
|
|
22
|
+
/** Vote POST endpoint, forwarded to `RoadmapView` via `votingOptions`. */
|
|
23
|
+
voteApiEndpoint?: string
|
|
24
|
+
/** Full voting options override — takes precedence over `voteApiEndpoint`. */
|
|
25
|
+
votingOptions?: UseRoadmapVotingOptions
|
|
26
|
+
/** Optional SSR hydrate (hub server-fetch) — skips the initial client fetch. */
|
|
27
|
+
initialItems?: RoadmapItem[]
|
|
28
|
+
/** Indent the grid (the hub's full-page roadmap look). Default off. */
|
|
29
|
+
showLeftMargin?: boolean
|
|
30
|
+
/** Back-button config. Pass `false` to hide. Default `{ href: '/' }`. */
|
|
31
|
+
backButton?: { label?: string; href?: string } | false
|
|
32
|
+
/** Override the hero title/subtitle (defaults from `OPENFRAME_DEV_SECTIONS`). */
|
|
33
|
+
title?: string
|
|
34
|
+
subtitle?: string
|
|
35
|
+
/** Optional slot rendered below the list, inside the page chrome — e.g. the
|
|
36
|
+
* hub's related-content rail. */
|
|
37
|
+
belowContent?: ReactNode
|
|
38
|
+
/** Render the standalone `<PageShell>`. Default true. Pass false when the host
|
|
39
|
+
* layout already provides the page container (forwarded to `DevSectionPage`). */
|
|
40
|
+
shell?: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function RoadmapPage({
|
|
44
|
+
roadmapEndpoint,
|
|
45
|
+
buildRefreshUrl,
|
|
46
|
+
voteApiEndpoint,
|
|
47
|
+
votingOptions,
|
|
48
|
+
initialItems,
|
|
49
|
+
showLeftMargin,
|
|
50
|
+
backButton,
|
|
51
|
+
title,
|
|
52
|
+
subtitle,
|
|
53
|
+
belowContent,
|
|
54
|
+
shell,
|
|
55
|
+
}: RoadmapPageProps) {
|
|
56
|
+
return (
|
|
57
|
+
<DevSectionPage sectionKey="roadmap" backButton={backButton} title={title} subtitle={subtitle} shell={shell}>
|
|
58
|
+
<RoadmapView
|
|
59
|
+
endpoint={roadmapEndpoint}
|
|
60
|
+
initialItems={initialItems}
|
|
61
|
+
showLeftMargin={showLeftMargin}
|
|
62
|
+
buildRefreshUrl={buildRefreshUrl}
|
|
63
|
+
votingOptions={votingOptions ?? (voteApiEndpoint ? { voteApiEndpoint } : undefined)}
|
|
64
|
+
/>
|
|
65
|
+
{belowContent}
|
|
66
|
+
</DevSectionPage>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
@@ -25,6 +25,12 @@
|
|
|
25
25
|
* If a new design genuinely needs different chrome: build a SEPARATE new
|
|
26
26
|
* component for it. Never mutate this one. If you believe an edit here is
|
|
27
27
|
* unavoidable, STOP and get explicit human sign-off first.
|
|
28
|
+
*
|
|
29
|
+
* SANCTIONED EXCEPTION (2026-06, explicit human sign-off): the OPTIONAL
|
|
30
|
+
* `titleSize` prop, forwarded to `TitleBlock`. Defaults to `'h2'` — the frozen
|
|
31
|
+
* baseline is unchanged for every existing caller. `titleSize="h1"` opts the
|
|
32
|
+
* title up to `text-h1` (the unified Help Center pages). Additive + default-
|
|
33
|
+
* preserving; do NOT change the default or anything else here.
|
|
28
34
|
* ========================================================================== */
|
|
29
35
|
|
|
30
36
|
import React from 'react'
|
|
@@ -49,6 +55,9 @@ export interface PageLayoutProps {
|
|
|
49
55
|
className?: string
|
|
50
56
|
contentClassName?: string
|
|
51
57
|
showHeader?: boolean
|
|
58
|
+
/** Title typography size, forwarded to `TitleBlock`. Default `'h2'` (frozen
|
|
59
|
+
* baseline). Pass `'h1'` for the unified Help Center pages. */
|
|
60
|
+
titleSize?: 'h1' | 'h2'
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
/**
|
|
@@ -70,6 +79,7 @@ export function PageLayout({
|
|
|
70
79
|
className,
|
|
71
80
|
contentClassName,
|
|
72
81
|
showHeader = true,
|
|
82
|
+
titleSize,
|
|
73
83
|
}: PageLayoutProps) {
|
|
74
84
|
const hasActions = actions && actions.length > 0
|
|
75
85
|
const needsBottomPadding = hasActions && actionsVariant === 'primary-buttons'
|
|
@@ -88,6 +98,7 @@ export function PageLayout({
|
|
|
88
98
|
menuActions={menuActions}
|
|
89
99
|
selector={selector}
|
|
90
100
|
variant={headerVariant}
|
|
101
|
+
titleSize={titleSize}
|
|
91
102
|
/>
|
|
92
103
|
)}
|
|
93
104
|
|
|
@@ -22,6 +22,13 @@
|
|
|
22
22
|
* current output. If a new design needs different chrome, build a SEPARATE new
|
|
23
23
|
* component — never mutate this one. If an edit here seems unavoidable, STOP
|
|
24
24
|
* and get explicit human sign-off first.
|
|
25
|
+
*
|
|
26
|
+
* SANCTIONED EXCEPTION (2026-06, explicit human sign-off): the OPTIONAL
|
|
27
|
+
* `titleSize` prop. It defaults to `'h2'` — i.e. the frozen baseline above is
|
|
28
|
+
* unchanged for EVERY existing caller. A caller may pass `titleSize="h1"` to
|
|
29
|
+
* opt the title typography up to `text-h1` (used by the unified Help Center
|
|
30
|
+
* pages). This is additive and default-preserving; do NOT change the default or
|
|
31
|
+
* touch anything else here.
|
|
25
32
|
* ========================================================================== */
|
|
26
33
|
|
|
27
34
|
import React from 'react'
|
|
@@ -59,6 +66,10 @@ export interface TitleBlockProps {
|
|
|
59
66
|
*/
|
|
60
67
|
variant?: 'plain' | 'card'
|
|
61
68
|
className?: string
|
|
69
|
+
/** Title typography size. Default `'h2'` (the frozen baseline). Pass `'h1'` to
|
|
70
|
+
* opt the title up to `text-h1` (the unified Help Center pages). Subtitle stays
|
|
71
|
+
* `text-h6` either way. */
|
|
72
|
+
titleSize?: 'h1' | 'h2'
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
export function TitleBlock({
|
|
@@ -72,9 +83,11 @@ export function TitleBlock({
|
|
|
72
83
|
selector,
|
|
73
84
|
variant = 'plain',
|
|
74
85
|
className,
|
|
86
|
+
titleSize = 'h2',
|
|
75
87
|
}: TitleBlockProps) {
|
|
76
88
|
const hasActions = actions && actions.length > 0
|
|
77
89
|
const hasMenuActions = !!menuActions && menuActions.some(g => g.items.length > 0)
|
|
90
|
+
const titleClass = titleSize === 'h1' ? 'text-h1' : 'text-h2'
|
|
78
91
|
|
|
79
92
|
return (
|
|
80
93
|
<div
|
|
@@ -113,7 +126,7 @@ export function TitleBlock({
|
|
|
113
126
|
)}
|
|
114
127
|
<div className="flex flex-col justify-center min-w-0 flex-1">
|
|
115
128
|
{title && (
|
|
116
|
-
<h1 className=
|
|
129
|
+
<h1 className={cn(titleClass, 'text-ods-text-primary truncate')} title={title}>{title}</h1>
|
|
117
130
|
)}
|
|
118
131
|
{subtitle && (
|
|
119
132
|
<p className="text-h6 text-ods-text-secondary truncate" title={subtitle}>{subtitle}</p>
|
|
@@ -121,7 +134,7 @@ export function TitleBlock({
|
|
|
121
134
|
</div>
|
|
122
135
|
</div>
|
|
123
136
|
) : (
|
|
124
|
-
title && <h1 className=
|
|
137
|
+
title && <h1 className={cn(titleClass, 'text-ods-text-primary')}>{title}</h1>
|
|
125
138
|
)}
|
|
126
139
|
</div>
|
|
127
140
|
|
|
@@ -74,6 +74,14 @@ export interface OnboardingGuideDetailViewProps {
|
|
|
74
74
|
/** Base path the related-card hrefs default to when
|
|
75
75
|
* `runtime.composeContentUrl` is not wired. Default `/onboarding-guides`. */
|
|
76
76
|
basePath?: string
|
|
77
|
+
/** Optional slot rendered inside the page chrome, BELOW the article + related
|
|
78
|
+
* guides — e.g. the hub's cross-type related-content / FAQ rail. Lets the hub
|
|
79
|
+
* mount this view directly (no local wrapper); embedders omit it. */
|
|
80
|
+
relatedContent?: ReactNode
|
|
81
|
+
/** Render the standalone `<PageShell>`. Default true. Pass false when the host
|
|
82
|
+
* layout already provides the page container — only the padding box renders,
|
|
83
|
+
* avoiding a nested `<main>`. */
|
|
84
|
+
shell?: boolean
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
export function OnboardingGuideDetailView({
|
|
@@ -89,10 +97,16 @@ export function OnboardingGuideDetailView({
|
|
|
89
97
|
backHref,
|
|
90
98
|
backLabel = 'Back to Getting Started',
|
|
91
99
|
basePath = '/onboarding-guides',
|
|
100
|
+
relatedContent,
|
|
101
|
+
shell = true,
|
|
92
102
|
}: OnboardingGuideDetailViewProps) {
|
|
93
103
|
const resolvedBackHref = backHref ?? basePath
|
|
94
104
|
const runtime = useChatRuntime()
|
|
95
105
|
const router = useRouter()
|
|
106
|
+
// `shell` true → standalone `<PageShell>`; false → padding-only box (no nested
|
|
107
|
+
// <main>) for hosts whose layout already provides the container.
|
|
108
|
+
const renderShell = (node: ReactNode) =>
|
|
109
|
+
shell ? <PageShell>{node}</PageShell> : <div className="page-shell-content">{node}</div>
|
|
96
110
|
|
|
97
111
|
// Controlled (hub SSR `initialData`) OR self-fetch by slug (config-only embed).
|
|
98
112
|
const url = initialData ? null : slug && guideEndpoint ? guideEndpoint(slug) : null
|
|
@@ -106,25 +120,19 @@ export function OnboardingGuideDetailView({
|
|
|
106
120
|
})
|
|
107
121
|
|
|
108
122
|
if (error || (!guide && !isLoading)) {
|
|
109
|
-
return (
|
|
110
|
-
<PageShell>
|
|
111
|
-
<LoadError message="Guide not found." onRetry={reload} />
|
|
112
|
-
</PageShell>
|
|
113
|
-
)
|
|
123
|
+
return renderShell(<LoadError message="Guide not found." onRetry={reload} />)
|
|
114
124
|
}
|
|
115
125
|
if (!guide) {
|
|
116
126
|
// Skeleton (not a bare "Loading…") for parity with every other shared view —
|
|
117
127
|
// catalog, roadmap, releases all render a skeleton on first load, so the detail
|
|
118
128
|
// page shouldn't flash text then content. `bare` + `PageShell` so the loading
|
|
119
129
|
// state matches the loaded page's full width / padding / min-height.
|
|
120
|
-
return (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<
|
|
125
|
-
|
|
126
|
-
</div>
|
|
127
|
-
</PageShell>
|
|
130
|
+
return renderShell(
|
|
131
|
+
// Match the loaded page's top offset (TitleBlock's
|
|
132
|
+
// `pt-[var(--spacing-system-l)]`) so content doesn't jump on load.
|
|
133
|
+
<div className="pt-[var(--spacing-system-l)]">
|
|
134
|
+
<DetailPageSkeleton bare showImageGallery={false} />
|
|
135
|
+
</div>
|
|
128
136
|
)
|
|
129
137
|
}
|
|
130
138
|
|
|
@@ -149,11 +157,12 @@ export function OnboardingGuideDetailView({
|
|
|
149
157
|
}
|
|
150
158
|
const renderRelatedCardFn = renderRelatedCard ?? defaultRenderRelatedCard
|
|
151
159
|
|
|
152
|
-
return (
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
160
|
+
return renderShell(
|
|
161
|
+
<PageLayout
|
|
162
|
+
title={guide.title}
|
|
163
|
+
titleSize="h1"
|
|
164
|
+
backButton={{ label: backLabel, onClick: () => router.push(resolvedBackHref) }}
|
|
165
|
+
>
|
|
157
166
|
{/* Tags — flat onboarding_guide_tags[] from entity_tags. */}
|
|
158
167
|
<EntityTagBadges tags={(guide as any).onboarding_guide_tags} />
|
|
159
168
|
|
|
@@ -232,7 +241,9 @@ export function OnboardingGuideDetailView({
|
|
|
232
241
|
</ul>
|
|
233
242
|
</div>
|
|
234
243
|
)}
|
|
244
|
+
|
|
245
|
+
{/* Host slot — cross-type related-content / FAQ rail. */}
|
|
246
|
+
{relatedContent}
|
|
235
247
|
</PageLayout>
|
|
236
|
-
</PageShell>
|
|
237
248
|
)
|
|
238
249
|
}
|