@flamingo-stack/openframe-frontend-core 0.0.274 → 0.0.275
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-XDGDCO3I.cjs → chunk-5LOLBUZG.cjs} +45 -44
- package/dist/chunk-5LOLBUZG.cjs.map +1 -0
- package/dist/{chunk-4YKGXZ7W.js → chunk-7AH75KFQ.js} +1 -1
- package/dist/chunk-7AH75KFQ.js.map +1 -0
- package/dist/{chunk-CYMMGPPV.js → chunk-CTBL56EC.js} +41 -5
- package/dist/chunk-CTBL56EC.js.map +1 -0
- package/dist/{chunk-2KHFSSFZ.cjs → chunk-DBJZF2OV.cjs} +15 -15
- package/dist/{chunk-2KHFSSFZ.cjs.map → chunk-DBJZF2OV.cjs.map} +1 -1
- package/dist/{chunk-4DYXV3E7.cjs → chunk-DEAWHNYW.cjs} +1177 -1223
- package/dist/chunk-DEAWHNYW.cjs.map +1 -0
- package/dist/{chunk-WU6PMH5L.cjs → chunk-E6RCVOQG.cjs} +41 -43
- package/dist/chunk-E6RCVOQG.cjs.map +1 -0
- package/dist/{chunk-FLIWL6V4.js → chunk-EINXNG5D.js} +4 -4
- package/dist/{chunk-OPSZNW3I.js → chunk-EKWPIEHH.js} +3031 -3077
- package/dist/chunk-EKWPIEHH.js.map +1 -0
- package/dist/{chunk-N5MCKGAB.js → chunk-FDHKJJPK.js} +34 -33
- package/dist/{chunk-N5MCKGAB.js.map → chunk-FDHKJJPK.js.map} +1 -1
- package/dist/{chunk-BSOHVQ7X.js → chunk-GHWOQUB4.js} +366 -391
- package/dist/chunk-GHWOQUB4.js.map +1 -0
- package/dist/{chunk-T7AJBKZP.cjs → chunk-GVSTRN36.cjs} +5 -2
- package/dist/chunk-GVSTRN36.cjs.map +1 -0
- package/dist/{chunk-L4LAAA5Q.js → chunk-IMNGGLKL.js} +5 -2
- package/dist/chunk-IMNGGLKL.js.map +1 -0
- package/dist/{chunk-VASNLWPZ.js → chunk-NY7OG2OJ.js} +5 -7
- package/dist/{chunk-VASNLWPZ.js.map → chunk-NY7OG2OJ.js.map} +1 -1
- package/dist/{chunk-5QJQAFTP.cjs → chunk-OKDCUJOZ.cjs} +48 -12
- package/dist/chunk-OKDCUJOZ.cjs.map +1 -0
- package/dist/{chunk-MEAR2WHS.cjs → chunk-PC7YOZ4I.cjs} +1 -1
- package/dist/chunk-PC7YOZ4I.cjs.map +1 -0
- package/dist/{chunk-QLWBBCI5.cjs → chunk-Q2ITV3NY.cjs} +5 -4
- package/dist/chunk-Q2ITV3NY.cjs.map +1 -0
- package/dist/chunk-RGALV4UX.js +433 -0
- package/dist/chunk-RGALV4UX.js.map +1 -0
- package/dist/{chunk-W5KBDFCR.cjs → chunk-SA4ZTD3G.cjs} +30 -30
- package/dist/{chunk-W5KBDFCR.cjs.map → chunk-SA4ZTD3G.cjs.map} +1 -1
- package/dist/{chunk-GL5X6VVC.js → chunk-T4XXYAYY.js} +4 -4
- package/dist/{chunk-ZMFT5XOB.js → chunk-UXJCZOLA.js} +4 -3
- package/dist/chunk-UXJCZOLA.js.map +1 -0
- package/dist/{chunk-7NJUPRN6.cjs → chunk-XCPBD2PG.cjs} +183 -208
- package/dist/chunk-XCPBD2PG.cjs.map +1 -0
- package/dist/chunk-ZSUNQJRO.cjs +433 -0
- package/dist/chunk-ZSUNQJRO.cjs.map +1 -0
- package/dist/components/chat/chat-message-row.d.ts +1 -1
- package/dist/components/chat/chat-message-row.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +10 -11
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.js +12 -13
- package/dist/components/contact/index.cjs +7 -9
- package/dist/components/contact/index.cjs.map +1 -1
- package/dist/components/contact/index.js +6 -8
- package/dist/components/faq/faq-document-page.d.ts +21 -0
- package/dist/components/faq/faq-document-page.d.ts.map +1 -0
- package/dist/components/faq/index.cjs +22 -5
- package/dist/components/faq/index.cjs.map +1 -1
- package/dist/components/faq/index.d.ts +1 -0
- package/dist/components/faq/index.d.ts.map +1 -1
- package/dist/components/faq/index.js +21 -4
- package/dist/components/features/index.cjs +6 -8
- package/dist/components/features/index.cjs.map +1 -1
- package/dist/components/features/index.js +5 -7
- package/dist/components/index.cjs +304 -309
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +249 -254
- package/dist/components/index.js.map +1 -1
- package/dist/components/layout/article-detail-layout.d.ts +21 -9
- package/dist/components/layout/article-detail-layout.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +6 -8
- package/dist/components/navigation/index.cjs.map +1 -1
- package/dist/components/navigation/index.js +5 -7
- package/dist/components/onboarding-guides/index.cjs +33 -46
- package/dist/components/onboarding-guides/index.cjs.map +1 -1
- package/dist/components/onboarding-guides/index.js +11 -24
- package/dist/components/onboarding-guides/index.js.map +1 -1
- package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
- package/dist/components/related-content/index.cjs +8 -10
- package/dist/components/related-content/index.cjs.map +1 -1
- package/dist/components/related-content/index.js +7 -9
- package/dist/components/shared/delivery/delivery-lists.d.ts.map +1 -1
- package/dist/components/shared/detail-page-skeleton.d.ts +6 -1
- package/dist/components/shared/detail-page-skeleton.d.ts.map +1 -1
- package/dist/components/shared/legal-document/use-legal-docs.d.ts.map +1 -1
- package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
- package/dist/components/shared/roadmap/roadmap-grid.d.ts.map +1 -1
- package/dist/components/shared/roadmap/use-roadmap-voting.d.ts.map +1 -1
- package/dist/components/tickets/help-center-list.d.ts +9 -1
- package/dist/components/tickets/help-center-list.d.ts.map +1 -1
- package/dist/components/tickets/hooks/use-ticket-actions.d.ts.map +1 -1
- package/dist/components/tickets/hooks/use-ticket-engagements.d.ts.map +1 -1
- package/dist/components/tickets/hooks/use-tickets-list.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +81 -76
- package/dist/components/tickets/index.cjs.map +1 -1
- package/dist/components/tickets/index.js +27 -22
- package/dist/components/tickets/index.js.map +1 -1
- package/dist/components/ui/index.cjs +12 -15
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +24 -27
- package/dist/contexts/chat-runtime-context.d.ts +9 -0
- package/dist/contexts/chat-runtime-context.d.ts.map +1 -1
- package/dist/contexts/index.cjs +2 -2
- package/dist/contexts/index.js +1 -1
- package/dist/hooks/index.cjs +3 -3
- package/dist/hooks/index.js +2 -2
- package/dist/hooks/use-contact-submission.d.ts.map +1 -1
- package/dist/hooks/use-self-fetch.d.ts.map +1 -1
- package/dist/index.cjs +11 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +32 -36
- package/dist/utils/embed-authed-fetch.d.ts +7 -0
- package/dist/utils/embed-authed-fetch.d.ts.map +1 -1
- package/dist/utils/embed-content-fetch.d.ts +2 -0
- package/dist/utils/embed-content-fetch.d.ts.map +1 -0
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/chat-message-row.tsx +12 -2
- package/src/components/faq/faq-document-page.tsx +69 -0
- package/src/components/faq/index.ts +1 -0
- package/src/components/layout/article-detail-layout.tsx +24 -11
- package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +21 -16
- package/src/components/shared/delivery/delivery-lists.tsx +3 -2
- package/src/components/shared/detail-page-skeleton.tsx +18 -10
- package/src/components/shared/legal-document/use-legal-docs.ts +2 -1
- package/src/components/shared/product-release/release-detail-page.tsx +27 -17
- package/src/components/shared/roadmap/roadmap-grid.tsx +2 -1
- package/src/components/shared/roadmap/use-roadmap-voting.ts +3 -2
- package/src/components/tickets/help-center-list.tsx +12 -4
- package/src/components/tickets/hooks/use-ticket-actions.ts +7 -2
- package/src/components/tickets/hooks/use-ticket-engagements.ts +6 -1
- package/src/components/tickets/hooks/use-tickets-list.ts +7 -1
- package/src/components/tickets/ticket-detail-drawer.tsx +1 -0
- package/src/contexts/chat-runtime-context.tsx +9 -0
- package/src/hooks/use-contact-submission.ts +2 -1
- package/src/hooks/use-self-fetch.ts +2 -1
- package/src/styles/index.css +1 -0
- package/src/styles/ods-page-shell.css +29 -0
- package/src/utils/embed-authed-fetch.ts +10 -0
- package/src/utils/embed-content-fetch.ts +30 -0
- package/dist/chunk-4DYXV3E7.cjs.map +0 -1
- package/dist/chunk-4YKGXZ7W.js.map +0 -1
- package/dist/chunk-5QJQAFTP.cjs.map +0 -1
- package/dist/chunk-722TTRIJ.cjs +0 -29
- package/dist/chunk-722TTRIJ.cjs.map +0 -1
- package/dist/chunk-7NJUPRN6.cjs.map +0 -1
- package/dist/chunk-BSOHVQ7X.js.map +0 -1
- package/dist/chunk-CYMMGPPV.js.map +0 -1
- package/dist/chunk-DAAFVOTL.cjs +0 -172
- package/dist/chunk-DAAFVOTL.cjs.map +0 -1
- package/dist/chunk-L4LAAA5Q.js.map +0 -1
- package/dist/chunk-MEAR2WHS.cjs.map +0 -1
- package/dist/chunk-OO4ZGDRO.js +0 -29
- package/dist/chunk-OO4ZGDRO.js.map +0 -1
- package/dist/chunk-OPSZNW3I.js.map +0 -1
- package/dist/chunk-QLWBBCI5.cjs.map +0 -1
- package/dist/chunk-T2URJAX2.js +0 -169
- package/dist/chunk-T2URJAX2.js.map +0 -1
- package/dist/chunk-T7AJBKZP.cjs.map +0 -1
- package/dist/chunk-WU6PMH5L.cjs.map +0 -1
- package/dist/chunk-XDGDCO3I.cjs.map +0 -1
- package/dist/chunk-Y2B6WDU6.cjs +0 -169
- package/dist/chunk-Y2B6WDU6.cjs.map +0 -1
- package/dist/chunk-ZMFT5XOB.js.map +0 -1
- package/dist/chunk-ZXZJSTNV.js +0 -172
- package/dist/chunk-ZXZJSTNV.js.map +0 -1
- /package/dist/{chunk-FLIWL6V4.js.map → chunk-EINXNG5D.js.map} +0 -0
- /package/dist/{chunk-GL5X6VVC.js.map → chunk-T4XXYAYY.js.map} +0 -0
package/package.json
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
import Image from '../../embed-shims/next-image'
|
|
23
23
|
import { getFirstLastInitials } from '../../utils/format'
|
|
24
24
|
import { useProxiedImageUrl } from './hooks/use-proxied-image-url'
|
|
25
|
-
import type
|
|
25
|
+
import { useState, type ReactNode } from 'react'
|
|
26
26
|
|
|
27
27
|
export interface ChatMessageRowProps {
|
|
28
28
|
/** Display name (bold, top-left). */
|
|
@@ -47,8 +47,17 @@ export function ChatMessageRow({
|
|
|
47
47
|
body,
|
|
48
48
|
footer,
|
|
49
49
|
}: ChatMessageRowProps) {
|
|
50
|
+
// Avatars load directly from their (https) host — same as the rest of the app,
|
|
51
|
+
// so the browser disk-caches them (Google/etc. send `cache-control: max-age`).
|
|
52
|
+
// `useProxiedImageUrl` only rewrites http/relative URLs; https pass through.
|
|
50
53
|
const proxiedAvatar = useProxiedImageUrl(avatarUrl ?? '')
|
|
51
|
-
const
|
|
54
|
+
const resolvedAvatar = proxiedAvatar || avatarUrl || undefined
|
|
55
|
+
// Fall back to the initials box if the avatar load FAILS (transient CDN 429,
|
|
56
|
+
// ad-blocker, dead URL) instead of showing a broken image. Keyed on the URL
|
|
57
|
+
// (not a bool) so a later render with a DIFFERENT avatar re-attempts the load
|
|
58
|
+
// rather than inheriting a stale failure.
|
|
59
|
+
const [failedSrc, setFailedSrc] = useState<string | null>(null)
|
|
60
|
+
const src = resolvedAvatar && resolvedAvatar !== failedSrc ? resolvedAvatar : undefined
|
|
52
61
|
const hasBody = body.trim().length > 0
|
|
53
62
|
|
|
54
63
|
return (
|
|
@@ -63,6 +72,7 @@ export function ChatMessageRow({
|
|
|
63
72
|
className="w-8 h-8 md:w-10 md:h-10 rounded-lg object-cover flex-shrink-0"
|
|
64
73
|
width={40}
|
|
65
74
|
height={40}
|
|
75
|
+
onError={() => setFailedSrc(resolvedAvatar ?? null)}
|
|
66
76
|
/>
|
|
67
77
|
) : (
|
|
68
78
|
<div className="w-8 h-8 md:w-10 md:h-10 rounded-lg flex-shrink-0 flex items-center justify-center bg-ods-bg border border-ods-border text-[12px] font-medium text-ods-text-primary font-body">
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FaqDocumentPage — the full `/faqs` page with chrome, so embedders drop in
|
|
5
|
+
* ONE component instead of hand-assembling `PageShell` + `PageLayout` around the
|
|
6
|
+
* bare `<FaqSection>`. Mirrors `LegalDocumentPage` / `DevSectionPage`: the
|
|
7
|
+
* page-level layout lives in the lib, the host passes only config + a back button.
|
|
8
|
+
*
|
|
9
|
+
* `<FaqSection heading={null}>` lets `PageLayout`'s `TitleBlock` own the heading +
|
|
10
|
+
* back button; it self-fetches `${apiBaseUrl}/api/faqs` via the authed
|
|
11
|
+
* `useSelfFetch`, and renders nothing on a fetch error or zero FAQs.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { PageShell, PageLayout } from '../ui';
|
|
15
|
+
import { useRouter } from '../../embed-shims/next-navigation';
|
|
16
|
+
import { FaqSection } from './faq-section';
|
|
17
|
+
|
|
18
|
+
export interface FaqDocumentPageProps {
|
|
19
|
+
/** Page title (PageLayout TitleBlock). Default "FAQs". */
|
|
20
|
+
title?: string;
|
|
21
|
+
/** Subtitle under the title. */
|
|
22
|
+
subtitle?: string;
|
|
23
|
+
/** Back-button config — same pattern as `DevSectionPage` / `LegalDocumentPage`.
|
|
24
|
+
* Pass `false` to hide. Default `{ label: 'Back to home', href: '/' }`. */
|
|
25
|
+
backButton?: { label?: string; href?: string } | false;
|
|
26
|
+
/** Base URL `FaqSection` appends `/api/faqs` to (reverse-proxy embedders). */
|
|
27
|
+
apiBaseUrl?: string;
|
|
28
|
+
/** Optional entity scoping forwarded to `FaqSection`. */
|
|
29
|
+
entityType?: string;
|
|
30
|
+
entityId?: number | string;
|
|
31
|
+
/** Minimum FAQ count before the section renders (forwarded to `FaqSection`). */
|
|
32
|
+
minResults?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function FaqDocumentPage({
|
|
36
|
+
title = 'FAQs',
|
|
37
|
+
subtitle,
|
|
38
|
+
backButton,
|
|
39
|
+
apiBaseUrl,
|
|
40
|
+
entityType,
|
|
41
|
+
entityId,
|
|
42
|
+
minResults,
|
|
43
|
+
}: FaqDocumentPageProps) {
|
|
44
|
+
const router = useRouter();
|
|
45
|
+
|
|
46
|
+
// Back-button config — mirrors LegalDocumentPage/DevSectionPage. Hide entirely
|
|
47
|
+
// when the caller passes `false` (embed-mode where the host owns nav chrome).
|
|
48
|
+
const backCfg =
|
|
49
|
+
backButton === false
|
|
50
|
+
? undefined
|
|
51
|
+
: {
|
|
52
|
+
label: backButton?.label ?? 'Back to home',
|
|
53
|
+
onClick: () => router.push(backButton?.href ?? '/'),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<PageShell>
|
|
58
|
+
<PageLayout title={title} subtitle={subtitle} backButton={backCfg}>
|
|
59
|
+
<FaqSection
|
|
60
|
+
heading={null}
|
|
61
|
+
apiBaseUrl={apiBaseUrl}
|
|
62
|
+
entityType={entityType}
|
|
63
|
+
entityId={entityId}
|
|
64
|
+
minResults={minResults}
|
|
65
|
+
/>
|
|
66
|
+
</PageLayout>
|
|
67
|
+
</PageShell>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -10,3 +10,4 @@
|
|
|
10
10
|
// (Server-safe subpath is wired in tsup.config.ts under the server/universal
|
|
11
11
|
// block and exposed via "./components/faq/json-ld" in package.json#exports.)
|
|
12
12
|
export { FaqSection, type FaqSectionProps } from './faq-section'
|
|
13
|
+
export { FaqDocumentPage, type FaqDocumentPageProps } from './faq-document-page'
|
|
@@ -1,30 +1,41 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { cn } from '../../utils/cn';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Unified page layout components.
|
|
5
6
|
*
|
|
6
|
-
* PageShell:
|
|
7
|
-
*
|
|
7
|
+
* PageShell: full-width <main> for list pages, dashboards, and other wide
|
|
8
|
+
* layouts (max-w-[1920px]) — bg + min-height + centered, max-width content box.
|
|
9
|
+
* ArticleDetailLayout: the same shell constrained to a readable article width
|
|
10
|
+
* (max-w-[1280px]) — blog posts, release notes, case studies, etc.
|
|
11
|
+
* Child components render content only; no layout wrappers needed.
|
|
8
12
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
13
|
+
* Outer padding is driven by CSS custom properties (see
|
|
14
|
+
* `styles/ods-page-shell.css`), so an embedder matches its host grid by setting
|
|
15
|
+
* the `--page-shell-*` vars on ANY ancestor — no prop-threading through the lib
|
|
16
|
+
* wrappers (DevSectionPage / LegalDocumentPage / ReleaseDetailPage / …), no
|
|
17
|
+
* imperative global, no context. The cascade scopes each override to its own
|
|
18
|
+
* subtree. Per-instance escape hatch: `contentClassName` (Tailwind padding
|
|
19
|
+
* utilities there win over the class defaults).
|
|
11
20
|
*/
|
|
12
21
|
|
|
13
22
|
interface LayoutProps {
|
|
14
23
|
children: React.ReactNode;
|
|
15
24
|
/** JSON-LD schema script elements (breadcrumbs, article schema, etc.) */
|
|
16
25
|
schemas?: React.ReactNode;
|
|
26
|
+
/** Per-instance class override on the content box (wins over the var defaults). */
|
|
27
|
+
contentClassName?: string;
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
/**
|
|
20
31
|
* Full-width page shell for list pages, dashboards, and other wide layouts.
|
|
21
32
|
* Max width: 1920px.
|
|
22
33
|
*/
|
|
23
|
-
export function PageShell({ children, schemas }: LayoutProps) {
|
|
34
|
+
export function PageShell({ children, schemas, contentClassName }: LayoutProps) {
|
|
24
35
|
return (
|
|
25
36
|
<main className="bg-ods-bg min-h-screen">
|
|
26
37
|
{schemas}
|
|
27
|
-
<div className=
|
|
38
|
+
<div className={cn('page-shell-content max-w-[1920px] mx-auto', contentClassName)}>
|
|
28
39
|
{children}
|
|
29
40
|
</div>
|
|
30
41
|
</main>
|
|
@@ -32,15 +43,17 @@ export function PageShell({ children, schemas }: LayoutProps) {
|
|
|
32
43
|
}
|
|
33
44
|
|
|
34
45
|
/**
|
|
35
|
-
* Constrained layout for article/detail pages
|
|
36
|
-
*
|
|
37
|
-
*
|
|
46
|
+
* Constrained layout for article/detail pages (max-w-[1280px]) — readable
|
|
47
|
+
* content width for blog posts, release notes, case studies, investor updates.
|
|
48
|
+
* Fixed hub padding (`px-6 md:px-20 py-6 md:py-10`); pass `contentClassName` to
|
|
49
|
+
* override per instance. (PageShell's `--page-shell-*` var system does NOT apply
|
|
50
|
+
* here — this layout keeps its own fixed spacing.)
|
|
38
51
|
*/
|
|
39
|
-
export function ArticleDetailLayout({ children, schemas }: LayoutProps) {
|
|
52
|
+
export function ArticleDetailLayout({ children, schemas, contentClassName }: LayoutProps) {
|
|
40
53
|
return (
|
|
41
54
|
<main className="bg-ods-bg min-h-screen">
|
|
42
55
|
{schemas}
|
|
43
|
-
<div className=
|
|
56
|
+
<div className={cn('max-w-[1280px] mx-auto px-6 md:px-20 py-6 md:py-10', contentClassName)}>
|
|
44
57
|
{children}
|
|
45
58
|
</div>
|
|
46
59
|
</main>
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { type ComponentType, type ReactNode } from 'react'
|
|
19
|
-
import {
|
|
19
|
+
import { PageLayout } from '../layout/page-layout'
|
|
20
20
|
|
|
21
|
-
import {
|
|
21
|
+
import { useRouter } from '../../embed-shims'
|
|
22
22
|
// PageShell (max-w-[1920px]) — the guide detail content must share the SAME
|
|
23
23
|
// container sizing as the related-content/FAQ rail the host page renders
|
|
24
24
|
// below it (which uses the wide shell). ArticleDetailLayout (1280px) made
|
|
@@ -91,6 +91,7 @@ export function OnboardingGuideDetailView({
|
|
|
91
91
|
}: OnboardingGuideDetailViewProps) {
|
|
92
92
|
const resolvedBackHref = backHref ?? basePath
|
|
93
93
|
const runtime = useChatRuntime()
|
|
94
|
+
const router = useRouter()
|
|
94
95
|
|
|
95
96
|
// Controlled (hub SSR `initialData`) OR self-fetch by slug (config-only embed).
|
|
96
97
|
const url = initialData ? null : slug && guideEndpoint ? guideEndpoint(slug) : null
|
|
@@ -104,13 +105,26 @@ export function OnboardingGuideDetailView({
|
|
|
104
105
|
})
|
|
105
106
|
|
|
106
107
|
if (error || (!guide && !isLoading)) {
|
|
107
|
-
return
|
|
108
|
+
return (
|
|
109
|
+
<PageShell>
|
|
110
|
+
<LoadError message="Guide not found." onRetry={reload} />
|
|
111
|
+
</PageShell>
|
|
112
|
+
)
|
|
108
113
|
}
|
|
109
114
|
if (!guide) {
|
|
110
115
|
// Skeleton (not a bare "Loading…") for parity with every other shared view —
|
|
111
116
|
// catalog, roadmap, releases all render a skeleton on first load, so the detail
|
|
112
|
-
// page shouldn't flash text then content.
|
|
113
|
-
|
|
117
|
+
// page shouldn't flash text then content. `bare` + `PageShell` so the loading
|
|
118
|
+
// state matches the loaded page's full width / padding / min-height.
|
|
119
|
+
return (
|
|
120
|
+
<PageShell>
|
|
121
|
+
{/* Match the loaded page's top offset (TitleBlock's
|
|
122
|
+
`pt-[var(--spacing-system-l)]`) so content doesn't jump on load. */}
|
|
123
|
+
<div className="pt-[var(--spacing-system-l)]">
|
|
124
|
+
<DetailPageSkeleton bare showImageGallery={false} />
|
|
125
|
+
</div>
|
|
126
|
+
</PageShell>
|
|
127
|
+
)
|
|
114
128
|
}
|
|
115
129
|
|
|
116
130
|
const captionsUrl = getCaptionsUrl('onboarding_guide', guide.id, guide.srt_content)
|
|
@@ -135,16 +149,7 @@ export function OnboardingGuideDetailView({
|
|
|
135
149
|
|
|
136
150
|
return (
|
|
137
151
|
<PageShell>
|
|
138
|
-
<
|
|
139
|
-
{/* Back link */}
|
|
140
|
-
<Link
|
|
141
|
-
href={resolvedBackHref}
|
|
142
|
-
className="inline-flex items-center gap-2 text-ods-text-secondary hover:text-ods-accent transition-colors"
|
|
143
|
-
>
|
|
144
|
-
<ArrowLeft className="h-4 w-4" />
|
|
145
|
-
<span className="text-h5">{backLabel}</span>
|
|
146
|
-
</Link>
|
|
147
|
-
|
|
152
|
+
<PageLayout backButton={{ label: backLabel, onClick: () => router.push(resolvedBackHref) }}>
|
|
148
153
|
<h1 className="text-h1 tracking-[-1.12px] text-ods-text-primary">{guide.title}</h1>
|
|
149
154
|
|
|
150
155
|
{/* Tags — flat onboarding_guide_tags[] from entity_tags. */}
|
|
@@ -225,7 +230,7 @@ export function OnboardingGuideDetailView({
|
|
|
225
230
|
</ul>
|
|
226
231
|
</div>
|
|
227
232
|
)}
|
|
228
|
-
</
|
|
233
|
+
</PageLayout>
|
|
229
234
|
</PageShell>
|
|
230
235
|
)
|
|
231
236
|
}
|
|
@@ -35,6 +35,7 @@ import { DeliveryTable } from './delivery-table';
|
|
|
35
35
|
import { EmptyState } from '../../empty-state';
|
|
36
36
|
import { LoadError } from '../../ui/error-state';
|
|
37
37
|
import { DEV_SECTION_PARAM_KEYS } from '../../../utils/dev-sections/dev-section-param-keys';
|
|
38
|
+
import { contentFetch } from '../../../utils/embed-content-fetch';
|
|
38
39
|
|
|
39
40
|
const DEFAULT_COMPLETED_ENDPOINT = '/api/delivery/completed';
|
|
40
41
|
const DEFAULT_IN_PROGRESS_ENDPOINT = '/api/delivery/in-progress';
|
|
@@ -97,8 +98,8 @@ export function DeliveryLists({
|
|
|
97
98
|
|
|
98
99
|
// Fetch completed and in-progress tasks separately with filters
|
|
99
100
|
const [completedResponse, inProgressResponse] = await Promise.all([
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
contentFetch(`${completedApiEndpoint}${queryParam}`),
|
|
102
|
+
contentFetch(`${inProgressApiEndpoint}${queryParam}`),
|
|
102
103
|
]);
|
|
103
104
|
|
|
104
105
|
if (!completedResponse.ok || !inProgressResponse.ok) {
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { PageLayout } from '../layout/page-layout';
|
|
4
4
|
|
|
5
5
|
export interface DetailPageSkeletonProps {
|
|
6
6
|
metadataColumns?: number; // Number of metadata grid columns (default 4)
|
|
7
7
|
showImageGallery?: boolean; // Show horizontal image gallery (default true)
|
|
8
|
+
/** Render only the skeleton blocks, WITHOUT the self-contained page wrapper
|
|
9
|
+
* — the caller supplies its own (e.g. `<PageShell>`) so the loading state
|
|
10
|
+
* matches the loaded page's width, padding, and min-height. Default false
|
|
11
|
+
* (self-contained `<PageLayout>` at the hub article width). */
|
|
12
|
+
bare?: boolean;
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
export function DetailPageSkeleton({
|
|
11
16
|
metadataColumns = 4,
|
|
12
|
-
showImageGallery = true
|
|
17
|
+
showImageGallery = true,
|
|
18
|
+
bare = false
|
|
13
19
|
}: DetailPageSkeletonProps = {}) {
|
|
14
|
-
|
|
15
|
-
<PageContainer
|
|
16
|
-
as="article"
|
|
17
|
-
backgroundClassName="bg-ods-bg"
|
|
18
|
-
contentPadding="py-6 md:py-10 px-6 md:px-20"
|
|
19
|
-
maxWidth="max-w-[1280px]"
|
|
20
|
-
>
|
|
20
|
+
const content = (
|
|
21
21
|
<div className="space-y-6 md:space-y-10 animate-pulse">
|
|
22
22
|
{/* Title Block */}
|
|
23
23
|
<div className="flex flex-col gap-6 w-full">
|
|
@@ -67,6 +67,14 @@ export function DetailPageSkeleton({
|
|
|
67
67
|
</div>
|
|
68
68
|
))}
|
|
69
69
|
</div>
|
|
70
|
-
|
|
70
|
+
);
|
|
71
|
+
if (bare) return content;
|
|
72
|
+
return (
|
|
73
|
+
<PageLayout
|
|
74
|
+
showHeader={false}
|
|
75
|
+
className="bg-ods-bg max-w-[1280px] mx-auto py-6 md:py-10 px-6 md:px-20"
|
|
76
|
+
>
|
|
77
|
+
{content}
|
|
78
|
+
</PageLayout>
|
|
71
79
|
);
|
|
72
80
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { useState, useEffect, useCallback } from 'react';
|
|
17
|
+
import { contentFetch } from '../../../utils/embed-content-fetch';
|
|
17
18
|
|
|
18
19
|
export interface LegalDocument {
|
|
19
20
|
title: string;
|
|
@@ -68,7 +69,7 @@ export function useLegalDocs(
|
|
|
68
69
|
setIsLoading(true);
|
|
69
70
|
setError(null);
|
|
70
71
|
|
|
71
|
-
const response = await
|
|
72
|
+
const response = await contentFetch(effectiveEndpoint);
|
|
72
73
|
|
|
73
74
|
if (!response.ok) {
|
|
74
75
|
throw new Error(
|
|
@@ -8,7 +8,7 @@ import { Card, CardContent } from '../../ui/card';
|
|
|
8
8
|
// renders below this view (was ArticleDetailLayout, 1280px — narrower than
|
|
9
9
|
// the rail, see hub detail-container alignment decision 2026-06-10).
|
|
10
10
|
import { PageShell } from '../../layout/article-detail-layout';
|
|
11
|
-
import {
|
|
11
|
+
import { PageLayout } from '../../layout/page-layout';
|
|
12
12
|
import { ReleaseChangelogSection } from '../../ui/release-changelog-section';
|
|
13
13
|
import { EntityTagBadges } from '../../features/entity-tag-badges';
|
|
14
14
|
import { EntityMetadataAuthorCell } from '../../chat/entity-cards/entity-author-card';
|
|
@@ -17,6 +17,7 @@ import { MediaGalleryStrip } from '../media-gallery-strip';
|
|
|
17
17
|
import { GitHubIcon } from '../../icons/github-icon';
|
|
18
18
|
import { AlertTriangle, ExternalLink, BookMarked, Sparkles, TrendingUp, Wrench } from 'lucide-react';
|
|
19
19
|
import { formatReleaseDate } from '../../../utils/date-formatters';
|
|
20
|
+
import { contentFetch } from '../../../utils/embed-content-fetch';
|
|
20
21
|
import { Video } from '../../features/video';
|
|
21
22
|
import { DetailPageSkeleton } from '../detail-page-skeleton';
|
|
22
23
|
import type { ChangelogEntry } from '../../../types/product-release';
|
|
@@ -158,7 +159,7 @@ export function ReleaseDetailPage({
|
|
|
158
159
|
if (roadmapTasksData && roadmapTasksData.length > 0 && RoadmapSection) {
|
|
159
160
|
setRoadmapLoading(true);
|
|
160
161
|
const roadmapIds = roadmapTasksData.map(t => t.clickup_task_id).join(',');
|
|
161
|
-
const roadmapResponse = await
|
|
162
|
+
const roadmapResponse = await contentFetch(`${roadmapApiEndpoint}?task_ids=${roadmapIds}`);
|
|
162
163
|
const roadmapData = await roadmapResponse.json();
|
|
163
164
|
setRoadmapTasks(roadmapData.items || []);
|
|
164
165
|
setRoadmapLoading(false);
|
|
@@ -169,7 +170,7 @@ export function ReleaseDetailPage({
|
|
|
169
170
|
if (deliveryTasksData && deliveryTasksData.length > 0 && DeliverySection) {
|
|
170
171
|
setDeliveryLoading(true);
|
|
171
172
|
const deliveryIds = deliveryTasksData.map(t => t.clickup_task_id).join(',');
|
|
172
|
-
const deliveryResponse = await
|
|
173
|
+
const deliveryResponse = await contentFetch(`${deliveryApiEndpoint}?task_ids=${deliveryIds}`);
|
|
173
174
|
const deliveryResponseData = await deliveryResponse.json();
|
|
174
175
|
setDeliveryData(deliveryResponseData);
|
|
175
176
|
setDeliveryLoading(false);
|
|
@@ -186,15 +187,27 @@ export function ReleaseDetailPage({
|
|
|
186
187
|
|
|
187
188
|
// Don't show loading skeleton if we have initialData
|
|
188
189
|
if (!initialData && isLoading) {
|
|
189
|
-
|
|
190
|
+
// `bare` + `PageShell` so the loading state matches the loaded page's full
|
|
191
|
+
// width / padding / min-height (the wrapper supplies the page chrome).
|
|
192
|
+
return (
|
|
193
|
+
<PageShell>
|
|
194
|
+
{/* Match the loaded page's top offset (TitleBlock's
|
|
195
|
+
`pt-[var(--spacing-system-l)]`) so content doesn't jump on load. */}
|
|
196
|
+
<div className="pt-[var(--spacing-system-l)]">
|
|
197
|
+
<DetailPageSkeleton bare metadataColumns={4} showImageGallery={true} />
|
|
198
|
+
</div>
|
|
199
|
+
</PageShell>
|
|
200
|
+
);
|
|
190
201
|
}
|
|
191
202
|
|
|
192
203
|
if (error || !release) {
|
|
193
204
|
return (
|
|
194
|
-
<
|
|
195
|
-
<
|
|
196
|
-
|
|
197
|
-
|
|
205
|
+
<PageShell>
|
|
206
|
+
<div className="text-center py-16">
|
|
207
|
+
<h1 className="text-4xl font-bold text-ods-text-primary mb-4">Release Not Found</h1>
|
|
208
|
+
<p className="text-xl text-ods-text-secondary">The release you're looking for doesn't exist.</p>
|
|
209
|
+
</div>
|
|
210
|
+
</PageShell>
|
|
198
211
|
);
|
|
199
212
|
}
|
|
200
213
|
|
|
@@ -228,15 +241,11 @@ export function ReleaseDetailPage({
|
|
|
228
241
|
|
|
229
242
|
return (
|
|
230
243
|
<PageShell>
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
onClick={() => router.push(backHref)}
|
|
237
|
-
className="hidden md:inline-flex mb-4"
|
|
238
|
-
/>
|
|
239
|
-
)}
|
|
244
|
+
<PageLayout
|
|
245
|
+
backButton={
|
|
246
|
+
showBackButton ? { label: backLabel, onClick: () => router.push(backHref) } : undefined
|
|
247
|
+
}
|
|
248
|
+
>
|
|
240
249
|
<div className="space-y-6 md:space-y-8">
|
|
241
250
|
{/* Title Block */}
|
|
242
251
|
<div className="flex flex-col md:flex-row md:items-end gap-4 w-full">
|
|
@@ -557,6 +566,7 @@ export function ReleaseDetailPage({
|
|
|
557
566
|
</div>
|
|
558
567
|
)}
|
|
559
568
|
</div>
|
|
569
|
+
</PageLayout>
|
|
560
570
|
</PageShell>
|
|
561
571
|
);
|
|
562
572
|
}
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
AccordionContent,
|
|
34
34
|
} from '../../ui';
|
|
35
35
|
import { cn } from '../../../utils/cn';
|
|
36
|
+
import { contentFetch } from '../../../utils/embed-content-fetch';
|
|
36
37
|
import type { RoadmapItem } from '../../chat/types/entities/roadmap-item';
|
|
37
38
|
|
|
38
39
|
const DEFAULT_BUILD_REFRESH_URL = (taskId: string) => `/api/roadmap/${taskId}`;
|
|
@@ -165,7 +166,7 @@ export function RoadmapGrid({
|
|
|
165
166
|
try {
|
|
166
167
|
const result = await toggleVote(taskId, voteType);
|
|
167
168
|
if (result.success) {
|
|
168
|
-
const response = await
|
|
169
|
+
const response = await contentFetch(buildRefreshUrl(taskId));
|
|
169
170
|
if (response.ok) {
|
|
170
171
|
const data = await response.json();
|
|
171
172
|
if (data.item && onItemUpdate) onItemUpdate(data.item);
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { useState, useEffect, useCallback } from 'react';
|
|
19
|
+
import { contentFetch } from '../../../utils/embed-content-fetch';
|
|
19
20
|
|
|
20
21
|
export type VoteType = 'up' | 'down' | null;
|
|
21
22
|
|
|
@@ -100,7 +101,7 @@ export function useRoadmapVoting(options: UseRoadmapVotingOptions = {}) {
|
|
|
100
101
|
// User clicked different vote - set it. If they had an opposite
|
|
101
102
|
// vote, remove that first so the server totals stay consistent.
|
|
102
103
|
if (currentVote) {
|
|
103
|
-
await
|
|
104
|
+
await contentFetch(voteApiEndpoint, {
|
|
104
105
|
method: 'POST',
|
|
105
106
|
headers: { 'Content-Type': 'application/json' },
|
|
106
107
|
body: JSON.stringify({
|
|
@@ -122,7 +123,7 @@ export function useRoadmapVoting(options: UseRoadmapVotingOptions = {}) {
|
|
|
122
123
|
}));
|
|
123
124
|
|
|
124
125
|
try {
|
|
125
|
-
const response = await
|
|
126
|
+
const response = await contentFetch(voteApiEndpoint, {
|
|
126
127
|
method: 'POST',
|
|
127
128
|
headers: { 'Content-Type': 'application/json' },
|
|
128
129
|
body: JSON.stringify({ taskId, voteType, action }),
|
|
@@ -47,9 +47,14 @@ export interface HelpCenterListProps {
|
|
|
47
47
|
/** Toast override (test-friendly). Defaults to the lib's shared
|
|
48
48
|
* toast singleton. */
|
|
49
49
|
toast?: typeof defaultToast
|
|
50
|
+
/** Back-button forwarded to the internal `DevSectionPage` chrome (same shape
|
|
51
|
+
* as `DevSectionPage` / `LegalDocumentPage`: `{ label?, href? }`, or `false`
|
|
52
|
+
* to hide). Omit ⇒ `DevSectionPage`'s default (`Back to home` → `/`), which
|
|
53
|
+
* embedders whose home isn't `/` MUST override. */
|
|
54
|
+
backButton?: { label?: string; href?: string } | false
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
export function HelpCenterList({ toast = defaultToast }: HelpCenterListProps = {}) {
|
|
57
|
+
export function HelpCenterList({ toast = defaultToast, backButton }: HelpCenterListProps = {}) {
|
|
53
58
|
const identity = useChatIdentity()
|
|
54
59
|
const searchParams = useSearchParams()
|
|
55
60
|
const router = useRouter()
|
|
@@ -77,14 +82,14 @@ export function HelpCenterList({ toast = defaultToast }: HelpCenterListProps = {
|
|
|
77
82
|
// mounts in the `preControls` slot.
|
|
78
83
|
if (identity.isLoading) {
|
|
79
84
|
return (
|
|
80
|
-
<DevSectionPage sectionKey="tickets" preControls={<HelpCenterCreateFormSkeleton />}>
|
|
85
|
+
<DevSectionPage sectionKey="tickets" backButton={backButton} preControls={<HelpCenterCreateFormSkeleton />}>
|
|
81
86
|
<DevCardRowSkeletonList />
|
|
82
87
|
</DevSectionPage>
|
|
83
88
|
)
|
|
84
89
|
}
|
|
85
90
|
if (identity.authTier === 'anon' || !identity.user?.email) {
|
|
86
91
|
return (
|
|
87
|
-
<DevSectionPage sectionKey="tickets">
|
|
92
|
+
<DevSectionPage sectionKey="tickets" backButton={backButton}>
|
|
88
93
|
<EmptyState
|
|
89
94
|
type="generic"
|
|
90
95
|
title="Sign in to manage tickets"
|
|
@@ -119,6 +124,7 @@ export function HelpCenterList({ toast = defaultToast }: HelpCenterListProps = {
|
|
|
119
124
|
toast={toast}
|
|
120
125
|
sessionName={sessionName}
|
|
121
126
|
sessionEmail={sessionEmail}
|
|
127
|
+
backButton={backButton}
|
|
122
128
|
/>
|
|
123
129
|
)
|
|
124
130
|
}
|
|
@@ -135,6 +141,7 @@ interface AuthedProps {
|
|
|
135
141
|
toast: typeof defaultToast
|
|
136
142
|
sessionName: string
|
|
137
143
|
sessionEmail: string
|
|
144
|
+
backButton?: { label?: string; href?: string } | false
|
|
138
145
|
}
|
|
139
146
|
|
|
140
147
|
function HelpCenterListAuthed({
|
|
@@ -148,6 +155,7 @@ function HelpCenterListAuthed({
|
|
|
148
155
|
toast,
|
|
149
156
|
sessionName,
|
|
150
157
|
sessionEmail,
|
|
158
|
+
backButton,
|
|
151
159
|
}: AuthedProps) {
|
|
152
160
|
const queryClient = useQueryClient()
|
|
153
161
|
const [optimisticTickets, setOptimisticTickets] = useState<OptimisticTicket[]>([])
|
|
@@ -365,7 +373,7 @@ function HelpCenterListAuthed({
|
|
|
365
373
|
)
|
|
366
374
|
|
|
367
375
|
return (
|
|
368
|
-
<DevSectionPage sectionKey="tickets" preControls={form}>
|
|
376
|
+
<DevSectionPage sectionKey="tickets" backButton={backButton} preControls={form}>
|
|
369
377
|
{body}
|
|
370
378
|
</DevSectionPage>
|
|
371
379
|
)
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
33
33
|
import { useQueryClient } from '@tanstack/react-query'
|
|
34
|
+
import { useRequiredChatRuntime } from '../../../contexts/chat-runtime-context'
|
|
34
35
|
import { embedAuthedFetch } from '../../../utils/embed-authed-fetch'
|
|
35
36
|
import type { ChatAttachment } from '../../chat/utils/chat-attachment-markdown'
|
|
36
37
|
import {
|
|
@@ -149,6 +150,10 @@ export interface UseTicketActionsReturn {
|
|
|
149
150
|
|
|
150
151
|
export function useTicketActions(options: UseTicketActionsOptions): UseTicketActionsReturn {
|
|
151
152
|
const queryClient = useQueryClient()
|
|
153
|
+
// Endpoint from the runtime config (like every other endpoint); falls back to
|
|
154
|
+
// the bare hub path when unconfigured.
|
|
155
|
+
const ticketActionEndpoint =
|
|
156
|
+
useRequiredChatRuntime().endpoints.ticketActionUrl ?? TICKET_ACTION_ENDPOINT
|
|
152
157
|
const { prependOptimistic, removeOptimistic, removeTicketFromCache, toast, onSupportSystemDown } = options
|
|
153
158
|
|
|
154
159
|
// Form-level single-flight uses BOTH a ref (for synchronous guarding
|
|
@@ -234,7 +239,7 @@ export function useTicketActions(options: UseTicketActionsOptions): UseTicketAct
|
|
|
234
239
|
|
|
235
240
|
const executeTicketAction = useCallback(
|
|
236
241
|
async (toolName: ToolName, args: Record<string, unknown>): Promise<TicketActionResponse> => {
|
|
237
|
-
const res = await embedAuthedFetch(
|
|
242
|
+
const res = await embedAuthedFetch(ticketActionEndpoint, {
|
|
238
243
|
method: 'POST',
|
|
239
244
|
body: JSON.stringify({ tool_name: toolName, args }),
|
|
240
245
|
})
|
|
@@ -248,7 +253,7 @@ export function useTicketActions(options: UseTicketActionsOptions): UseTicketAct
|
|
|
248
253
|
}
|
|
249
254
|
return body
|
|
250
255
|
},
|
|
251
|
-
[],
|
|
256
|
+
[ticketActionEndpoint],
|
|
252
257
|
)
|
|
253
258
|
|
|
254
259
|
// Mirror-sync watcher — backoff refetches when the post-create mirror
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { useQuery } from '@tanstack/react-query'
|
|
18
|
+
import { useRequiredChatRuntime } from '../../../contexts/chat-runtime-context'
|
|
18
19
|
import { embedAuthedFetch } from '../../../utils/embed-authed-fetch'
|
|
19
20
|
import { useChatIdentity } from '../../chat/hooks/use-chat-identity'
|
|
20
21
|
|
|
@@ -87,6 +88,10 @@ export function useTicketEngagements(
|
|
|
87
88
|
): UseTicketEngagementsReturn {
|
|
88
89
|
const identity = useChatIdentity()
|
|
89
90
|
const identityKey = identity.user?.email ?? 'anon'
|
|
91
|
+
// Endpoint from the runtime config (like every other endpoint); falls back to
|
|
92
|
+
// the bare hub path when unconfigured.
|
|
93
|
+
const listEngagementsEndpoint =
|
|
94
|
+
useRequiredChatRuntime().endpoints.listEngagementsUrl ?? LIST_ENGAGEMENTS_ENDPOINT
|
|
90
95
|
|
|
91
96
|
// "Will this ticket fetch its timeline once identity is ready?" — i.e. it's a
|
|
92
97
|
// real, non-optimistic ticket the caller enabled. INDEPENDENT of whether
|
|
@@ -116,7 +121,7 @@ export function useTicketEngagements(
|
|
|
116
121
|
// so polling pauses on a hidden tab.
|
|
117
122
|
refetchInterval,
|
|
118
123
|
queryFn: async (): Promise<TicketEngagement[]> => {
|
|
119
|
-
const response = await embedAuthedFetch(
|
|
124
|
+
const response = await embedAuthedFetch(listEngagementsEndpoint, {
|
|
120
125
|
method: 'POST',
|
|
121
126
|
body: JSON.stringify({ ticket_id: externalTicketId }),
|
|
122
127
|
})
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { useQuery } from '@tanstack/react-query'
|
|
23
|
+
import { useRequiredChatRuntime } from '../../../contexts/chat-runtime-context'
|
|
23
24
|
import { embedAuthedFetch } from '../../../utils/embed-authed-fetch'
|
|
24
25
|
import type { TicketData } from '../types'
|
|
25
26
|
|
|
@@ -91,6 +92,11 @@ export interface UseTicketsListReturn {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
export function useTicketsList(filters: UseTicketsListFilters): UseTicketsListReturn {
|
|
95
|
+
// Endpoint from the runtime config (like every other endpoint) so embedders
|
|
96
|
+
// behind a reverse proxy route it through `/content/*`; falls back to the bare
|
|
97
|
+
// hub path when unconfigured.
|
|
98
|
+
const findTicketEndpoint =
|
|
99
|
+
useRequiredChatRuntime().endpoints.findTicketUrl ?? FIND_TICKET_ENDPOINT
|
|
94
100
|
const customerEmail = filters.customerEmail
|
|
95
101
|
const search = (filters.search ?? '').trim()
|
|
96
102
|
const status = (filters.status ?? '').trim().toLowerCase()
|
|
@@ -135,7 +141,7 @@ export function useTicketsList(filters: UseTicketsListFilters): UseTicketsListRe
|
|
|
135
141
|
pageSize,
|
|
136
142
|
}
|
|
137
143
|
if (statusFilter) body.status = statusFilter
|
|
138
|
-
const response = await embedAuthedFetch(
|
|
144
|
+
const response = await embedAuthedFetch(findTicketEndpoint, {
|
|
139
145
|
method: 'POST',
|
|
140
146
|
body: JSON.stringify(body),
|
|
141
147
|
})
|