@flamingo-stack/openframe-frontend-core 0.0.691 → 0.0.692
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-LCENRN2N.cjs → chunk-5XPK2QGU.cjs} +3 -3
- package/dist/{chunk-LCENRN2N.cjs.map → chunk-5XPK2QGU.cjs.map} +1 -1
- package/dist/{chunk-SA4KQCNB.cjs → chunk-7BNWMOPM.cjs} +36 -36
- package/dist/{chunk-SA4KQCNB.cjs.map → chunk-7BNWMOPM.cjs.map} +1 -1
- package/dist/{chunk-6Z26ZMR4.js → chunk-A5NHAHV6.js} +2 -2
- package/dist/{chunk-Y3Q76RAA.cjs → chunk-ATREVNIN.cjs} +33 -33
- package/dist/{chunk-Y3Q76RAA.cjs.map → chunk-ATREVNIN.cjs.map} +1 -1
- package/dist/{chunk-FNMQ2J44.js → chunk-AVAZ6BD6.js} +2 -2
- package/dist/{chunk-DDWLF2JV.cjs → chunk-CG7OWW7X.cjs} +4 -4
- package/dist/{chunk-DDWLF2JV.cjs.map → chunk-CG7OWW7X.cjs.map} +1 -1
- package/dist/{chunk-KMW2PXBU.js → chunk-CKO6NKNE.js} +750 -212
- package/dist/chunk-CKO6NKNE.js.map +1 -0
- package/dist/{chunk-ZLYLHP64.cjs → chunk-CKUPANVU.cjs} +4 -4
- package/dist/{chunk-ZLYLHP64.cjs.map → chunk-CKUPANVU.cjs.map} +1 -1
- package/dist/{chunk-H54RDLVC.cjs → chunk-DKJDO7YM.cjs} +18 -18
- package/dist/{chunk-H54RDLVC.cjs.map → chunk-DKJDO7YM.cjs.map} +1 -1
- package/dist/{chunk-N4S7RBUM.cjs → chunk-DUURFYGS.cjs} +3 -3
- package/dist/{chunk-N4S7RBUM.cjs.map → chunk-DUURFYGS.cjs.map} +1 -1
- package/dist/{chunk-CWXRZVDI.cjs → chunk-EXWS2QSD.cjs} +588 -50
- package/dist/chunk-EXWS2QSD.cjs.map +1 -0
- package/dist/{chunk-WZYIAKIH.cjs → chunk-F6LXGP6S.cjs} +3 -3
- package/dist/{chunk-WZYIAKIH.cjs.map → chunk-F6LXGP6S.cjs.map} +1 -1
- package/dist/{chunk-4N2JOPAQ.cjs → chunk-FTK3S57R.cjs} +13 -13
- package/dist/{chunk-4N2JOPAQ.cjs.map → chunk-FTK3S57R.cjs.map} +1 -1
- package/dist/{chunk-MJI5GVT7.js → chunk-IQTQIBZ3.js} +4 -4
- package/dist/{chunk-BXE2RZEV.cjs → chunk-KDCIYRZV.cjs} +17 -17
- package/dist/{chunk-BXE2RZEV.cjs.map → chunk-KDCIYRZV.cjs.map} +1 -1
- package/dist/{chunk-ESFPY5FF.cjs → chunk-KQZMM3QJ.cjs} +51 -51
- package/dist/{chunk-ESFPY5FF.cjs.map → chunk-KQZMM3QJ.cjs.map} +1 -1
- package/dist/{chunk-Y7ZQZOE6.js → chunk-LYJJH4D6.js} +2 -2
- package/dist/{chunk-TSNGVWQO.js → chunk-LYQULSK4.js} +5 -5
- package/dist/{chunk-KVDLL4LU.js → chunk-MCNQKS6I.js} +2 -2
- package/dist/{chunk-DC3G4XGP.js → chunk-PKZGXRU2.js} +3 -3
- package/dist/{chunk-Y3BC7TOJ.cjs → chunk-Q25VYAEL.cjs} +17 -17
- package/dist/{chunk-Y3BC7TOJ.cjs.map → chunk-Q25VYAEL.cjs.map} +1 -1
- package/dist/{chunk-JSG7WZKV.js → chunk-RK47E3PP.js} +2 -2
- package/dist/{chunk-4HZXOQRM.js → chunk-RLNNV46K.js} +2 -2
- package/dist/{chunk-BXAGR3PO.cjs → chunk-RTKF5AYA.cjs} +3 -3
- package/dist/{chunk-BXAGR3PO.cjs.map → chunk-RTKF5AYA.cjs.map} +1 -1
- package/dist/{chunk-Z5FHXCXF.js → chunk-TEIHEFXP.js} +4 -4
- package/dist/{chunk-XBYF4SGD.js → chunk-TXFGG47A.js} +2 -2
- package/dist/{chunk-5N2ZSB42.cjs → chunk-WTYQCT4A.cjs} +10 -10
- package/dist/{chunk-5N2ZSB42.cjs.map → chunk-WTYQCT4A.cjs.map} +1 -1
- package/dist/{chunk-CMYDGVTZ.js → chunk-X5XFKI6N.js} +2 -2
- package/dist/{chunk-7WYWPELF.js → chunk-Z7QCNCBN.js} +5 -5
- package/dist/{chunk-DZMDG7PC.js → chunk-ZAV2F4VX.js} +2 -2
- package/dist/components/case-studies/index.cjs +11 -11
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +6 -6
- package/dist/components/docs/index.js +5 -5
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +34 -2
- package/dist/components/features/index.cjs.map +1 -1
- package/dist/components/features/index.d.ts +1 -0
- package/dist/components/features/index.d.ts.map +1 -1
- package/dist/components/features/index.js +33 -1
- package/dist/components/features/remote-session/index.d.ts +13 -0
- package/dist/components/features/remote-session/index.d.ts.map +1 -0
- package/dist/components/features/remote-session/remote-access-request-card.d.ts +19 -0
- package/dist/components/features/remote-session/remote-access-request-card.d.ts.map +1 -0
- package/dist/components/features/remote-session/remote-session-block.d.ts +41 -0
- package/dist/components/features/remote-session/remote-session-block.d.ts.map +1 -0
- package/dist/components/features/remote-session/remote-session-chat-panel.d.ts +23 -0
- package/dist/components/features/remote-session/remote-session-chat-panel.d.ts.map +1 -0
- package/dist/components/features/remote-session/remote-session-dialog.d.ts +15 -0
- package/dist/components/features/remote-session/remote-session-dialog.d.ts.map +1 -0
- package/dist/components/features/remote-session/remote-session-overlay.d.ts +12 -0
- package/dist/components/features/remote-session/remote-session-overlay.d.ts.map +1 -0
- package/dist/components/features/remote-session/remote-session-parts.d.ts +34 -0
- package/dist/components/features/remote-session/remote-session-parts.d.ts.map +1 -0
- package/dist/components/features/remote-session/types.d.ts +33 -0
- package/dist/components/features/remote-session/types.d.ts.map +1 -0
- package/dist/components/help-center-pages/index.cjs +49 -49
- package/dist/components/help-center-pages/index.js +13 -13
- package/dist/components/index.cjs +64 -32
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +44 -12
- package/dist/components/index.js.map +1 -1
- package/dist/components/meeting-scheduler/index.cjs +22 -22
- package/dist/components/meeting-scheduler/index.js +1 -1
- package/dist/components/navigation/index.cjs +5 -5
- package/dist/components/navigation/index.js +4 -4
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/tickets/index.cjs +6 -6
- package/dist/components/tickets/index.js +5 -5
- package/dist/components/ui/index.cjs +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/index.cjs +38 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +37 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/features/__tests__/remote-session.test.tsx +97 -0
- package/src/components/features/index.ts +1 -0
- package/src/components/features/remote-session/index.ts +36 -0
- package/src/components/features/remote-session/remote-access-request-card.tsx +102 -0
- package/src/components/features/remote-session/remote-session-block.tsx +182 -0
- package/src/components/features/remote-session/remote-session-chat-panel.tsx +178 -0
- package/src/components/features/remote-session/remote-session-dialog.tsx +77 -0
- package/src/components/features/remote-session/remote-session-overlay.tsx +33 -0
- package/src/components/features/remote-session/remote-session-parts.tsx +138 -0
- package/src/components/features/remote-session/types.ts +41 -0
- package/src/stories/RemoteSessionAccessRequest.stories.tsx +71 -0
- package/src/stories/RemoteSessionBlock.stories.tsx +164 -0
- package/src/stories/RemoteSessionDialog.stories.tsx +49 -0
- package/src/stories/RemoteSessionIndicator.stories.tsx +44 -0
- package/dist/chunk-CWXRZVDI.cjs.map +0 -1
- package/dist/chunk-KMW2PXBU.js.map +0 -1
- /package/dist/{chunk-6Z26ZMR4.js.map → chunk-A5NHAHV6.js.map} +0 -0
- /package/dist/{chunk-FNMQ2J44.js.map → chunk-AVAZ6BD6.js.map} +0 -0
- /package/dist/{chunk-MJI5GVT7.js.map → chunk-IQTQIBZ3.js.map} +0 -0
- /package/dist/{chunk-Y7ZQZOE6.js.map → chunk-LYJJH4D6.js.map} +0 -0
- /package/dist/{chunk-TSNGVWQO.js.map → chunk-LYQULSK4.js.map} +0 -0
- /package/dist/{chunk-KVDLL4LU.js.map → chunk-MCNQKS6I.js.map} +0 -0
- /package/dist/{chunk-DC3G4XGP.js.map → chunk-PKZGXRU2.js.map} +0 -0
- /package/dist/{chunk-JSG7WZKV.js.map → chunk-RK47E3PP.js.map} +0 -0
- /package/dist/{chunk-4HZXOQRM.js.map → chunk-RLNNV46K.js.map} +0 -0
- /package/dist/{chunk-Z5FHXCXF.js.map → chunk-TEIHEFXP.js.map} +0 -0
- /package/dist/{chunk-XBYF4SGD.js.map → chunk-TXFGG47A.js.map} +0 -0
- /package/dist/{chunk-CMYDGVTZ.js.map → chunk-X5XFKI6N.js.map} +0 -0
- /package/dist/{chunk-7WYWPELF.js.map → chunk-Z7QCNCBN.js.map} +0 -0
- /package/dist/{chunk-DZMDG7PC.js.map → chunk-ZAV2F4VX.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-BXE2RZEV.cjs","../src/components/onboarding-guides/onboarding-guides-catalog-view.tsx","../src/components/onboarding-guides/onboarding-guides-catalog-skeleton.tsx","../src/components/onboarding-guides/onboarding-guide-detail-view.tsx","../src/components/onboarding-guides/hooks/use-onboarding-guides.ts"],"names":["jsx","jsxs"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-BXE2RZEV.cjs","sourcesContent":[null,"'use client';\n\n/**\n * Public-facing catalog view for `/onboarding-guides`.\n *\n * Two data modes:\n * - **controlled** (hub SSR): pass `initialGuides` + `initialSections`\n * (server-fetched; section changes re-render the host with new props).\n * - **self-fetching** (config-only embed): omit the data props and pass\n * `guidesEndpoint` + `sectionsEndpoint` (the api routes). The view fetches\n * the guides (refetching on `?section=` change) + the section list itself —\n * no host data layer. (Plain fetch + useEffect, the DeliveryLists pattern.)\n *\n * The view renders ONLY its body + its OWN controls (the RAG `<DocSearchBar>` +\n * dynamic section pills — onboarding's controls are content-driven, unlike the\n * static search/filter the `DevSectionView` chrome ships). It does NOT render the\n * page chrome: the CALLER wraps it in `<DevSectionPage sectionKey=\"onboarding\">`\n * (hero + back button), exactly like the roadmap / releases / delivery pages — so\n * every dev-center surface composes the same way and the view stays embeddable in\n * tabbed contexts. Card hrefs flow through `runtime.composeContentUrl`.\n */\n\nimport { GraduationCap } from 'lucide-react';\nimport { useMemo, useTransition, type ReactNode } from 'react';\n\nimport { useChatRuntime } from '../../contexts/chat-runtime-context';\nimport { useRouter, useSearchParams } from '../../embed-shims';\nimport { useSelfFetch } from '../../hooks/use-self-fetch';\nimport { resolveContentHref } from '../../utils/content-href';\nimport { OnboardingGuideCard } from '../chat/entity-cards/onboarding-guide-card';\nimport type { OnboardingGuide } from '../chat/types/entities/onboarding-guide';\nimport { DocSearchBar, useDocSearch } from '../shared/doc-search';\nimport { LoadError } from '../ui/error-state';\nimport { FilterPillRow } from '../ui/filter-pill-row';\nimport { OnboardingGuidesCatalogSkeleton } from './onboarding-guides-catalog-skeleton';\n\ntype SectionSummary = { section: string; section_order: number; count: number };\n\nexport interface OnboardingGuidesCatalogViewProps {\n /** Controlled / SSR: server-fetched guides. Omit + pass `guidesEndpoint`\n * for the self-fetching config-only mode. */\n initialGuides?: OnboardingGuide[];\n initialSections?: SectionSummary[];\n initialSection?: string;\n /** Self-fetch: GET list endpoint (the api route). Appends `?section=`. */\n guidesEndpoint?: string;\n /** Self-fetch: GET section-summary endpoint (the api route). */\n sectionsEndpoint?: string;\n /** Optional per-row card renderer override. */\n renderCard?: (guide: OnboardingGuide) => ReactNode;\n /** Base path the catalog is mounted under (fallback href prefix + `?section=`\n * push target). Default `/onboarding-guides`. */\n basePath?: string;\n}\n\nexport function OnboardingGuidesCatalogView({\n initialGuides,\n initialSections,\n initialSection = '',\n guidesEndpoint,\n sectionsEndpoint,\n renderCard,\n basePath = '/onboarding-guides',\n}: OnboardingGuidesCatalogViewProps) {\n const router = useRouter();\n const searchParams = useSearchParams();\n const [isPending, startTransition] = useTransition();\n const runtime = useChatRuntime();\n\n // Self-fetch only when the host supplied NO data but DID give an endpoint.\n const selfFetch = initialGuides === undefined && guidesEndpoint !== undefined;\n // Active section: from the URL when self-fetching, from the prop (host RSC\n // already read the URL server-side) when controlled.\n const sectionValue = selfFetch ? (searchParams.get('section') ?? '') : initialSection;\n const activeSection = sectionValue || 'all';\n\n // Self-fetch the guides (refetched whenever `?section=` changes — it's folded\n // into the url) + the section summaries. Controlled mode passes `null` urls so\n // the hook never fetches. The guides fetch carries the error/retry affordance.\n const guidesQs = sectionValue ? `?section=${encodeURIComponent(sectionValue)}` : '';\n const guidesRes = useSelfFetch<{ data?: OnboardingGuide[] }>(\n selfFetch && guidesEndpoint ? `${guidesEndpoint}${guidesQs}` : null,\n );\n const sectionsRes = useSelfFetch<SectionSummary[]>(selfFetch && sectionsEndpoint ? sectionsEndpoint : null);\n\n // Invariant: `guidesRes.data` is `null` until the first fetch resolves, then\n // `{ data: [...] }`. The `?.data ?? []` chain collapses never-fetched (null) and\n // legitimately-empty ([]) to the same `[]` — so the first-load skeleton gate below\n // keys on the OUTER `guidesRes.data == null` to distinguish them (an empty section\n // toggle keeps the chrome; only a true cold start shows the full-page skeleton).\n // Memoised because the `??` fallback minted a NEW array on every\n // render, which made the memo below re-run every time — i.e. do nothing.\n const guides = useMemo(() => initialGuides ?? guidesRes.data?.data ?? [], [initialGuides, guidesRes.data?.data]);\n const sections = useMemo(\n () => initialSections ?? (Array.isArray(sectionsRes.data) ? sectionsRes.data : []),\n [initialSections, sectionsRes.data],\n );\n const isLoading = selfFetch ? guidesRes.isLoading : false;\n\n // Section grouping. Data arrives already filtered (server-side `?section=`\n // for the host, or our `?section=` fetch); this just buckets visible rows.\n const grouped = useMemo(() => {\n const map = new Map<string, { section_order: number; guides: OnboardingGuide[] }>();\n for (const g of guides) {\n const existing = map.get(g.section);\n if (existing) {\n if (g.section_order < existing.section_order) existing.section_order = g.section_order;\n existing.guides.push(g);\n } else {\n map.set(g.section, { section_order: g.section_order, guides: [g] });\n }\n }\n for (const entry of map.values()) {\n entry.guides.sort((a, b) => a.step_order - b.step_order || a.title.localeCompare(b.title));\n }\n return Array.from(map.entries())\n .map(([section, info]) => ({ section, ...info }))\n .sort((a, b) => a.section_order - b.section_order || a.section.localeCompare(b.section));\n }, [guides]);\n\n const sectionFilterOptions = useMemo(\n () => [\n { value: 'all', label: `All (${guides.length})` },\n ...sections.map(s => ({ value: s.section, label: `${s.section} (${s.count})` })),\n ],\n [guides.length, sections],\n );\n\n // Section pill change → push `?section=X`. Controlled mode: host RSC\n // re-fetches. Self-fetch mode: our guides effect re-fetches on the URL change.\n const setSection = (value: string) => {\n const params = new URLSearchParams(searchParams.toString());\n if (value === 'all') {\n params.delete('section');\n } else {\n params.set('section', value);\n }\n const qs = params.toString();\n startTransition(() => {\n router.push(qs ? `${basePath}?${qs}` : basePath);\n });\n };\n\n // Search bar — scoped to onboarding-guides only via RAG-search `tableIds`.\n const source = runtime?.source ?? 'openframe';\n const docSearch = useDocSearch({\n source,\n baseRoute: basePath,\n onNavigate: path => router.push(path),\n tableIds: ['onboarding-guides'],\n });\n\n // Per-row card renderer — runtime-composed href, fallback to relative.\n const defaultRenderCard = (guide: OnboardingGuide) => {\n const cta = resolveContentHref(runtime?.composeContentUrl, {\n type: 'onboarding_guide',\n slug: guide.slug,\n basePath,\n platforms: guide.onboarding_guide_platforms,\n });\n return <OnboardingGuideCard guide={guide} href={cta.href} targetPlatform={cta.targetPlatform} size=\"catalog\" />;\n };\n const renderCardFn = renderCard ?? defaultRenderCard;\n\n const preControls = (\n <div className=\"space-y-4\">\n <DocSearchBar\n placeholder=\"Search onboarding guides, releases, case studies…\"\n query={docSearch.query}\n onQueryChange={docSearch.setQuery}\n results={docSearch.results}\n isLoading={docSearch.isLoading}\n onResultSelect={docSearch.handleResultSelect}\n showDropdown={docSearch.keepDropdownOpen}\n />\n {sections.length > 0 && (\n <FilterPillRow\n label=\"Section\"\n selectedValue={activeSection}\n onValueChange={setSection}\n options={sectionFilterOptions}\n />\n )}\n </div>\n );\n\n // A failed guides fetch → a RETRYABLE error (not the indistinguishable-from-\n // empty \"no guides found\" state) — parity with the sibling self-fetch views.\n if (selfFetch && guidesRes.error) {\n return <LoadError message=\"Failed to load onboarding guides.\" onRetry={guidesRes.reload} />;\n }\n // Full-page skeleton ONLY on the very first load (no data has loaded yet) — a\n // later section toggle that legitimately returns ZERO guides keeps the page\n // chrome (search + section pills) and dims the grid via the className below.\n if (selfFetch && guidesRes.data == null && isLoading) {\n return <OnboardingGuidesCatalogSkeleton />;\n }\n\n return (\n <div className=\"flex w-full flex-col gap-10\">\n {preControls}\n {guides.length === 0 ? (\n <div className=\"py-16 text-center\">\n <GraduationCap className=\"mx-auto mb-4 h-12 w-12 text-ods-text-secondary\" />\n <h2 className=\"mb-2 text-ods-text-primary text-h3\">No onboarding guides found</h2>\n <p className=\"text-ods-text-secondary text-h6\">\n {activeSection !== 'all'\n ? 'No guides in this section yet.'\n : \"We're working on the onboarding library. Check back soon.\"}\n </p>\n </div>\n ) : (\n <div className={isPending || isLoading ? 'space-y-10 opacity-60 transition-opacity' : 'space-y-10'}>\n {grouped.map(sec => (\n <section key={sec.section} className=\"space-y-4\">\n <h2 className=\"flex items-center gap-2 text-ods-text-primary text-h3\">\n {sec.section}\n <span className=\"inline-flex items-center justify-center rounded-full bg-ods-text-secondary/20 px-2 py-0.5 text-ods-text-secondary text-h6\">\n {sec.guides.length}\n </span>\n </h2>\n {/* HORIZONTAL catalog list — single column so consecutive steps\n read top-to-bottom; a grid would visually reorder them. */}\n <ul className=\"flex flex-col gap-4\">\n {sec.guides.map(guide => (\n <li key={guide.id}>{renderCardFn(guide)}</li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n </div>\n );\n}\n","'use client';\n\nimport { OnboardingGuideCardSkeleton } from '../chat/entity-cards/onboarding-guide-card';\n\n/**\n * Loading skeleton for `/onboarding-guides` — CHROME-LESS, exactly like the\n * loaded `<OnboardingGuidesCatalogView>`. The HOST page owns the\n * `<DevSectionPage sectionKey=\"onboarding\">` shell (hero + back button);\n * this component renders only what replaces the view: the search/pill\n * placeholder block and the section card lists.\n *\n * HISTORY: this used to mount its own `<DevSectionPage>` — nested inside the\n * page's shell that double-rendered the hero + back button during loading\n * (the view was made chrome-less in the page restructure; the skeleton was\n * missed). Keep BOTH chrome-less.\n *\n * Wrapper mirrors the loaded view's `w-full flex flex-col gap-10`; per-card\n * height matches the loaded catalog card so resolve shifts are zero.\n */\nexport function OnboardingGuidesCatalogSkeleton() {\n return (\n <div className=\"flex w-full animate-pulse flex-col gap-10\">\n {/* Search input placeholder — matches `<SearchInput>` h-12 — plus the\n section pill row (~74 px incl. padding), same as the loaded\n preControls block. */}\n <div className=\"space-y-4\">\n <div className=\"h-12 w-full rounded-md border border-ods-border bg-ods-card\" />\n <div className=\"flex flex-wrap items-center gap-3 rounded-lg border border-ods-border bg-ods-card p-4\">\n <div className=\"h-4 w-14 rounded bg-ods-border/60\" />\n {[0, 1, 2, 3].map(i => (\n <div key={i} className=\"h-10 w-24 rounded-md border border-ods-border bg-ods-card\" />\n ))}\n </div>\n </div>\n <div className=\"space-y-10\">\n {[4, 3, 3].map((cardCount, sectionIdx) => (\n <section key={sectionIdx} className=\"space-y-4\">\n <h2 className=\"flex items-center gap-2 tracking-[-0.36px] text-ods-text-primary text-h3\">\n <span className=\"h-6 w-40 rounded bg-ods-border/70\" />\n <span className=\"h-5 w-8 rounded-full bg-ods-text-secondary/20\" />\n </h2>\n <ul className=\"flex flex-col gap-4\">\n {Array.from({ length: cardCount }).map((_, cardIdx) => (\n <li key={cardIdx}>\n <OnboardingGuideCardSkeleton size=\"catalog\" />\n </li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n </div>\n );\n}\n","'use client';\n\n/**\n * Public-facing detail view for `/onboarding-guides/<slug>`.\n *\n * Two modes:\n * - **controlled** (hub SSR): pass `initialData` (a server-fetched guide).\n * - **self-fetching** (config-only embed): pass `slug` + `guideEndpoint`\n * (the api route) and the view fetches the guide itself — no host data\n * layer. (Plain fetch + useEffect, the DeliveryLists pattern.)\n *\n * Everything else flows through lib primitives + the ChatRuntime context\n * (markdown, video warmup, captions, related-card hrefs via\n * `runtime.composeContentUrl`). Optional `MarkdownRenderer` lets hosts swap a\n * renderer with extra plugins.\n */\n\nimport { Fragment, type ComponentType, type ReactNode } from 'react';\nimport { useChatRuntime } from '../../contexts/chat-runtime-context';\nimport { useRouter } from '../../embed-shims';\nimport { useSelfFetch } from '../../hooks/use-self-fetch';\nimport type { VideoTeaser } from '../../types/video-processing';\nimport { resolveContentHref } from '../../utils/content-href';\nimport { buildOgPlaceholderUrl } from '../../utils/og-placeholder';\nimport { EntityAuthorCard } from '../chat/entity-cards/entity-author-card';\nimport { OnboardingGuideCard } from '../chat/entity-cards/onboarding-guide-card';\nimport type { OnboardingGuide } from '../chat/types/entities/onboarding-guide';\nimport { EntityTagBadges } from '../features/entity-tag-badges';\nimport { EntityVideoSection } from '../features/entity-video-section';\nimport { useCaptions } from '../features/use-captions';\nimport { useVideoWarmup } from '../features/use-video-warmup';\nimport { toStripProfile } from '../features/video-bites-shared';\nimport { VideoBitesStrip } from '../features/video-bites-strip';\n// PageShell (max-w-[1920px]) — the guide detail content must share the SAME\n// container sizing as the related-content/FAQ rail the host page renders\n// below it (which uses the wide shell). ArticleDetailLayout (1280px) made\n// the detail block visibly narrower than the rail.\nimport { PageShell } from '../layout/article-detail-layout';\nimport { PageLayout } from '../layout/page-layout';\nimport { ArticleAuthorByline } from '../shared/article-author-byline';\nimport { DetailPageSkeleton } from '../shared/detail-page-skeleton';\nimport { LoadError } from '../ui/error-state';\nimport { RichMarkdownRenderer } from '../ui/markdown';\n\nexport interface OnboardingGuideDetailViewProps {\n /** Server-fetched guide (controlled / SSR mode). Omit it and pass\n * `slug` + `guideEndpoint` for the self-fetching config-only mode. */\n initialData?: OnboardingGuide;\n /** Self-fetch: the guide's slug (the host reads it from its route). */\n slug?: string;\n /** Self-fetch: builds the GET url for a single guide (the api route).\n * e.g. `(s) => \\`/content/api/onboarding-guides/${s}\\``. */\n guideEndpoint?: (slug: string) => string;\n related?: OnboardingGuide[];\n /** Link target for the author name in the metadata grid — the host\n * computes it (public author page; absent ⇒ plain text). */\n authorHref?: string;\n /** Bio paragraph for the end-of-article author byline. Defaults to the\n * guide payload's `author.about` (hubs that hydrate it can omit this). */\n authorBio?: string | null;\n /** Byline fallback paragraph when the bio is empty — the host passes its\n * platform-aware copy (the lib has no config awareness). Absent ⇒ the\n * byline renders nothing below the name when the bio is empty. */\n fallbackBio?: string | null;\n /** Optional markdown renderer override. Defaults to lib\n * `<RichMarkdownRenderer>`. */\n MarkdownRenderer?: ComponentType<{ content: string }>;\n /** Optional per-row related-card renderer override. */\n renderRelatedCard?: (guide: OnboardingGuide) => ReactNode;\n /** Back-link target. Defaults to `basePath`. */\n backHref?: string;\n /** Back-link label. Defaults to \"Back to Getting Started\". */\n backLabel?: string;\n /** Base path the related-card hrefs default to when\n * `runtime.composeContentUrl` is not wired. Default `/onboarding-guides`. */\n basePath?: string;\n /** Optional slot rendered inside the page chrome, BELOW the article + related\n * guides — e.g. the hub's cross-type related-content / FAQ rail. Lets the hub\n * mount this view directly (no local wrapper); embedders omit it. */\n relatedContent?: ReactNode;\n /** Render the standalone `<PageShell>`. Default true. Pass false when the host\n * layout already provides the page container — only the padding box renders,\n * avoiding a nested `<main>`. */\n shell?: boolean;\n}\n\nexport function OnboardingGuideDetailView({\n authorHref,\n authorBio,\n fallbackBio,\n initialData,\n slug,\n guideEndpoint,\n related = [],\n MarkdownRenderer = RichMarkdownRenderer,\n renderRelatedCard,\n backHref,\n backLabel = 'Back to Getting Started',\n basePath = '/onboarding-guides',\n relatedContent,\n shell = true,\n}: OnboardingGuideDetailViewProps) {\n const resolvedBackHref = backHref ?? basePath;\n const runtime = useChatRuntime();\n const captions = useCaptions();\n const router = useRouter();\n // `shell` true → standalone `<PageShell>`; false → padding-only box (no nested\n // <main>) for hosts whose layout already provides the container.\n const renderShell = (node: ReactNode) =>\n shell ? <PageShell>{node}</PageShell> : <div className=\"page-shell-content\">{node}</div>;\n\n // Controlled (hub SSR `initialData`) OR self-fetch by slug (config-only embed).\n const url = initialData ? null : slug && guideEndpoint ? guideEndpoint(slug) : null;\n const { data: guide, isLoading, error, reload } = useSelfFetch<OnboardingGuide>(url, { initialData });\n\n // Hooks must run unconditionally — feed them optional-chained values so they\n // no-op while the guide is still loading.\n const { ref: videoWarmupRef } = useVideoWarmup<HTMLDivElement>({\n videoUrl: guide?.main_video_url,\n supabaseStorageOrigin: runtime?.endpoints.supabaseStorageOrigin,\n });\n\n if (error || (!guide && !isLoading)) {\n return renderShell(<LoadError message=\"Guide not found.\" onRetry={reload} />);\n }\n if (!guide) {\n // Skeleton (not a bare \"Loading…\") for parity with every other shared view —\n // catalog, roadmap, releases all render a skeleton on first load, so the detail\n // page shouldn't flash text then content. `bare` + `PageShell` so the loading\n // state matches the loaded page's full width / padding / min-height.\n return renderShell(\n // Match the loaded page's top offset (TitleBlock's\n // `pt-[var(--spacing-system-l)]`) so content doesn't jump on load.\n <div className=\"pt-[var(--spacing-system-l)]\">\n <DetailPageSkeleton bare showImageGallery={false} />\n </div>,\n );\n }\n\n // Track URLs via the ONE captions configurer (`useCaptions`); same-origin\n // hosts get the relative `/api/captions/...` default. Zero per-page wiring.\n const { captionsUrl, highlightCaptionsUrl } = captions.forEntity('onboarding_guide', guide);\n\n const videoPoster =\n guide.main_video_thumbnail ||\n guide.featured_image ||\n guide.og_image_url ||\n // `buildOgPlaceholderUrl` always returns a usable string (relative route at\n // worst), so it's the terminal fallback — no trailing `|| undefined` needed.\n buildOgPlaceholderUrl(runtime?.endpoints, guide.title, { aspect: 'wide' });\n\n const defaultRenderRelatedCard = (g: OnboardingGuide) => {\n const cta = resolveContentHref(runtime?.composeContentUrl, {\n type: 'onboarding_guide',\n slug: g.slug,\n basePath,\n platforms: g.onboarding_guide_platforms,\n });\n return <OnboardingGuideCard guide={g} href={cta.href} targetPlatform={cta.targetPlatform} />;\n };\n const renderRelatedCardFn = renderRelatedCard ?? defaultRenderRelatedCard;\n\n return renderShell(\n <PageLayout\n title={guide.title}\n titleSize=\"h1\"\n titleWrap\n backButton={{ label: backLabel, onClick: () => router.push(resolvedBackHref) }}\n >\n {/* Tags — flat onboarding_guide_tags[] from entity_tags. */}\n <EntityTagBadges tags={guide.onboarding_guide_tags} />\n\n {/* Metadata grid — Section · Step | Published | Author. */}\n <EntityAuthorCard\n author={guide.author}\n authorHref={authorHref}\n publishedAt={guide.published_at}\n extraCells={[\n {\n value: `${guide.section} · Step ${guide.step_order}`,\n label: 'Section',\n uppercase: false,\n },\n ]}\n />\n\n {/* Video. `main_video_url` (Mux/MP4) and `youtube_url` are independent\n columns — either populated renders the player. */}\n {(guide.main_video_url || guide.youtube_url) && (\n <div ref={videoWarmupRef}>\n <EntityVideoSection\n mainVideoUrl={guide.main_video_url}\n youtubeUrl={guide.youtube_url || undefined}\n highlightVideoUrl={guide.highlight_video_url}\n mainVideoPoster={videoPoster}\n highlightVideoThumbnail={guide.highlight_video_thumbnail || undefined}\n videoSummary={undefined}\n videoBites={undefined}\n title={guide.title}\n srtContent={guide.srt_content}\n captionsUrl={captionsUrl}\n highlightCaptionsUrl={highlightCaptionsUrl}\n MarkdownRenderer={MarkdownRenderer}\n />\n </div>\n )}\n\n {/* Markdown body */}\n {guide.content && (\n <div className=\"space-y-4\">\n <MarkdownRenderer content={guide.content} />\n </div>\n )}\n\n {/* Video Bites — unified strip; renders nothing when no published bites. */}\n <VideoBitesStrip\n bites={(guide.video_bites as VideoTeaser[] | null) ?? []}\n filterPublished={true}\n showTitle={false}\n profile={toStripProfile(guide.author)}\n href={`/onboarding-guides/${guide.slug}`}\n />\n\n {/* End-of-article author byline (avatar + linked name + bio) — the\n author DESCRIPTION block every article-shaped detail page renders.\n Hidden when the guide has no author (the byline returns null). */}\n <ArticleAuthorByline\n author={guide.author?.full_name ?? null}\n avatar={guide.author?.avatar_url}\n jobTitle={guide.author?.job_title}\n bio={authorBio ?? guide.author?.about}\n href={authorHref}\n fallbackBio={fallbackBio}\n />\n\n {/* Related — same-section, ordered by step. */}\n {related.length > 0 && (\n <div className=\"space-y-4 border-t border-ods-border pt-8\">\n <h2 className=\"tracking-[-0.36px] text-ods-text-primary text-h3\">More in {guide.section}</h2>\n <ul className=\"flex flex-col gap-3\">\n {related.map(r => (\n <li key={r.id}>{renderRelatedCardFn(r)}</li>\n ))}\n </ul>\n </div>\n )}\n\n {/*\n * Host slot — cross-type related-content / FAQ rail.\n *\n * KEYED DELIBERATELY. This is one member of PageLayout's static children\n * array, and the only member whose element may be created in a SERVER\n * component and crossed over the RSC boundary. Static JSX children are\n * normally exempt from the key warning because `jsxs` runs\n * `validateChildKeys` and stamps `_store.validated = 1` on each element —\n * but a slot arriving over the wire is referenced as a lazy chunk wrapper,\n * so validation marks the WRAPPER and the fulfilled path never copies the\n * flag onto the revived element. It reaches the reconciler with\n * `validated: 0, key: null` and warns, blaming PageLayout (the owner of the\n * enclosing element) rather than anything actually at fault.\n *\n * A keyed Fragment emits no DOM, so rendered output is byte-identical.\n * Do NOT \"fix\" this in PageLayout via `React.Children.toArray` — that would\n * auto-key every consumer's children and change reconciliation identity for\n * all of them, and that file carries an explicit FROZEN contract.\n */}\n <Fragment key=\"related-content\">{relatedContent}</Fragment>\n </PageLayout>,\n );\n}\n","'use client';\n\n/**\n * React Query hooks for Onboarding Guides — PUBLIC reads only.\n *\n * Admin mutations (create/update/delete/publish/stats/save-highlight)\n * stay HUB-side in `hooks/use-onboarding-guides.ts` and import the\n * `onboardingGuideKeys.admin` sub-namespace from this module so the\n * cache namespace `['onboarding-guides']` has a single source of truth.\n *\n * Endpoints (`/api/onboarding-guides*`) are host-supplied — non-Next.js\n * embedders must reverse-proxy the same routes. Same precedent as the\n * tickets list hook.\n */\n\nimport { useQuery } from '@tanstack/react-query';\n\nimport type {\n OnboardingGuide,\n OnboardingGuideFilters,\n OnboardingGuideListResponse,\n OnboardingGuideSectionSummary,\n} from '../../chat/types/entities/onboarding-guide';\n\n/**\n * Cache key builder for the `['onboarding-guides']` namespace.\n *\n * Includes BOTH public-read sub-keys (`lists`, `list`, `details`,\n * `detail`, `sections`) AND the `admin` sub-namespace. Admin mutations\n * live hub-side but invalidate against this same builder so a single\n * `qc.invalidateQueries({ queryKey: onboardingGuideKeys.all })` from\n * an admin hook also clears the public read cache.\n */\nexport const onboardingGuideKeys = {\n all: ['onboarding-guides'] as const,\n lists: () => [...onboardingGuideKeys.all, 'list'] as const,\n list: (filters: OnboardingGuideFilters) => [...onboardingGuideKeys.lists(), filters] as const,\n details: () => [...onboardingGuideKeys.all, 'detail'] as const,\n detail: (slug: string) => [...onboardingGuideKeys.details(), slug] as const,\n sections: () => [...onboardingGuideKeys.all, 'sections'] as const,\n admin: {\n all: ['admin', 'onboarding-guides'] as const,\n lists: () => ['admin', 'onboarding-guides', 'list'] as const,\n detail: (slug: string) => ['admin', 'onboarding-guide', slug] as const,\n stats: () => ['admin', 'onboarding-guides', 'stats'] as const,\n },\n};\n\nexport function useOnboardingGuides(filters?: OnboardingGuideFilters) {\n return useQuery({\n queryKey: onboardingGuideKeys.list(filters || {}),\n queryFn: async (): Promise<OnboardingGuideListResponse> => {\n const params = new URLSearchParams();\n if (filters?.search) params.set('search', filters.search);\n if (filters?.section) params.set('section', filters.section);\n if (filters?.limit) params.set('limit', filters.limit.toString());\n if (filters?.offset) params.set('offset', filters.offset.toString());\n // Comma-joined, matching the route's `task_ids`-style split. This is the\n // chat-card loader's path: `ids` ALSO tells the route to drop the\n // platform scope, so omitting it silently returned a platform-filtered\n // catalog page under a cache key that claimed to be the id-filtered set.\n if (filters?.ids?.length) params.set('ids', filters.ids.join(','));\n const res = await fetch(`/api/onboarding-guides?${params}`);\n if (!res.ok) throw new Error('Failed to fetch onboarding guides');\n return (await res.json()) as OnboardingGuideListResponse;\n },\n });\n}\n\nexport function useOnboardingGuide(slug: string | undefined) {\n return useQuery({\n queryKey: onboardingGuideKeys.detail(slug || ''),\n queryFn: async (): Promise<OnboardingGuide> => {\n const res = await fetch(`/api/onboarding-guides/${slug}`);\n if (!res.ok) throw new Error('Failed to fetch onboarding guide');\n return (await res.json()) as OnboardingGuide;\n },\n enabled: !!slug,\n });\n}\n\nexport function useOnboardingGuideSections() {\n return useQuery({\n queryKey: onboardingGuideKeys.sections(),\n queryFn: async (): Promise<OnboardingGuideSectionSummary[]> => {\n const res = await fetch('/api/onboarding-guides/sections');\n if (!res.ok) throw new Error('Failed to fetch onboarding-guide sections');\n return (await res.json()) as OnboardingGuideSectionSummary[];\n },\n staleTime: 0,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-KDCIYRZV.cjs","../src/components/onboarding-guides/onboarding-guides-catalog-view.tsx","../src/components/onboarding-guides/onboarding-guides-catalog-skeleton.tsx","../src/components/onboarding-guides/onboarding-guide-detail-view.tsx","../src/components/onboarding-guides/hooks/use-onboarding-guides.ts"],"names":["jsx","jsxs"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-KDCIYRZV.cjs","sourcesContent":[null,"'use client';\n\n/**\n * Public-facing catalog view for `/onboarding-guides`.\n *\n * Two data modes:\n * - **controlled** (hub SSR): pass `initialGuides` + `initialSections`\n * (server-fetched; section changes re-render the host with new props).\n * - **self-fetching** (config-only embed): omit the data props and pass\n * `guidesEndpoint` + `sectionsEndpoint` (the api routes). The view fetches\n * the guides (refetching on `?section=` change) + the section list itself —\n * no host data layer. (Plain fetch + useEffect, the DeliveryLists pattern.)\n *\n * The view renders ONLY its body + its OWN controls (the RAG `<DocSearchBar>` +\n * dynamic section pills — onboarding's controls are content-driven, unlike the\n * static search/filter the `DevSectionView` chrome ships). It does NOT render the\n * page chrome: the CALLER wraps it in `<DevSectionPage sectionKey=\"onboarding\">`\n * (hero + back button), exactly like the roadmap / releases / delivery pages — so\n * every dev-center surface composes the same way and the view stays embeddable in\n * tabbed contexts. Card hrefs flow through `runtime.composeContentUrl`.\n */\n\nimport { GraduationCap } from 'lucide-react';\nimport { useMemo, useTransition, type ReactNode } from 'react';\n\nimport { useChatRuntime } from '../../contexts/chat-runtime-context';\nimport { useRouter, useSearchParams } from '../../embed-shims';\nimport { useSelfFetch } from '../../hooks/use-self-fetch';\nimport { resolveContentHref } from '../../utils/content-href';\nimport { OnboardingGuideCard } from '../chat/entity-cards/onboarding-guide-card';\nimport type { OnboardingGuide } from '../chat/types/entities/onboarding-guide';\nimport { DocSearchBar, useDocSearch } from '../shared/doc-search';\nimport { LoadError } from '../ui/error-state';\nimport { FilterPillRow } from '../ui/filter-pill-row';\nimport { OnboardingGuidesCatalogSkeleton } from './onboarding-guides-catalog-skeleton';\n\ntype SectionSummary = { section: string; section_order: number; count: number };\n\nexport interface OnboardingGuidesCatalogViewProps {\n /** Controlled / SSR: server-fetched guides. Omit + pass `guidesEndpoint`\n * for the self-fetching config-only mode. */\n initialGuides?: OnboardingGuide[];\n initialSections?: SectionSummary[];\n initialSection?: string;\n /** Self-fetch: GET list endpoint (the api route). Appends `?section=`. */\n guidesEndpoint?: string;\n /** Self-fetch: GET section-summary endpoint (the api route). */\n sectionsEndpoint?: string;\n /** Optional per-row card renderer override. */\n renderCard?: (guide: OnboardingGuide) => ReactNode;\n /** Base path the catalog is mounted under (fallback href prefix + `?section=`\n * push target). Default `/onboarding-guides`. */\n basePath?: string;\n}\n\nexport function OnboardingGuidesCatalogView({\n initialGuides,\n initialSections,\n initialSection = '',\n guidesEndpoint,\n sectionsEndpoint,\n renderCard,\n basePath = '/onboarding-guides',\n}: OnboardingGuidesCatalogViewProps) {\n const router = useRouter();\n const searchParams = useSearchParams();\n const [isPending, startTransition] = useTransition();\n const runtime = useChatRuntime();\n\n // Self-fetch only when the host supplied NO data but DID give an endpoint.\n const selfFetch = initialGuides === undefined && guidesEndpoint !== undefined;\n // Active section: from the URL when self-fetching, from the prop (host RSC\n // already read the URL server-side) when controlled.\n const sectionValue = selfFetch ? (searchParams.get('section') ?? '') : initialSection;\n const activeSection = sectionValue || 'all';\n\n // Self-fetch the guides (refetched whenever `?section=` changes — it's folded\n // into the url) + the section summaries. Controlled mode passes `null` urls so\n // the hook never fetches. The guides fetch carries the error/retry affordance.\n const guidesQs = sectionValue ? `?section=${encodeURIComponent(sectionValue)}` : '';\n const guidesRes = useSelfFetch<{ data?: OnboardingGuide[] }>(\n selfFetch && guidesEndpoint ? `${guidesEndpoint}${guidesQs}` : null,\n );\n const sectionsRes = useSelfFetch<SectionSummary[]>(selfFetch && sectionsEndpoint ? sectionsEndpoint : null);\n\n // Invariant: `guidesRes.data` is `null` until the first fetch resolves, then\n // `{ data: [...] }`. The `?.data ?? []` chain collapses never-fetched (null) and\n // legitimately-empty ([]) to the same `[]` — so the first-load skeleton gate below\n // keys on the OUTER `guidesRes.data == null` to distinguish them (an empty section\n // toggle keeps the chrome; only a true cold start shows the full-page skeleton).\n // Memoised because the `??` fallback minted a NEW array on every\n // render, which made the memo below re-run every time — i.e. do nothing.\n const guides = useMemo(() => initialGuides ?? guidesRes.data?.data ?? [], [initialGuides, guidesRes.data?.data]);\n const sections = useMemo(\n () => initialSections ?? (Array.isArray(sectionsRes.data) ? sectionsRes.data : []),\n [initialSections, sectionsRes.data],\n );\n const isLoading = selfFetch ? guidesRes.isLoading : false;\n\n // Section grouping. Data arrives already filtered (server-side `?section=`\n // for the host, or our `?section=` fetch); this just buckets visible rows.\n const grouped = useMemo(() => {\n const map = new Map<string, { section_order: number; guides: OnboardingGuide[] }>();\n for (const g of guides) {\n const existing = map.get(g.section);\n if (existing) {\n if (g.section_order < existing.section_order) existing.section_order = g.section_order;\n existing.guides.push(g);\n } else {\n map.set(g.section, { section_order: g.section_order, guides: [g] });\n }\n }\n for (const entry of map.values()) {\n entry.guides.sort((a, b) => a.step_order - b.step_order || a.title.localeCompare(b.title));\n }\n return Array.from(map.entries())\n .map(([section, info]) => ({ section, ...info }))\n .sort((a, b) => a.section_order - b.section_order || a.section.localeCompare(b.section));\n }, [guides]);\n\n const sectionFilterOptions = useMemo(\n () => [\n { value: 'all', label: `All (${guides.length})` },\n ...sections.map(s => ({ value: s.section, label: `${s.section} (${s.count})` })),\n ],\n [guides.length, sections],\n );\n\n // Section pill change → push `?section=X`. Controlled mode: host RSC\n // re-fetches. Self-fetch mode: our guides effect re-fetches on the URL change.\n const setSection = (value: string) => {\n const params = new URLSearchParams(searchParams.toString());\n if (value === 'all') {\n params.delete('section');\n } else {\n params.set('section', value);\n }\n const qs = params.toString();\n startTransition(() => {\n router.push(qs ? `${basePath}?${qs}` : basePath);\n });\n };\n\n // Search bar — scoped to onboarding-guides only via RAG-search `tableIds`.\n const source = runtime?.source ?? 'openframe';\n const docSearch = useDocSearch({\n source,\n baseRoute: basePath,\n onNavigate: path => router.push(path),\n tableIds: ['onboarding-guides'],\n });\n\n // Per-row card renderer — runtime-composed href, fallback to relative.\n const defaultRenderCard = (guide: OnboardingGuide) => {\n const cta = resolveContentHref(runtime?.composeContentUrl, {\n type: 'onboarding_guide',\n slug: guide.slug,\n basePath,\n platforms: guide.onboarding_guide_platforms,\n });\n return <OnboardingGuideCard guide={guide} href={cta.href} targetPlatform={cta.targetPlatform} size=\"catalog\" />;\n };\n const renderCardFn = renderCard ?? defaultRenderCard;\n\n const preControls = (\n <div className=\"space-y-4\">\n <DocSearchBar\n placeholder=\"Search onboarding guides, releases, case studies…\"\n query={docSearch.query}\n onQueryChange={docSearch.setQuery}\n results={docSearch.results}\n isLoading={docSearch.isLoading}\n onResultSelect={docSearch.handleResultSelect}\n showDropdown={docSearch.keepDropdownOpen}\n />\n {sections.length > 0 && (\n <FilterPillRow\n label=\"Section\"\n selectedValue={activeSection}\n onValueChange={setSection}\n options={sectionFilterOptions}\n />\n )}\n </div>\n );\n\n // A failed guides fetch → a RETRYABLE error (not the indistinguishable-from-\n // empty \"no guides found\" state) — parity with the sibling self-fetch views.\n if (selfFetch && guidesRes.error) {\n return <LoadError message=\"Failed to load onboarding guides.\" onRetry={guidesRes.reload} />;\n }\n // Full-page skeleton ONLY on the very first load (no data has loaded yet) — a\n // later section toggle that legitimately returns ZERO guides keeps the page\n // chrome (search + section pills) and dims the grid via the className below.\n if (selfFetch && guidesRes.data == null && isLoading) {\n return <OnboardingGuidesCatalogSkeleton />;\n }\n\n return (\n <div className=\"flex w-full flex-col gap-10\">\n {preControls}\n {guides.length === 0 ? (\n <div className=\"py-16 text-center\">\n <GraduationCap className=\"mx-auto mb-4 h-12 w-12 text-ods-text-secondary\" />\n <h2 className=\"mb-2 text-ods-text-primary text-h3\">No onboarding guides found</h2>\n <p className=\"text-ods-text-secondary text-h6\">\n {activeSection !== 'all'\n ? 'No guides in this section yet.'\n : \"We're working on the onboarding library. Check back soon.\"}\n </p>\n </div>\n ) : (\n <div className={isPending || isLoading ? 'space-y-10 opacity-60 transition-opacity' : 'space-y-10'}>\n {grouped.map(sec => (\n <section key={sec.section} className=\"space-y-4\">\n <h2 className=\"flex items-center gap-2 text-ods-text-primary text-h3\">\n {sec.section}\n <span className=\"inline-flex items-center justify-center rounded-full bg-ods-text-secondary/20 px-2 py-0.5 text-ods-text-secondary text-h6\">\n {sec.guides.length}\n </span>\n </h2>\n {/* HORIZONTAL catalog list — single column so consecutive steps\n read top-to-bottom; a grid would visually reorder them. */}\n <ul className=\"flex flex-col gap-4\">\n {sec.guides.map(guide => (\n <li key={guide.id}>{renderCardFn(guide)}</li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n </div>\n );\n}\n","'use client';\n\nimport { OnboardingGuideCardSkeleton } from '../chat/entity-cards/onboarding-guide-card';\n\n/**\n * Loading skeleton for `/onboarding-guides` — CHROME-LESS, exactly like the\n * loaded `<OnboardingGuidesCatalogView>`. The HOST page owns the\n * `<DevSectionPage sectionKey=\"onboarding\">` shell (hero + back button);\n * this component renders only what replaces the view: the search/pill\n * placeholder block and the section card lists.\n *\n * HISTORY: this used to mount its own `<DevSectionPage>` — nested inside the\n * page's shell that double-rendered the hero + back button during loading\n * (the view was made chrome-less in the page restructure; the skeleton was\n * missed). Keep BOTH chrome-less.\n *\n * Wrapper mirrors the loaded view's `w-full flex flex-col gap-10`; per-card\n * height matches the loaded catalog card so resolve shifts are zero.\n */\nexport function OnboardingGuidesCatalogSkeleton() {\n return (\n <div className=\"flex w-full animate-pulse flex-col gap-10\">\n {/* Search input placeholder — matches `<SearchInput>` h-12 — plus the\n section pill row (~74 px incl. padding), same as the loaded\n preControls block. */}\n <div className=\"space-y-4\">\n <div className=\"h-12 w-full rounded-md border border-ods-border bg-ods-card\" />\n <div className=\"flex flex-wrap items-center gap-3 rounded-lg border border-ods-border bg-ods-card p-4\">\n <div className=\"h-4 w-14 rounded bg-ods-border/60\" />\n {[0, 1, 2, 3].map(i => (\n <div key={i} className=\"h-10 w-24 rounded-md border border-ods-border bg-ods-card\" />\n ))}\n </div>\n </div>\n <div className=\"space-y-10\">\n {[4, 3, 3].map((cardCount, sectionIdx) => (\n <section key={sectionIdx} className=\"space-y-4\">\n <h2 className=\"flex items-center gap-2 tracking-[-0.36px] text-ods-text-primary text-h3\">\n <span className=\"h-6 w-40 rounded bg-ods-border/70\" />\n <span className=\"h-5 w-8 rounded-full bg-ods-text-secondary/20\" />\n </h2>\n <ul className=\"flex flex-col gap-4\">\n {Array.from({ length: cardCount }).map((_, cardIdx) => (\n <li key={cardIdx}>\n <OnboardingGuideCardSkeleton size=\"catalog\" />\n </li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n </div>\n );\n}\n","'use client';\n\n/**\n * Public-facing detail view for `/onboarding-guides/<slug>`.\n *\n * Two modes:\n * - **controlled** (hub SSR): pass `initialData` (a server-fetched guide).\n * - **self-fetching** (config-only embed): pass `slug` + `guideEndpoint`\n * (the api route) and the view fetches the guide itself — no host data\n * layer. (Plain fetch + useEffect, the DeliveryLists pattern.)\n *\n * Everything else flows through lib primitives + the ChatRuntime context\n * (markdown, video warmup, captions, related-card hrefs via\n * `runtime.composeContentUrl`). Optional `MarkdownRenderer` lets hosts swap a\n * renderer with extra plugins.\n */\n\nimport { Fragment, type ComponentType, type ReactNode } from 'react';\nimport { useChatRuntime } from '../../contexts/chat-runtime-context';\nimport { useRouter } from '../../embed-shims';\nimport { useSelfFetch } from '../../hooks/use-self-fetch';\nimport type { VideoTeaser } from '../../types/video-processing';\nimport { resolveContentHref } from '../../utils/content-href';\nimport { buildOgPlaceholderUrl } from '../../utils/og-placeholder';\nimport { EntityAuthorCard } from '../chat/entity-cards/entity-author-card';\nimport { OnboardingGuideCard } from '../chat/entity-cards/onboarding-guide-card';\nimport type { OnboardingGuide } from '../chat/types/entities/onboarding-guide';\nimport { EntityTagBadges } from '../features/entity-tag-badges';\nimport { EntityVideoSection } from '../features/entity-video-section';\nimport { useCaptions } from '../features/use-captions';\nimport { useVideoWarmup } from '../features/use-video-warmup';\nimport { toStripProfile } from '../features/video-bites-shared';\nimport { VideoBitesStrip } from '../features/video-bites-strip';\n// PageShell (max-w-[1920px]) — the guide detail content must share the SAME\n// container sizing as the related-content/FAQ rail the host page renders\n// below it (which uses the wide shell). ArticleDetailLayout (1280px) made\n// the detail block visibly narrower than the rail.\nimport { PageShell } from '../layout/article-detail-layout';\nimport { PageLayout } from '../layout/page-layout';\nimport { ArticleAuthorByline } from '../shared/article-author-byline';\nimport { DetailPageSkeleton } from '../shared/detail-page-skeleton';\nimport { LoadError } from '../ui/error-state';\nimport { RichMarkdownRenderer } from '../ui/markdown';\n\nexport interface OnboardingGuideDetailViewProps {\n /** Server-fetched guide (controlled / SSR mode). Omit it and pass\n * `slug` + `guideEndpoint` for the self-fetching config-only mode. */\n initialData?: OnboardingGuide;\n /** Self-fetch: the guide's slug (the host reads it from its route). */\n slug?: string;\n /** Self-fetch: builds the GET url for a single guide (the api route).\n * e.g. `(s) => \\`/content/api/onboarding-guides/${s}\\``. */\n guideEndpoint?: (slug: string) => string;\n related?: OnboardingGuide[];\n /** Link target for the author name in the metadata grid — the host\n * computes it (public author page; absent ⇒ plain text). */\n authorHref?: string;\n /** Bio paragraph for the end-of-article author byline. Defaults to the\n * guide payload's `author.about` (hubs that hydrate it can omit this). */\n authorBio?: string | null;\n /** Byline fallback paragraph when the bio is empty — the host passes its\n * platform-aware copy (the lib has no config awareness). Absent ⇒ the\n * byline renders nothing below the name when the bio is empty. */\n fallbackBio?: string | null;\n /** Optional markdown renderer override. Defaults to lib\n * `<RichMarkdownRenderer>`. */\n MarkdownRenderer?: ComponentType<{ content: string }>;\n /** Optional per-row related-card renderer override. */\n renderRelatedCard?: (guide: OnboardingGuide) => ReactNode;\n /** Back-link target. Defaults to `basePath`. */\n backHref?: string;\n /** Back-link label. Defaults to \"Back to Getting Started\". */\n backLabel?: string;\n /** Base path the related-card hrefs default to when\n * `runtime.composeContentUrl` is not wired. Default `/onboarding-guides`. */\n basePath?: string;\n /** Optional slot rendered inside the page chrome, BELOW the article + related\n * guides — e.g. the hub's cross-type related-content / FAQ rail. Lets the hub\n * mount this view directly (no local wrapper); embedders omit it. */\n relatedContent?: ReactNode;\n /** Render the standalone `<PageShell>`. Default true. Pass false when the host\n * layout already provides the page container — only the padding box renders,\n * avoiding a nested `<main>`. */\n shell?: boolean;\n}\n\nexport function OnboardingGuideDetailView({\n authorHref,\n authorBio,\n fallbackBio,\n initialData,\n slug,\n guideEndpoint,\n related = [],\n MarkdownRenderer = RichMarkdownRenderer,\n renderRelatedCard,\n backHref,\n backLabel = 'Back to Getting Started',\n basePath = '/onboarding-guides',\n relatedContent,\n shell = true,\n}: OnboardingGuideDetailViewProps) {\n const resolvedBackHref = backHref ?? basePath;\n const runtime = useChatRuntime();\n const captions = useCaptions();\n const router = useRouter();\n // `shell` true → standalone `<PageShell>`; false → padding-only box (no nested\n // <main>) for hosts whose layout already provides the container.\n const renderShell = (node: ReactNode) =>\n shell ? <PageShell>{node}</PageShell> : <div className=\"page-shell-content\">{node}</div>;\n\n // Controlled (hub SSR `initialData`) OR self-fetch by slug (config-only embed).\n const url = initialData ? null : slug && guideEndpoint ? guideEndpoint(slug) : null;\n const { data: guide, isLoading, error, reload } = useSelfFetch<OnboardingGuide>(url, { initialData });\n\n // Hooks must run unconditionally — feed them optional-chained values so they\n // no-op while the guide is still loading.\n const { ref: videoWarmupRef } = useVideoWarmup<HTMLDivElement>({\n videoUrl: guide?.main_video_url,\n supabaseStorageOrigin: runtime?.endpoints.supabaseStorageOrigin,\n });\n\n if (error || (!guide && !isLoading)) {\n return renderShell(<LoadError message=\"Guide not found.\" onRetry={reload} />);\n }\n if (!guide) {\n // Skeleton (not a bare \"Loading…\") for parity with every other shared view —\n // catalog, roadmap, releases all render a skeleton on first load, so the detail\n // page shouldn't flash text then content. `bare` + `PageShell` so the loading\n // state matches the loaded page's full width / padding / min-height.\n return renderShell(\n // Match the loaded page's top offset (TitleBlock's\n // `pt-[var(--spacing-system-l)]`) so content doesn't jump on load.\n <div className=\"pt-[var(--spacing-system-l)]\">\n <DetailPageSkeleton bare showImageGallery={false} />\n </div>,\n );\n }\n\n // Track URLs via the ONE captions configurer (`useCaptions`); same-origin\n // hosts get the relative `/api/captions/...` default. Zero per-page wiring.\n const { captionsUrl, highlightCaptionsUrl } = captions.forEntity('onboarding_guide', guide);\n\n const videoPoster =\n guide.main_video_thumbnail ||\n guide.featured_image ||\n guide.og_image_url ||\n // `buildOgPlaceholderUrl` always returns a usable string (relative route at\n // worst), so it's the terminal fallback — no trailing `|| undefined` needed.\n buildOgPlaceholderUrl(runtime?.endpoints, guide.title, { aspect: 'wide' });\n\n const defaultRenderRelatedCard = (g: OnboardingGuide) => {\n const cta = resolveContentHref(runtime?.composeContentUrl, {\n type: 'onboarding_guide',\n slug: g.slug,\n basePath,\n platforms: g.onboarding_guide_platforms,\n });\n return <OnboardingGuideCard guide={g} href={cta.href} targetPlatform={cta.targetPlatform} />;\n };\n const renderRelatedCardFn = renderRelatedCard ?? defaultRenderRelatedCard;\n\n return renderShell(\n <PageLayout\n title={guide.title}\n titleSize=\"h1\"\n titleWrap\n backButton={{ label: backLabel, onClick: () => router.push(resolvedBackHref) }}\n >\n {/* Tags — flat onboarding_guide_tags[] from entity_tags. */}\n <EntityTagBadges tags={guide.onboarding_guide_tags} />\n\n {/* Metadata grid — Section · Step | Published | Author. */}\n <EntityAuthorCard\n author={guide.author}\n authorHref={authorHref}\n publishedAt={guide.published_at}\n extraCells={[\n {\n value: `${guide.section} · Step ${guide.step_order}`,\n label: 'Section',\n uppercase: false,\n },\n ]}\n />\n\n {/* Video. `main_video_url` (Mux/MP4) and `youtube_url` are independent\n columns — either populated renders the player. */}\n {(guide.main_video_url || guide.youtube_url) && (\n <div ref={videoWarmupRef}>\n <EntityVideoSection\n mainVideoUrl={guide.main_video_url}\n youtubeUrl={guide.youtube_url || undefined}\n highlightVideoUrl={guide.highlight_video_url}\n mainVideoPoster={videoPoster}\n highlightVideoThumbnail={guide.highlight_video_thumbnail || undefined}\n videoSummary={undefined}\n videoBites={undefined}\n title={guide.title}\n srtContent={guide.srt_content}\n captionsUrl={captionsUrl}\n highlightCaptionsUrl={highlightCaptionsUrl}\n MarkdownRenderer={MarkdownRenderer}\n />\n </div>\n )}\n\n {/* Markdown body */}\n {guide.content && (\n <div className=\"space-y-4\">\n <MarkdownRenderer content={guide.content} />\n </div>\n )}\n\n {/* Video Bites — unified strip; renders nothing when no published bites. */}\n <VideoBitesStrip\n bites={(guide.video_bites as VideoTeaser[] | null) ?? []}\n filterPublished={true}\n showTitle={false}\n profile={toStripProfile(guide.author)}\n href={`/onboarding-guides/${guide.slug}`}\n />\n\n {/* End-of-article author byline (avatar + linked name + bio) — the\n author DESCRIPTION block every article-shaped detail page renders.\n Hidden when the guide has no author (the byline returns null). */}\n <ArticleAuthorByline\n author={guide.author?.full_name ?? null}\n avatar={guide.author?.avatar_url}\n jobTitle={guide.author?.job_title}\n bio={authorBio ?? guide.author?.about}\n href={authorHref}\n fallbackBio={fallbackBio}\n />\n\n {/* Related — same-section, ordered by step. */}\n {related.length > 0 && (\n <div className=\"space-y-4 border-t border-ods-border pt-8\">\n <h2 className=\"tracking-[-0.36px] text-ods-text-primary text-h3\">More in {guide.section}</h2>\n <ul className=\"flex flex-col gap-3\">\n {related.map(r => (\n <li key={r.id}>{renderRelatedCardFn(r)}</li>\n ))}\n </ul>\n </div>\n )}\n\n {/*\n * Host slot — cross-type related-content / FAQ rail.\n *\n * KEYED DELIBERATELY. This is one member of PageLayout's static children\n * array, and the only member whose element may be created in a SERVER\n * component and crossed over the RSC boundary. Static JSX children are\n * normally exempt from the key warning because `jsxs` runs\n * `validateChildKeys` and stamps `_store.validated = 1` on each element —\n * but a slot arriving over the wire is referenced as a lazy chunk wrapper,\n * so validation marks the WRAPPER and the fulfilled path never copies the\n * flag onto the revived element. It reaches the reconciler with\n * `validated: 0, key: null` and warns, blaming PageLayout (the owner of the\n * enclosing element) rather than anything actually at fault.\n *\n * A keyed Fragment emits no DOM, so rendered output is byte-identical.\n * Do NOT \"fix\" this in PageLayout via `React.Children.toArray` — that would\n * auto-key every consumer's children and change reconciliation identity for\n * all of them, and that file carries an explicit FROZEN contract.\n */}\n <Fragment key=\"related-content\">{relatedContent}</Fragment>\n </PageLayout>,\n );\n}\n","'use client';\n\n/**\n * React Query hooks for Onboarding Guides — PUBLIC reads only.\n *\n * Admin mutations (create/update/delete/publish/stats/save-highlight)\n * stay HUB-side in `hooks/use-onboarding-guides.ts` and import the\n * `onboardingGuideKeys.admin` sub-namespace from this module so the\n * cache namespace `['onboarding-guides']` has a single source of truth.\n *\n * Endpoints (`/api/onboarding-guides*`) are host-supplied — non-Next.js\n * embedders must reverse-proxy the same routes. Same precedent as the\n * tickets list hook.\n */\n\nimport { useQuery } from '@tanstack/react-query';\n\nimport type {\n OnboardingGuide,\n OnboardingGuideFilters,\n OnboardingGuideListResponse,\n OnboardingGuideSectionSummary,\n} from '../../chat/types/entities/onboarding-guide';\n\n/**\n * Cache key builder for the `['onboarding-guides']` namespace.\n *\n * Includes BOTH public-read sub-keys (`lists`, `list`, `details`,\n * `detail`, `sections`) AND the `admin` sub-namespace. Admin mutations\n * live hub-side but invalidate against this same builder so a single\n * `qc.invalidateQueries({ queryKey: onboardingGuideKeys.all })` from\n * an admin hook also clears the public read cache.\n */\nexport const onboardingGuideKeys = {\n all: ['onboarding-guides'] as const,\n lists: () => [...onboardingGuideKeys.all, 'list'] as const,\n list: (filters: OnboardingGuideFilters) => [...onboardingGuideKeys.lists(), filters] as const,\n details: () => [...onboardingGuideKeys.all, 'detail'] as const,\n detail: (slug: string) => [...onboardingGuideKeys.details(), slug] as const,\n sections: () => [...onboardingGuideKeys.all, 'sections'] as const,\n admin: {\n all: ['admin', 'onboarding-guides'] as const,\n lists: () => ['admin', 'onboarding-guides', 'list'] as const,\n detail: (slug: string) => ['admin', 'onboarding-guide', slug] as const,\n stats: () => ['admin', 'onboarding-guides', 'stats'] as const,\n },\n};\n\nexport function useOnboardingGuides(filters?: OnboardingGuideFilters) {\n return useQuery({\n queryKey: onboardingGuideKeys.list(filters || {}),\n queryFn: async (): Promise<OnboardingGuideListResponse> => {\n const params = new URLSearchParams();\n if (filters?.search) params.set('search', filters.search);\n if (filters?.section) params.set('section', filters.section);\n if (filters?.limit) params.set('limit', filters.limit.toString());\n if (filters?.offset) params.set('offset', filters.offset.toString());\n // Comma-joined, matching the route's `task_ids`-style split. This is the\n // chat-card loader's path: `ids` ALSO tells the route to drop the\n // platform scope, so omitting it silently returned a platform-filtered\n // catalog page under a cache key that claimed to be the id-filtered set.\n if (filters?.ids?.length) params.set('ids', filters.ids.join(','));\n const res = await fetch(`/api/onboarding-guides?${params}`);\n if (!res.ok) throw new Error('Failed to fetch onboarding guides');\n return (await res.json()) as OnboardingGuideListResponse;\n },\n });\n}\n\nexport function useOnboardingGuide(slug: string | undefined) {\n return useQuery({\n queryKey: onboardingGuideKeys.detail(slug || ''),\n queryFn: async (): Promise<OnboardingGuide> => {\n const res = await fetch(`/api/onboarding-guides/${slug}`);\n if (!res.ok) throw new Error('Failed to fetch onboarding guide');\n return (await res.json()) as OnboardingGuide;\n },\n enabled: !!slug,\n });\n}\n\nexport function useOnboardingGuideSections() {\n return useQuery({\n queryKey: onboardingGuideKeys.sections(),\n queryFn: async (): Promise<OnboardingGuideSectionSummary[]> => {\n const res = await fetch('/api/onboarding-guides/sections');\n if (!res.ok) throw new Error('Failed to fetch onboarding-guide sections');\n return (await res.json()) as OnboardingGuideSectionSummary[];\n },\n staleTime: 0,\n });\n}\n"]}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var _chunkQ25VYAELcjs = require('./chunk-Q25VYAEL.cjs');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
var
|
|
11
|
+
var _chunkWTYQCT4Acjs = require('./chunk-WTYQCT4A.cjs');
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
@@ -19,7 +19,7 @@ var _chunkDVSPRY2Vcjs = require('./chunk-DVSPRY2V.cjs');
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
var
|
|
22
|
+
var _chunkRTKF5AYAcjs = require('./chunk-RTKF5AYA.cjs');
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
|
|
@@ -35,7 +35,7 @@ var _chunkBXAGR3POcjs = require('./chunk-BXAGR3PO.cjs');
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
var
|
|
38
|
+
var _chunkEXWS2QSDcjs = require('./chunk-EXWS2QSD.cjs');
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
|
|
@@ -113,7 +113,7 @@ var TicketActionFailure = class extends Error {
|
|
|
113
113
|
function useTicketActions(options) {
|
|
114
114
|
const queryClient = _reactquery.useQueryClient.call(void 0, );
|
|
115
115
|
const ticketActionEndpoint = _nullishCoalesce(_chunkU2YLMFJKcjs.useRequiredChatRuntime.call(void 0, ).endpoints.ticketActionUrl, () => ( TICKET_ACTION_ENDPOINT));
|
|
116
|
-
const ticketLive =
|
|
116
|
+
const ticketLive = _chunkRTKF5AYAcjs.useOptionalTicketLive.call(void 0, );
|
|
117
117
|
const notifyTicketCreated = _optionalChain([ticketLive, 'optionalAccess', _2 => _2.notifyTicketCreated]);
|
|
118
118
|
const { prependOptimistic, removeOptimistic, removeTicketFromCache, toast: toast2, onSupportSystemDown } = options;
|
|
119
119
|
const formInFlightRef = _react.useRef.call(void 0, null);
|
|
@@ -274,10 +274,10 @@ function useTicketActions(options) {
|
|
|
274
274
|
..._optionalChain([input, 'access', _3 => _3.attachments, 'optionalAccess', _4 => _4.length]) ? { attachments: input.attachments } : {}
|
|
275
275
|
});
|
|
276
276
|
if (result.mirror_synced === false) {
|
|
277
|
-
toast2(
|
|
277
|
+
toast2(_chunkRTKF5AYAcjs.TOAST_COPY.open_mirror_pending);
|
|
278
278
|
watchMirrorSync(placeholderId, result.ticket_id);
|
|
279
279
|
} else {
|
|
280
|
-
toast2(
|
|
280
|
+
toast2(_chunkRTKF5AYAcjs.TOAST_COPY.open_success);
|
|
281
281
|
await queryClient.invalidateQueries({ queryKey: ["tickets"] });
|
|
282
282
|
removeOptimistic(placeholderId);
|
|
283
283
|
}
|
|
@@ -363,7 +363,7 @@ function useTicketActions(options) {
|
|
|
363
363
|
...hasText ? { content_addendum: trimmed } : {},
|
|
364
364
|
...hasFiles ? { attachments } : {}
|
|
365
365
|
},
|
|
366
|
-
|
|
366
|
+
_chunkRTKF5AYAcjs.TOAST_COPY.comment_success,
|
|
367
367
|
"send message"
|
|
368
368
|
);
|
|
369
369
|
if (ok) {
|
|
@@ -386,13 +386,13 @@ function useTicketActions(options) {
|
|
|
386
386
|
status: "CLOSED",
|
|
387
387
|
..._optionalChain([resolution, 'optionalAccess', _6 => _6.trim, 'call', _7 => _7()]) ? { resolution: resolution.trim() } : {}
|
|
388
388
|
},
|
|
389
|
-
|
|
389
|
+
_chunkRTKF5AYAcjs.TOAST_COPY.close_success,
|
|
390
390
|
"close ticket"
|
|
391
391
|
),
|
|
392
392
|
[updateTicket]
|
|
393
393
|
);
|
|
394
394
|
const reopenTicket = _react.useCallback.call(void 0,
|
|
395
|
-
(ticket) => updateTicket(ticket, { status: "OPEN" },
|
|
395
|
+
(ticket) => updateTicket(ticket, { status: "OPEN" }, _chunkRTKF5AYAcjs.TOAST_COPY.reopen_success, "reopen ticket"),
|
|
396
396
|
[updateTicket]
|
|
397
397
|
);
|
|
398
398
|
return _react.useMemo.call(void 0,
|
|
@@ -607,14 +607,14 @@ function useTicketsList(filters) {
|
|
|
607
607
|
// src/components/tickets/ticket-open-form.tsx
|
|
608
608
|
|
|
609
609
|
var _jsxruntime = require('react/jsx-runtime');
|
|
610
|
-
var COUNTER_VISIBLE_AT = Math.floor(
|
|
610
|
+
var COUNTER_VISIBLE_AT = Math.floor(_chunkRTKF5AYAcjs.TICKET_TEXT_MAX_CHARS * 0.8);
|
|
611
611
|
function TicketOpenForm({ onSubmit, isSubmitting, supportSystemDown }) {
|
|
612
612
|
const [subject, setSubject] = _react.useState.call(void 0, "");
|
|
613
613
|
const [content, setContent] = _react.useState.call(void 0, "");
|
|
614
614
|
const { attachments, readyAttachments, hasInflightUploads, addFiles, removeAttachment, clear } = _chunkGZRU2A4Lcjs.useChatAttachments.call(void 0, );
|
|
615
615
|
const trimmedSubject = subject.trim();
|
|
616
616
|
const trimmedContent = content.trim();
|
|
617
|
-
const overCap = content.length >
|
|
617
|
+
const overCap = content.length > _chunkRTKF5AYAcjs.TICKET_TEXT_MAX_CHARS;
|
|
618
618
|
const showCounter = content.length >= COUNTER_VISIBLE_AT;
|
|
619
619
|
const canSubmit = !isSubmitting && !supportSystemDown && !hasInflightUploads && trimmedSubject.length > 0 && trimmedContent.length > 0 && !overCap;
|
|
620
620
|
const handleSubmit = async (e) => {
|
|
@@ -631,7 +631,7 @@ function TicketOpenForm({ onSubmit, isSubmitting, supportSystemDown }) {
|
|
|
631
631
|
clear();
|
|
632
632
|
}
|
|
633
633
|
};
|
|
634
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk53O6PQ3Fcjs.Card, { className: "p-6", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { onSubmit: handleSubmit, className: "flex flex-col gap-6 md:flex-row", ...
|
|
634
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk53O6PQ3Fcjs.Card, { className: "p-6", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { onSubmit: handleSubmit, className: "flex flex-col gap-6 md:flex-row", ..._chunkEXWS2QSDcjs.HUBSPOT_DO_NOT_COLLECT_FORM_PROPS, children: [
|
|
635
635
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "min-w-0 flex-1 md:max-w-md", children: [
|
|
636
636
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: "mb-2 text-ods-text-primary text-h2", children: "Need Support?" }),
|
|
637
637
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-ods-text-secondary text-h6", children: "Can't find what you're looking for? Submit a support ticket below \u2014 we'll follow up shortly." }),
|
|
@@ -656,7 +656,7 @@ function TicketOpenForm({ onSubmit, isSubmitting, supportSystemDown }) {
|
|
|
656
656
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
|
|
657
657
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { htmlFor: "ticket-content", className: "mb-1 block text-ods-text-primary text-h6", children: "Your Message" }),
|
|
658
658
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
659
|
-
|
|
659
|
+
_chunkEXWS2QSDcjs.Textarea,
|
|
660
660
|
{
|
|
661
661
|
id: "ticket-content",
|
|
662
662
|
placeholder: "Describe your issue or question in detail...",
|
|
@@ -670,7 +670,7 @@ function TicketOpenForm({ onSubmit, isSubmitting, supportSystemDown }) {
|
|
|
670
670
|
showCounter && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { className: `mt-1 text-right text-h6 ${overCap ? "text-ods-error" : "text-ods-text-secondary"}`, children: [
|
|
671
671
|
content.length,
|
|
672
672
|
"/",
|
|
673
|
-
|
|
673
|
+
_chunkRTKF5AYAcjs.TICKET_TEXT_MAX_CHARS
|
|
674
674
|
] })
|
|
675
675
|
] }),
|
|
676
676
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
@@ -786,7 +786,7 @@ function TicketLinkedDeliveryCard({ clickup, className }) {
|
|
|
786
786
|
dateClosed: clickup.date_closed,
|
|
787
787
|
clickupUrl: _nullishCoalesce(clickup.clickup_url, () => ( ""))
|
|
788
788
|
};
|
|
789
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: `overflow-hidden rounded-md border border-ods-border bg-ods-bg ${_nullishCoalesce(className, () => ( ""))}`, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
789
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: `overflow-hidden rounded-md border border-ods-border bg-ods-bg ${_nullishCoalesce(className, () => ( ""))}`, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWTYQCT4Acjs.DeliveryRow, { item, href: clickup.delivery_href, caption: "Linked delivery" }) });
|
|
790
790
|
}
|
|
791
791
|
|
|
792
792
|
// src/components/tickets/ticket-reply-composer.tsx
|
|
@@ -839,7 +839,7 @@ function TicketReplyComposer({
|
|
|
839
839
|
sending: busy || attachments.hasInflightUploads,
|
|
840
840
|
disabled: supportSystemDown,
|
|
841
841
|
allowEmptySend: hasReadyFiles,
|
|
842
|
-
maxLength:
|
|
842
|
+
maxLength: _chunkRTKF5AYAcjs.TICKET_TEXT_MAX_CHARS,
|
|
843
843
|
onSend: handleSend
|
|
844
844
|
}
|
|
845
845
|
),
|
|
@@ -868,25 +868,25 @@ function TicketReplyComposer({
|
|
|
868
868
|
}
|
|
869
869
|
)
|
|
870
870
|
] }),
|
|
871
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
872
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
873
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
874
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
871
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.AlertDialog, { open: closeDialogOpen, onOpenChange: setCloseDialogOpen, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkEXWS2QSDcjs.AlertDialogContent, { className: "border-ods-border bg-ods-card", children: [
|
|
872
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkEXWS2QSDcjs.AlertDialogHeader, { children: [
|
|
873
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.AlertDialogTitle, { className: "text-ods-text-primary", children: "Close this ticket?" }),
|
|
874
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.AlertDialogDescription, { className: "text-ods-text-secondary", children: "Add an optional resolution note below. You can reopen the ticket later if needed." })
|
|
875
875
|
] }),
|
|
876
876
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
877
|
-
|
|
877
|
+
_chunkEXWS2QSDcjs.Textarea,
|
|
878
878
|
{
|
|
879
879
|
value: resolution,
|
|
880
880
|
onChange: (e) => setResolution(e.target.value),
|
|
881
881
|
placeholder: "Resolution (optional)",
|
|
882
882
|
rows: 3,
|
|
883
|
-
maxLength:
|
|
883
|
+
maxLength: _chunkRTKF5AYAcjs.TICKET_TEXT_MAX_CHARS,
|
|
884
884
|
className: "mt-2"
|
|
885
885
|
}
|
|
886
886
|
),
|
|
887
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
887
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkEXWS2QSDcjs.AlertDialogFooter, { children: [
|
|
888
888
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
889
|
-
|
|
889
|
+
_chunkEXWS2QSDcjs.AlertDialogCancel,
|
|
890
890
|
{
|
|
891
891
|
disabled: busy,
|
|
892
892
|
className: "border-ods-border bg-transparent text-ods-text-primary hover:bg-ods-border",
|
|
@@ -894,7 +894,7 @@ function TicketReplyComposer({
|
|
|
894
894
|
}
|
|
895
895
|
),
|
|
896
896
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
897
|
-
|
|
897
|
+
_chunkEXWS2QSDcjs.AlertDialogAction,
|
|
898
898
|
{
|
|
899
899
|
onClick: () => void confirmClose(),
|
|
900
900
|
disabled: busy,
|
|
@@ -957,9 +957,9 @@ var TICKET_FEED_INNER = "flex flex-col gap-4 md:gap-6 px-4 md:px-6 py-4 md:py-6"
|
|
|
957
957
|
var TICKET_FEED_SKELETON_ROWS = 6;
|
|
958
958
|
function TicketTimelinePanel({ ticket }) {
|
|
959
959
|
const identity = _chunkGZRU2A4Lcjs.useChatIdentity.call(void 0, );
|
|
960
|
-
const externalId =
|
|
960
|
+
const externalId = _chunkRTKF5AYAcjs.isOptimistic.call(void 0, ticket) ? null : ticket.external_id;
|
|
961
961
|
const { engagements, isLoading } = useTicketEngagements(externalId, !!externalId);
|
|
962
|
-
const live =
|
|
962
|
+
const live = _chunkRTKF5AYAcjs.useOptionalTicketLive.call(void 0, );
|
|
963
963
|
const setOpenTicketId = _optionalChain([live, 'optionalAccess', _25 => _25.setOpenTicketId]);
|
|
964
964
|
const markRead = _optionalChain([live, 'optionalAccess', _26 => _26.markRead]);
|
|
965
965
|
const markReadDebounceRef = _react.useRef.call(void 0, null);
|
|
@@ -983,7 +983,7 @@ function TicketTimelinePanel({ ticket }) {
|
|
|
983
983
|
markReadDebounceRef.current = setTimeout(() => {
|
|
984
984
|
markReadDebounceRef.current = null;
|
|
985
985
|
void markRead(externalId);
|
|
986
|
-
},
|
|
986
|
+
}, _chunkRTKF5AYAcjs.TICKET_MARK_READ_DEBOUNCE_MS);
|
|
987
987
|
}, [engagementsCount, externalId, markRead]);
|
|
988
988
|
const { scrollRef, contentRef } = _usesticktobottom.useStickToBottom.call(void 0, { initial: "instant", resize: "smooth" });
|
|
989
989
|
const bodyTurns = ticket.body ? ticket.body.split(TURN_SEPARATOR_RE).map((t) => t.trim()).filter(Boolean) : [];
|
|
@@ -1052,7 +1052,7 @@ function TicketTimelinePanel({ ticket }) {
|
|
|
1052
1052
|
avatarUrl: avatarSrc,
|
|
1053
1053
|
timeLabel: eng.createdAt ? _chunkGZRU2A4Lcjs.formatRelativeTime.call(void 0, eng.createdAt) : null,
|
|
1054
1054
|
body: stripAttachmentsPreamble(_nullishCoalesce(eng.body, () => ( ""))),
|
|
1055
|
-
footer: engAttachments.length > 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "mt-2", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1055
|
+
footer: engAttachments.length > 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "mt-2", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.TicketAttachmentsList, { attachments: engAttachments, size: "compact" }) }) : null
|
|
1056
1056
|
},
|
|
1057
1057
|
eng.id
|
|
1058
1058
|
);
|
|
@@ -1165,7 +1165,7 @@ function TicketRow({
|
|
|
1165
1165
|
onActionCollapsed,
|
|
1166
1166
|
id
|
|
1167
1167
|
}) {
|
|
1168
|
-
const optimistic =
|
|
1168
|
+
const optimistic = _chunkRTKF5AYAcjs.isOptimistic.call(void 0, ticket);
|
|
1169
1169
|
const rowRef = _react.useRef.call(void 0, null);
|
|
1170
1170
|
const handleClick = _react.useCallback.call(void 0, () => {
|
|
1171
1171
|
onToggle(ticket.id);
|
|
@@ -1334,11 +1334,11 @@ function TicketCenterAuthed({
|
|
|
1334
1334
|
) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk53O6PQ3Fcjs.Card, { className: "overflow-hidden", children: merged.map((ticket) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1335
1335
|
TicketRow,
|
|
1336
1336
|
{
|
|
1337
|
-
id:
|
|
1337
|
+
id: _chunkEXWS2QSDcjs.devSectionAnchorId.call(void 0, "ticket", ticket.external_id),
|
|
1338
1338
|
ticket,
|
|
1339
1339
|
expanded: expandedTicketId === ticket.id,
|
|
1340
1340
|
onToggle: toggleRow,
|
|
1341
|
-
busy:
|
|
1341
|
+
busy: _chunkRTKF5AYAcjs.isOptimistic.call(void 0, ticket) ? false : actions.isRowBusy(ticket.id),
|
|
1342
1342
|
supportSystemDown,
|
|
1343
1343
|
onSendMessage: actions.sendMessage,
|
|
1344
1344
|
onClose: actions.closeTicket,
|
|
@@ -1392,7 +1392,7 @@ function HelpCenterCard({
|
|
|
1392
1392
|
onClearReplyError,
|
|
1393
1393
|
id
|
|
1394
1394
|
}) {
|
|
1395
|
-
const optimistic =
|
|
1395
|
+
const optimistic = _chunkRTKF5AYAcjs.isOptimistic.call(void 0, ticket);
|
|
1396
1396
|
const rawStatus = (_nullishCoalesce(ticket.status, () => ( "OPEN"))).toUpperCase();
|
|
1397
1397
|
const priority = (_nullishCoalesce(ticket.priority, () => ( ""))).toUpperCase();
|
|
1398
1398
|
const relativeUpdated = ticket.hubspot_updated_at ? _chunkGZRU2A4Lcjs.formatRelativeTime.call(void 0, ticket.hubspot_updated_at) : "recently";
|
|
@@ -1414,7 +1414,7 @@ function HelpCenterCard({
|
|
|
1414
1414
|
});
|
|
1415
1415
|
return () => cancelAnimationFrame(raf);
|
|
1416
1416
|
}, [isExpanded]);
|
|
1417
|
-
const live =
|
|
1417
|
+
const live = _chunkRTKF5AYAcjs.useOptionalTicketLive.call(void 0, );
|
|
1418
1418
|
const unreadCount = !optimistic && ticket.external_id && _optionalChain([live, 'optionalAccess', _64 => _64.unreadByTicket, 'access', _65 => _65[ticket.external_id]]) || 0;
|
|
1419
1419
|
const rightBadges = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1420
1420
|
unreadCount > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
@@ -1464,7 +1464,7 @@ function HelpCenterCard({
|
|
|
1464
1464
|
"aria-controls": isExpanded ? `help-center-drawer-${ticket.id}` : void 0,
|
|
1465
1465
|
className: "w-full p-[12px] text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ods-accent disabled:cursor-default md:p-[16px]",
|
|
1466
1466
|
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1467
|
-
|
|
1467
|
+
_chunkWTYQCT4Acjs.DevCardRowContent,
|
|
1468
1468
|
{
|
|
1469
1469
|
title,
|
|
1470
1470
|
subtitle,
|
|
@@ -1565,7 +1565,7 @@ function HelpCenterCreateForm({
|
|
|
1565
1565
|
subjectError && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { id: "help-center-subject-error", className: "mt-1 text-ods-error text-h6", children: subjectError })
|
|
1566
1566
|
] });
|
|
1567
1567
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1568
|
-
|
|
1568
|
+
_chunkQ25VYAELcjs.ContactForm,
|
|
1569
1569
|
{
|
|
1570
1570
|
rescue: null,
|
|
1571
1571
|
title: "Open a new ticket",
|
|
@@ -1608,25 +1608,25 @@ function HelpCenterList({ toast: toast2 = _chunkLNKLB7GMcjs.toast, backButton, t
|
|
|
1608
1608
|
const searchParams = _chunkSADRZ4YIcjs.useSearchParams.call(void 0, );
|
|
1609
1609
|
const router = _chunkSADRZ4YIcjs.useRouter.call(void 0, );
|
|
1610
1610
|
const pathname = _chunkSADRZ4YIcjs.usePathname.call(void 0, );
|
|
1611
|
-
const search = searchParams.get(
|
|
1611
|
+
const search = searchParams.get(_chunkEXWS2QSDcjs.DEV_SECTION_PARAM_KEYS.search) || "";
|
|
1612
1612
|
const status = searchParams.get("status") || "all";
|
|
1613
|
-
const ticketParam = searchParams.get(
|
|
1613
|
+
const ticketParam = searchParams.get(_chunkEXWS2QSDcjs.TICKET_OPEN_PARAM) || "";
|
|
1614
1614
|
const page = _chunkMIZU2OTOcjs.positiveInt.call(void 0, searchParams.get("page"), 1);
|
|
1615
1615
|
if (identity.isLoading) {
|
|
1616
1616
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1617
|
-
|
|
1617
|
+
_chunkWTYQCT4Acjs.DevSectionPage,
|
|
1618
1618
|
{
|
|
1619
1619
|
sectionKey: "tickets",
|
|
1620
1620
|
backButton,
|
|
1621
1621
|
title,
|
|
1622
1622
|
shell,
|
|
1623
1623
|
preControls: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, HelpCenterCreateFormSkeleton, {}),
|
|
1624
|
-
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1624
|
+
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWTYQCT4Acjs.DevCardRowSkeletonList, {})
|
|
1625
1625
|
}
|
|
1626
1626
|
);
|
|
1627
1627
|
}
|
|
1628
1628
|
if (identity.authTier === "anon" || !_optionalChain([identity, 'access', _66 => _66.user, 'optionalAccess', _67 => _67.email])) {
|
|
1629
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1629
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWTYQCT4Acjs.DevSectionPage, { sectionKey: "tickets", backButton, title, shell, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1630
1630
|
_chunkDVSPRY2Vcjs.EmptyState,
|
|
1631
1631
|
{
|
|
1632
1632
|
type: "generic",
|
|
@@ -1678,8 +1678,8 @@ function HelpCenterListAuthed({
|
|
|
1678
1678
|
const setOpenTicket = _react.useCallback.call(void 0,
|
|
1679
1679
|
(externalId) => {
|
|
1680
1680
|
const params = new URLSearchParams(searchParams.toString());
|
|
1681
|
-
if (externalId) params.set(
|
|
1682
|
-
else params.delete(
|
|
1681
|
+
if (externalId) params.set(_chunkEXWS2QSDcjs.TICKET_OPEN_PARAM, externalId);
|
|
1682
|
+
else params.delete(_chunkEXWS2QSDcjs.TICKET_OPEN_PARAM);
|
|
1683
1683
|
const qs = params.toString();
|
|
1684
1684
|
router.replace(qs ? `${pathname}?${qs}` : pathname, { scroll: false });
|
|
1685
1685
|
},
|
|
@@ -1756,15 +1756,15 @@ function HelpCenterListAuthed({
|
|
|
1756
1756
|
] }),
|
|
1757
1757
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkF6U2H42Lcjs.Button, { type: "button", variant: "accent", onClick: () => refetch(), children: "Retry" })
|
|
1758
1758
|
] }),
|
|
1759
|
-
!error && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full", children: isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1759
|
+
!error && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full", children: isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWTYQCT4Acjs.DevCardRowSkeletonList, {}) : !hasResults && isFetching ? (
|
|
1760
1760
|
// Bridge state — background refetch in flight and the
|
|
1761
1761
|
// optimistic placeholder was just removed by the mutation
|
|
1762
1762
|
// callback. Without this branch "No tickets yet" would flash
|
|
1763
1763
|
// for ~50ms between `removeOptimistic` and the server
|
|
1764
1764
|
// response landing.
|
|
1765
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1765
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWTYQCT4Acjs.DevCardRowSkeletonList, { rows: 1 })
|
|
1766
1766
|
) : !hasResults ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1767
|
-
|
|
1767
|
+
_chunkWTYQCT4Acjs.ListEmptyState,
|
|
1768
1768
|
{
|
|
1769
1769
|
isFiltered: hasActiveFilters,
|
|
1770
1770
|
filtered: {
|
|
@@ -1795,11 +1795,11 @@ function HelpCenterListAuthed({
|
|
|
1795
1795
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full overflow-clip rounded-[6px] border border-ods-border bg-ods-card", children: merged.map((ticket) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1796
1796
|
HelpCenterCard,
|
|
1797
1797
|
{
|
|
1798
|
-
id:
|
|
1798
|
+
id: _chunkEXWS2QSDcjs.devSectionAnchorId.call(void 0, "ticket", ticket.external_id),
|
|
1799
1799
|
ticket,
|
|
1800
1800
|
expanded: expandedTicketId === ticket.id,
|
|
1801
1801
|
onToggle: toggleRow,
|
|
1802
|
-
busy:
|
|
1802
|
+
busy: _chunkRTKF5AYAcjs.isOptimistic.call(void 0, ticket) ? false : actions.isRowBusy(ticket.id),
|
|
1803
1803
|
supportSystemDown,
|
|
1804
1804
|
onSendMessage: actions.sendMessage,
|
|
1805
1805
|
onClose: actions.closeTicket,
|
|
@@ -1813,7 +1813,7 @@ function HelpCenterListAuthed({
|
|
|
1813
1813
|
) }),
|
|
1814
1814
|
!error && totalPages > 1 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkDVSPRY2Vcjs.UnifiedPagination, { currentPage: page, totalPages })
|
|
1815
1815
|
] });
|
|
1816
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1816
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWTYQCT4Acjs.DevSectionPage, { sectionKey: "tickets", backButton, title, shell, preControls: form, children: body });
|
|
1817
1817
|
}
|
|
1818
1818
|
|
|
1819
1819
|
|
|
@@ -1832,4 +1832,4 @@ function HelpCenterListAuthed({
|
|
|
1832
1832
|
|
|
1833
1833
|
|
|
1834
1834
|
exports.useTicketActions = useTicketActions; exports.mapTicketActionError = mapTicketActionError; exports.useTicketsList = useTicketsList; exports.TicketOpenForm = TicketOpenForm; exports.useTicketEngagements = useTicketEngagements; exports.TicketLinkedDeliveryCard = TicketLinkedDeliveryCard; exports.TicketReplyComposer = TicketReplyComposer; exports.TicketDetailDrawer = TicketDetailDrawer; exports.TicketRow = TicketRow; exports.TicketCenter = TicketCenter; exports.HelpCenterCard = HelpCenterCard; exports.HelpCenterCreateFormSkeleton = HelpCenterCreateFormSkeleton; exports.HelpCenterCreateForm = HelpCenterCreateForm; exports.HelpCenterList = HelpCenterList;
|
|
1835
|
-
//# sourceMappingURL=chunk-
|
|
1835
|
+
//# sourceMappingURL=chunk-KQZMM3QJ.cjs.map
|