@flamingo-stack/openframe-frontend-core 0.0.238 → 0.0.239-snapshot.20260609201008

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/{chunk-M3I6ZPOM.js → chunk-APGMNHPY.js} +5 -6
  2. package/dist/chunk-APGMNHPY.js.map +1 -0
  3. package/dist/{chunk-DOM4KWZ7.cjs → chunk-DV3YZWGN.cjs} +6 -7
  4. package/dist/chunk-DV3YZWGN.cjs.map +1 -0
  5. package/dist/components/chevron-button.d.ts +3 -1
  6. package/dist/components/chevron-button.d.ts.map +1 -1
  7. package/dist/components/faq/faq-section.d.ts +1 -1
  8. package/dist/components/faq/faq-section.d.ts.map +1 -1
  9. package/dist/components/faq/index.cjs +2 -6
  10. package/dist/components/faq/index.cjs.map +1 -1
  11. package/dist/components/faq/index.d.ts +0 -1
  12. package/dist/components/faq/index.d.ts.map +1 -1
  13. package/dist/components/faq/index.js +3 -7
  14. package/dist/components/faq/json-ld.cjs +32 -0
  15. package/dist/components/faq/json-ld.cjs.map +1 -0
  16. package/dist/components/faq/{faq-json-ld.d.ts → json-ld.d.ts} +1 -1
  17. package/dist/components/faq/json-ld.d.ts.map +1 -0
  18. package/dist/components/faq/json-ld.js +29 -0
  19. package/dist/components/faq/json-ld.js.map +1 -0
  20. package/dist/components/faq-accordion.d.ts.map +1 -1
  21. package/dist/components/index.cjs +2 -6
  22. package/dist/components/index.cjs.map +1 -1
  23. package/dist/components/index.d.ts.map +1 -1
  24. package/dist/components/index.js +2 -6
  25. package/dist/components/index.js.map +1 -1
  26. package/package.json +7 -1
  27. package/src/components/chevron-button.tsx +5 -1
  28. package/src/components/faq/faq-section.tsx +2 -2
  29. package/src/components/faq/index.ts +11 -1
  30. package/src/components/faq-accordion.tsx +2 -1
  31. package/src/components/index.ts +6 -2
  32. package/dist/chunk-DOM4KWZ7.cjs.map +0 -1
  33. package/dist/chunk-M3I6ZPOM.js.map +0 -1
  34. package/dist/components/faq/faq-json-ld.d.ts.map +0 -1
  35. /package/src/components/faq/{faq-json-ld.ts → json-ld.ts} +0 -0
@@ -66,9 +66,10 @@ function FaqAccordion({ items, defaultOpenIds = [] }) {
66
66
  className: "flex w-full items-center justify-between px-6 md:px-8 py-6 text-left focus:outline-none transition-colors cursor-pointer",
67
67
  children: [
68
68
  /* @__PURE__ */ jsx("h3", { children: item.question }),
69
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", "aria-label": isOpen ? "Collapse question" : "Expand question", children: /* @__PURE__ */ jsx(
69
+ /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(
70
70
  ChevronButton,
71
71
  {
72
+ "aria-label": isOpen ? "Collapse question" : "Expand question",
72
73
  size: "md",
73
74
  isExpanded: isOpen,
74
75
  backgroundColor: "transparent",
@@ -93,7 +94,7 @@ function FaqAccordion({ items, defaultOpenIds = [] }) {
93
94
  }) });
94
95
  }
95
96
 
96
- // src/components/faq/faq-json-ld.ts
97
+ // src/components/faq/json-ld.ts
97
98
  var DEFAULT_NAME = "Frequently Asked Questions";
98
99
  var DEFAULT_DESCRIPTION = "Answers to common questions.";
99
100
  function baseFaqSchema(opts = {}) {
@@ -147,7 +148,7 @@ function groupBySection(faqs) {
147
148
  }
148
149
  function FaqSkeleton() {
149
150
  return /* @__PURE__ */ jsxs2("div", { className: "space-y-8 animate-pulse", children: [
150
- /* @__PURE__ */ jsx2("div", { className: "h-[48px] md:h-[56px] w-2/3 rounded bg-ods-border" }),
151
+ /* @__PURE__ */ jsx2("div", { className: "h-12 md:h-14 w-2/3 rounded bg-ods-border" }),
151
152
  /* @__PURE__ */ jsx2("div", { className: "rounded-3xl border border-ods-border overflow-hidden bg-ods-card divide-y divide-ods-border w-full", children: Array.from({ length: 8 }).map((_, idx) => /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between px-6 md:px-8 py-6", children: [
152
153
  /* @__PURE__ */ jsx2("div", { className: "h-6 w-5/6 rounded bg-ods-border" }),
153
154
  /* @__PURE__ */ jsx2("div", { className: "h-10 w-10 rounded-md bg-ods-border" })
@@ -200,8 +201,6 @@ function FaqSection({
200
201
 
201
202
  export {
202
203
  FaqAccordion,
203
- baseFaqSchema,
204
- buildFaqJsonLdFromFaqs,
205
204
  FaqSection
206
205
  };
207
- //# sourceMappingURL=chunk-M3I6ZPOM.js.map
206
+ //# sourceMappingURL=chunk-APGMNHPY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/faq-accordion.tsx","../src/components/faq/json-ld.ts","../src/components/faq/faq-section.tsx"],"sourcesContent":["\"use client\"\n\nimport React, { useRef, useState, useEffect, useCallback } from 'react'\nimport { ChevronButton } from './ui/chevron-button'\nimport { cn } from \"../utils/cn\"\n\nexport interface FaqItem {\n id: number | string\n question: string\n answer: string\n}\n\ninterface FaqAccordionProps {\n items: FaqItem[]\n defaultOpenIds?: (number | string)[]\n}\n\n// Utility to measure scrollHeight outside render cycle\nconst useMeasuredHeight = (isOpen: boolean) => {\n const ref = useRef<HTMLDivElement | null>(null)\n const [maxHeight, setMaxHeight] = useState<string>('0px')\n\n const measure = useCallback(() => {\n if (ref.current) {\n const height = ref.current.scrollHeight\n setMaxHeight(`${height}px`)\n }\n }, [])\n\n // Update height only when section is open\n useEffect(() => {\n if (isOpen) {\n measure()\n } else {\n setMaxHeight('0px')\n }\n }, [isOpen, measure])\n\n return { ref, maxHeight }\n}\n\nexport function FaqAccordion({ items, defaultOpenIds = [] }: FaqAccordionProps) {\n const [openSet, setOpenSet] = useState<Set<string | number>>(new Set(defaultOpenIds))\n\n const toggle = (id: string | number) => {\n setOpenSet(prev => {\n const next = new Set(prev)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }\n\n return (\n <div className=\"rounded-3xl border border-ods-border divide-y divide-ods-border bg-ods-card overflow-hidden\">\n {items.map(item => {\n const isOpen = openSet.has(item.id)\n const { ref, maxHeight } = useMeasuredHeight(isOpen)\n\n return (\n <div\n key={item.id}\n className={cn('group transition-colors hover:bg-[#1E1E1E]', isOpen ? 'bg-ods-bg' : 'bg-transparent')}\n >\n {/* Header */}\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => toggle(item.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggle(item.id);\n }\n }}\n aria-expanded={isOpen}\n className=\"flex w-full items-center justify-between px-6 md:px-8 py-6 text-left focus:outline-none transition-colors cursor-pointer\"\n >\n <h3>\n {item.question}\n </h3>\n <div className=\"flex-shrink-0\">\n <ChevronButton\n aria-label={isOpen ? 'Collapse question' : 'Expand question'}\n size=\"md\"\n isExpanded={isOpen}\n backgroundColor=\"transparent\"\n borderColor=\"#3A3A3A\"\n />\n </div>\n </div>\n {/* Content wrapper with max-height animation */}\n <div\n style={{ maxHeight, transition: 'max-height 0.35s ease-in-out, opacity 0.35s ease-in-out', opacity: isOpen ? 1 : 0 }}\n className=\"overflow-hidden group-hover:bg-[#1E1E1E]/30\"\n >\n <div ref={ref} className=\"px-6 md:px-8 pb-6 text-ods-text-primary text-h4\">\n {item.answer}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n )\n} ","/**\n * Pure FAQ JSON-LD builder. No React, no client-only deps — safe to import from\n * Server Components via the `./components/faq` subpath export. (Do NOT import\n * through the root `./components` barrel — that barrel is `\"use client\"` and\n * dragging it into a Server Component would force the client graph into the\n * server.)\n *\n * The hub used to harcode `name`/`description` here for OpenMSP; in the lib we\n * accept overrides so every embedder can supply its own platform branding.\n */\nimport type { Faq } from '../../types/faq'\n\nexport interface FaqSchemaOptions {\n name?: string\n description?: string\n url?: string\n}\n\nconst DEFAULT_NAME = 'Frequently Asked Questions'\nconst DEFAULT_DESCRIPTION =\n 'Answers to common questions.'\n\nexport function baseFaqSchema(opts: FaqSchemaOptions = {}) {\n return {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n name: opts.name ?? DEFAULT_NAME,\n description: opts.description ?? DEFAULT_DESCRIPTION,\n ...(opts.url ? { url: opts.url } : {}),\n } as const\n}\n\nexport function buildFaqJsonLdFromFaqs(faqs: Faq[], opts: FaqSchemaOptions = {}) {\n return {\n ...baseFaqSchema(opts),\n mainEntity: faqs.map((faq) => ({\n '@type': 'Question',\n name: faq.question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: faq.answer,\n },\n })),\n } as const\n}\n","\"use client\"\n\nimport React from 'react'\nimport type { Faq } from '../../types/faq'\nimport { FaqAccordion, type FaqItem } from '../faq-accordion'\nimport { useSelfFetch } from '../../hooks/use-self-fetch'\nimport { buildFaqJsonLdFromFaqs, type FaqSchemaOptions } from './json-ld'\n\nexport interface FaqSectionProps {\n /**\n * SSR hydrate. When provided, the hook skips the first client fetch (per\n * useSelfFetch contract). The consuming server page resolves FAQs then drills\n * them into this prop — the lib never re-fetches what the host already gated on.\n */\n initialFaqs?: Faq[]\n /** Both required together for entity-attached FAQs; partial → bare /api/faqs. */\n entityType?: string\n entityId?: number | string\n /**\n * Page heading. Pass null to render in embedded mode (no <h1>, error branch\n * suppresses any banner). React node so platforms can drill their local\n * <PageHeading> component without the lib referencing platform state.\n */\n heading?: React.ReactNode | null\n /** Inject FAQPage schema.org JSON-LD as a <script>. Off by default so embeds\n * don't emit duplicate schema. */\n emitJsonLd?: boolean\n /** Overrides for the JSON-LD's name/description/url. */\n jsonLd?: FaqSchemaOptions\n className?: string\n}\n\nconst DEFAULT_HEADING_TEXT = 'Frequently Asked Questions'\n\nfunction buildFaqsUrl(entityType?: string, entityId?: number | string): string {\n if (!entityType || entityId === undefined || entityId === null || entityId === '') {\n return '/api/faqs'\n }\n const qs = new URLSearchParams({ entityType, entityId: String(entityId) })\n return `/api/faqs?${qs.toString()}`\n}\n\nfunction groupBySection(faqs: Faq[]): Array<{ section: string | null; items: FaqItem[] }> {\n const map = new Map<string, FaqItem[]>()\n for (const faq of faqs) {\n const key = faq.section || ''\n let arr = map.get(key)\n if (!arr) {\n arr = []\n map.set(key, arr)\n }\n arr.push({ id: faq.id, question: faq.question, answer: faq.answer })\n }\n return Array.from(map.entries()).map(([section, items]) => ({\n section: section || null,\n items,\n }))\n}\n\nfunction FaqSkeleton() {\n return (\n <div className=\"space-y-8 animate-pulse\">\n <div className=\"h-12 md:h-14 w-2/3 rounded bg-ods-border\" />\n <div className=\"rounded-3xl border border-ods-border overflow-hidden bg-ods-card divide-y divide-ods-border w-full\">\n {Array.from({ length: 8 }).map((_, idx) => (\n <div key={idx} className=\"flex items-center justify-between px-6 md:px-8 py-6\">\n <div className=\"h-6 w-5/6 rounded bg-ods-border\" />\n <div className=\"h-10 w-10 rounded-md bg-ods-border\" />\n </div>\n ))}\n </div>\n </div>\n )\n}\n\ninterface FaqsResponse {\n faqs: Faq[]\n}\n\n/**\n * Generic, embeddable FAQ display surface.\n *\n * - Standalone /faqs page: pass `initialFaqs` from the server + `heading` +\n * `emitJsonLd` with `jsonLd` overrides for SEO.\n * - Per-entity embed: pass `entityType` + `entityId` (no `initialFaqs`); the\n * hook self-fetches and the public /api/faqs endpoint picks override-vs-fallback.\n *\n * CONTRACT: the consuming app MUST implement `GET /api/faqs`. Without it,\n * useSelfFetch reports `error:true` (logged once); in embedded mode (heading\n * === null) this component renders nothing so the host page isn't disfigured.\n */\nexport function FaqSection({\n initialFaqs,\n entityType,\n entityId,\n heading,\n emitJsonLd = false,\n jsonLd,\n className,\n}: FaqSectionProps) {\n const url = buildFaqsUrl(entityType, entityId)\n const initialData = initialFaqs ? { faqs: initialFaqs } : undefined\n const { data, isLoading, error } = useSelfFetch<FaqsResponse>(url, { initialData })\n\n const faqs = data?.faqs ?? []\n const showHeading = heading !== null\n const headingNode =\n heading === undefined\n ? <h1 className=\"text-h1 tracking-[-0.04em] text-ods-text-primary\">{DEFAULT_HEADING_TEXT}</h1>\n : heading\n\n const groups = groupBySection(faqs)\n\n // Embedded mode (no heading): degrade silently on error.\n if (error && !showHeading) {\n return null\n }\n\n if (isLoading && faqs.length === 0) {\n return (\n <main className={className ?? 'bg-ods-bg'}>\n <div className=\"max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto\">\n <FaqSkeleton />\n </div>\n </main>\n )\n }\n\n if (error && faqs.length === 0) {\n return (\n <main className={className ?? 'bg-ods-bg flex items-center justify-center py-20'}>\n <p className=\"text-ods-text-primary font-medium\">Failed to load FAQs. Please try again later.</p>\n </main>\n )\n }\n\n const schema = emitJsonLd ? buildFaqJsonLdFromFaqs(faqs, jsonLd) : null\n\n return (\n <>\n <main className={className ?? 'bg-ods-bg'}>\n <div className=\"max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto space-y-10\">\n {showHeading && headingNode}\n {groups.map(({ section, items }) => (\n <div key={section || 'default'} className=\"space-y-4\">\n {section && (\n <h2 className=\"text-h2 tracking-[-0.04em] text-ods-text-primary mb-3 md:mb-4\">\n {section}\n </h2>\n )}\n <FaqAccordion items={items} />\n </div>\n ))}\n </div>\n </main>\n {schema && (\n <script\n type=\"application/ld+json\"\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n )}\n </>\n )\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
@@ -66,9 +66,10 @@ function FaqAccordion({ items, defaultOpenIds = [] }) {
66
66
  className: "flex w-full items-center justify-between px-6 md:px-8 py-6 text-left focus:outline-none transition-colors cursor-pointer",
67
67
  children: [
68
68
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { children: item.question }),
69
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex-shrink-0", "aria-label": isOpen ? "Collapse question" : "Expand question", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
69
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex-shrink-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
70
70
  _chunkFSS2MIEAcjs.ChevronButton,
71
71
  {
72
+ "aria-label": isOpen ? "Collapse question" : "Expand question",
72
73
  size: "md",
73
74
  isExpanded: isOpen,
74
75
  backgroundColor: "transparent",
@@ -93,7 +94,7 @@ function FaqAccordion({ items, defaultOpenIds = [] }) {
93
94
  }) });
94
95
  }
95
96
 
96
- // src/components/faq/faq-json-ld.ts
97
+ // src/components/faq/json-ld.ts
97
98
  var DEFAULT_NAME = "Frequently Asked Questions";
98
99
  var DEFAULT_DESCRIPTION = "Answers to common questions.";
99
100
  function baseFaqSchema(opts = {}) {
@@ -147,7 +148,7 @@ function groupBySection(faqs) {
147
148
  }
148
149
  function FaqSkeleton() {
149
150
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-8 animate-pulse", children: [
150
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-[48px] md:h-[56px] w-2/3 rounded bg-ods-border" }),
151
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-12 md:h-14 w-2/3 rounded bg-ods-border" }),
151
152
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "rounded-3xl border border-ods-border overflow-hidden bg-ods-card divide-y divide-ods-border w-full", children: Array.from({ length: 8 }).map((_, idx) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center justify-between px-6 md:px-8 py-6", children: [
152
153
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-6 w-5/6 rounded bg-ods-border" }),
153
154
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-10 w-10 rounded-md bg-ods-border" })
@@ -201,7 +202,5 @@ function FaqSection({
201
202
 
202
203
 
203
204
 
204
-
205
-
206
- exports.FaqAccordion = FaqAccordion; exports.baseFaqSchema = baseFaqSchema; exports.buildFaqJsonLdFromFaqs = buildFaqJsonLdFromFaqs; exports.FaqSection = FaqSection;
207
- //# sourceMappingURL=chunk-DOM4KWZ7.cjs.map
205
+ exports.FaqAccordion = FaqAccordion; exports.FaqSection = FaqSection;
206
+ //# sourceMappingURL=chunk-DV3YZWGN.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-DV3YZWGN.cjs","../src/components/faq-accordion.tsx","../src/components/faq/json-ld.ts","../src/components/faq/faq-section.tsx"],"names":["jsx"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-DV3YZWGN.cjs","sourcesContent":[null,"\"use client\"\n\nimport React, { useRef, useState, useEffect, useCallback } from 'react'\nimport { ChevronButton } from './ui/chevron-button'\nimport { cn } from \"../utils/cn\"\n\nexport interface FaqItem {\n id: number | string\n question: string\n answer: string\n}\n\ninterface FaqAccordionProps {\n items: FaqItem[]\n defaultOpenIds?: (number | string)[]\n}\n\n// Utility to measure scrollHeight outside render cycle\nconst useMeasuredHeight = (isOpen: boolean) => {\n const ref = useRef<HTMLDivElement | null>(null)\n const [maxHeight, setMaxHeight] = useState<string>('0px')\n\n const measure = useCallback(() => {\n if (ref.current) {\n const height = ref.current.scrollHeight\n setMaxHeight(`${height}px`)\n }\n }, [])\n\n // Update height only when section is open\n useEffect(() => {\n if (isOpen) {\n measure()\n } else {\n setMaxHeight('0px')\n }\n }, [isOpen, measure])\n\n return { ref, maxHeight }\n}\n\nexport function FaqAccordion({ items, defaultOpenIds = [] }: FaqAccordionProps) {\n const [openSet, setOpenSet] = useState<Set<string | number>>(new Set(defaultOpenIds))\n\n const toggle = (id: string | number) => {\n setOpenSet(prev => {\n const next = new Set(prev)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }\n\n return (\n <div className=\"rounded-3xl border border-ods-border divide-y divide-ods-border bg-ods-card overflow-hidden\">\n {items.map(item => {\n const isOpen = openSet.has(item.id)\n const { ref, maxHeight } = useMeasuredHeight(isOpen)\n\n return (\n <div\n key={item.id}\n className={cn('group transition-colors hover:bg-[#1E1E1E]', isOpen ? 'bg-ods-bg' : 'bg-transparent')}\n >\n {/* Header */}\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => toggle(item.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggle(item.id);\n }\n }}\n aria-expanded={isOpen}\n className=\"flex w-full items-center justify-between px-6 md:px-8 py-6 text-left focus:outline-none transition-colors cursor-pointer\"\n >\n <h3>\n {item.question}\n </h3>\n <div className=\"flex-shrink-0\">\n <ChevronButton\n aria-label={isOpen ? 'Collapse question' : 'Expand question'}\n size=\"md\"\n isExpanded={isOpen}\n backgroundColor=\"transparent\"\n borderColor=\"#3A3A3A\"\n />\n </div>\n </div>\n {/* Content wrapper with max-height animation */}\n <div\n style={{ maxHeight, transition: 'max-height 0.35s ease-in-out, opacity 0.35s ease-in-out', opacity: isOpen ? 1 : 0 }}\n className=\"overflow-hidden group-hover:bg-[#1E1E1E]/30\"\n >\n <div ref={ref} className=\"px-6 md:px-8 pb-6 text-ods-text-primary text-h4\">\n {item.answer}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n )\n} ","/**\n * Pure FAQ JSON-LD builder. No React, no client-only deps — safe to import from\n * Server Components via the `./components/faq` subpath export. (Do NOT import\n * through the root `./components` barrel — that barrel is `\"use client\"` and\n * dragging it into a Server Component would force the client graph into the\n * server.)\n *\n * The hub used to harcode `name`/`description` here for OpenMSP; in the lib we\n * accept overrides so every embedder can supply its own platform branding.\n */\nimport type { Faq } from '../../types/faq'\n\nexport interface FaqSchemaOptions {\n name?: string\n description?: string\n url?: string\n}\n\nconst DEFAULT_NAME = 'Frequently Asked Questions'\nconst DEFAULT_DESCRIPTION =\n 'Answers to common questions.'\n\nexport function baseFaqSchema(opts: FaqSchemaOptions = {}) {\n return {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n name: opts.name ?? DEFAULT_NAME,\n description: opts.description ?? DEFAULT_DESCRIPTION,\n ...(opts.url ? { url: opts.url } : {}),\n } as const\n}\n\nexport function buildFaqJsonLdFromFaqs(faqs: Faq[], opts: FaqSchemaOptions = {}) {\n return {\n ...baseFaqSchema(opts),\n mainEntity: faqs.map((faq) => ({\n '@type': 'Question',\n name: faq.question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: faq.answer,\n },\n })),\n } as const\n}\n","\"use client\"\n\nimport React from 'react'\nimport type { Faq } from '../../types/faq'\nimport { FaqAccordion, type FaqItem } from '../faq-accordion'\nimport { useSelfFetch } from '../../hooks/use-self-fetch'\nimport { buildFaqJsonLdFromFaqs, type FaqSchemaOptions } from './json-ld'\n\nexport interface FaqSectionProps {\n /**\n * SSR hydrate. When provided, the hook skips the first client fetch (per\n * useSelfFetch contract). The consuming server page resolves FAQs then drills\n * them into this prop — the lib never re-fetches what the host already gated on.\n */\n initialFaqs?: Faq[]\n /** Both required together for entity-attached FAQs; partial → bare /api/faqs. */\n entityType?: string\n entityId?: number | string\n /**\n * Page heading. Pass null to render in embedded mode (no <h1>, error branch\n * suppresses any banner). React node so platforms can drill their local\n * <PageHeading> component without the lib referencing platform state.\n */\n heading?: React.ReactNode | null\n /** Inject FAQPage schema.org JSON-LD as a <script>. Off by default so embeds\n * don't emit duplicate schema. */\n emitJsonLd?: boolean\n /** Overrides for the JSON-LD's name/description/url. */\n jsonLd?: FaqSchemaOptions\n className?: string\n}\n\nconst DEFAULT_HEADING_TEXT = 'Frequently Asked Questions'\n\nfunction buildFaqsUrl(entityType?: string, entityId?: number | string): string {\n if (!entityType || entityId === undefined || entityId === null || entityId === '') {\n return '/api/faqs'\n }\n const qs = new URLSearchParams({ entityType, entityId: String(entityId) })\n return `/api/faqs?${qs.toString()}`\n}\n\nfunction groupBySection(faqs: Faq[]): Array<{ section: string | null; items: FaqItem[] }> {\n const map = new Map<string, FaqItem[]>()\n for (const faq of faqs) {\n const key = faq.section || ''\n let arr = map.get(key)\n if (!arr) {\n arr = []\n map.set(key, arr)\n }\n arr.push({ id: faq.id, question: faq.question, answer: faq.answer })\n }\n return Array.from(map.entries()).map(([section, items]) => ({\n section: section || null,\n items,\n }))\n}\n\nfunction FaqSkeleton() {\n return (\n <div className=\"space-y-8 animate-pulse\">\n <div className=\"h-12 md:h-14 w-2/3 rounded bg-ods-border\" />\n <div className=\"rounded-3xl border border-ods-border overflow-hidden bg-ods-card divide-y divide-ods-border w-full\">\n {Array.from({ length: 8 }).map((_, idx) => (\n <div key={idx} className=\"flex items-center justify-between px-6 md:px-8 py-6\">\n <div className=\"h-6 w-5/6 rounded bg-ods-border\" />\n <div className=\"h-10 w-10 rounded-md bg-ods-border\" />\n </div>\n ))}\n </div>\n </div>\n )\n}\n\ninterface FaqsResponse {\n faqs: Faq[]\n}\n\n/**\n * Generic, embeddable FAQ display surface.\n *\n * - Standalone /faqs page: pass `initialFaqs` from the server + `heading` +\n * `emitJsonLd` with `jsonLd` overrides for SEO.\n * - Per-entity embed: pass `entityType` + `entityId` (no `initialFaqs`); the\n * hook self-fetches and the public /api/faqs endpoint picks override-vs-fallback.\n *\n * CONTRACT: the consuming app MUST implement `GET /api/faqs`. Without it,\n * useSelfFetch reports `error:true` (logged once); in embedded mode (heading\n * === null) this component renders nothing so the host page isn't disfigured.\n */\nexport function FaqSection({\n initialFaqs,\n entityType,\n entityId,\n heading,\n emitJsonLd = false,\n jsonLd,\n className,\n}: FaqSectionProps) {\n const url = buildFaqsUrl(entityType, entityId)\n const initialData = initialFaqs ? { faqs: initialFaqs } : undefined\n const { data, isLoading, error } = useSelfFetch<FaqsResponse>(url, { initialData })\n\n const faqs = data?.faqs ?? []\n const showHeading = heading !== null\n const headingNode =\n heading === undefined\n ? <h1 className=\"text-h1 tracking-[-0.04em] text-ods-text-primary\">{DEFAULT_HEADING_TEXT}</h1>\n : heading\n\n const groups = groupBySection(faqs)\n\n // Embedded mode (no heading): degrade silently on error.\n if (error && !showHeading) {\n return null\n }\n\n if (isLoading && faqs.length === 0) {\n return (\n <main className={className ?? 'bg-ods-bg'}>\n <div className=\"max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto\">\n <FaqSkeleton />\n </div>\n </main>\n )\n }\n\n if (error && faqs.length === 0) {\n return (\n <main className={className ?? 'bg-ods-bg flex items-center justify-center py-20'}>\n <p className=\"text-ods-text-primary font-medium\">Failed to load FAQs. Please try again later.</p>\n </main>\n )\n }\n\n const schema = emitJsonLd ? buildFaqJsonLdFromFaqs(faqs, jsonLd) : null\n\n return (\n <>\n <main className={className ?? 'bg-ods-bg'}>\n <div className=\"max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto space-y-10\">\n {showHeading && headingNode}\n {groups.map(({ section, items }) => (\n <div key={section || 'default'} className=\"space-y-4\">\n {section && (\n <h2 className=\"text-h2 tracking-[-0.04em] text-ods-text-primary mb-3 md:mb-4\">\n {section}\n </h2>\n )}\n <FaqAccordion items={items} />\n </div>\n ))}\n </div>\n </main>\n {schema && (\n <script\n type=\"application/ld+json\"\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n />\n )}\n </>\n )\n}\n"]}
@@ -6,7 +6,9 @@ interface ChevronButtonProps {
6
6
  backgroundColor?: string;
7
7
  borderColor?: string;
8
8
  onClick?: (e: React.MouseEvent) => void;
9
+ /** Forwarded to the inner <button> for assistive tech. */
10
+ 'aria-label'?: string;
9
11
  }
10
- export declare function ChevronButton({ isExpanded, className, size, backgroundColor, borderColor, onClick }: ChevronButtonProps): React.JSX.Element;
12
+ export declare function ChevronButton({ isExpanded, className, size, backgroundColor, borderColor, onClick, 'aria-label': ariaLabel, }: ChevronButtonProps): React.JSX.Element;
11
13
  export {};
12
14
  //# sourceMappingURL=chevron-button.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chevron-button.d.ts","sourceRoot":"","sources":["../../src/components/chevron-button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,UAAU,kBAAkB;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;CACzC;AAED,wBAAgB,aAAa,CAAC,EAC5B,UAAkB,EAClB,SAAS,EACT,IAAa,EACb,eAA+B,EAC/B,WAA2B,EAC3B,OAAO,EACR,EAAE,kBAAkB,qBA0CpB"}
1
+ {"version":3,"file":"chevron-button.d.ts","sourceRoot":"","sources":["../../src/components/chevron-button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,UAAU,kBAAkB;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACxC,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,aAAa,CAAC,EAC5B,UAAkB,EAClB,SAAS,EACT,IAAa,EACb,eAA+B,EAC/B,WAA2B,EAC3B,OAAO,EACP,YAAY,EAAE,SAAS,GACxB,EAAE,kBAAkB,qBA2CpB"}
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { Faq } from '../../types/faq';
3
- import { type FaqSchemaOptions } from './faq-json-ld';
3
+ import { type FaqSchemaOptions } from './json-ld';
4
4
  export interface FaqSectionProps {
5
5
  /**
6
6
  * SSR hydrate. When provided, the hook skips the first client fetch (per
@@ -1 +1 @@
1
- {"version":3,"file":"faq-section.d.ts","sourceRoot":"","sources":["../../../src/components/faq/faq-section.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AAG1C,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAE7E,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,GAAG,EAAE,CAAA;IACnB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI,CAAA;IAChC;uCACmC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,wDAAwD;IACxD,MAAM,CAAC,EAAE,gBAAgB,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAiDD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,EACzB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,OAAO,EACP,UAAkB,EAClB,MAAM,EACN,SAAS,GACV,EAAE,eAAe,4BAiEjB"}
1
+ {"version":3,"file":"faq-section.d.ts","sourceRoot":"","sources":["../../../src/components/faq/faq-section.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AAG1C,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAEzE,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,GAAG,EAAE,CAAA;IACnB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI,CAAA;IAChC;uCACmC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,wDAAwD;IACxD,MAAM,CAAC,EAAE,gBAAgB,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAiDD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,EACzB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,OAAO,EACP,UAAkB,EAClB,MAAM,EACN,SAAS,GACV,EAAE,eAAe,4BAiEjB"}
@@ -1,16 +1,12 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
2
 
3
3
 
4
-
5
-
6
- var _chunkDOM4KWZ7cjs = require('../../chunk-DOM4KWZ7.cjs');
4
+ var _chunkDV3YZWGNcjs = require('../../chunk-DV3YZWGN.cjs');
7
5
  require('../../chunk-T7AJBKZP.cjs');
8
6
  require('../../chunk-FSS2MIEA.cjs');
9
7
  require('../../chunk-FIG2RKZF.cjs');
10
8
  require('../../chunk-VRHGVLSL.cjs');
11
9
 
12
10
 
13
-
14
-
15
- exports.FaqSection = _chunkDOM4KWZ7cjs.FaqSection; exports.baseFaqSchema = _chunkDOM4KWZ7cjs.baseFaqSchema; exports.buildFaqJsonLdFromFaqs = _chunkDOM4KWZ7cjs.buildFaqJsonLdFromFaqs;
11
+ exports.FaqSection = _chunkDV3YZWGNcjs.FaqSection;
16
12
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/components/faq/index.cjs"],"names":[],"mappings":"AAAA,qFAAY;AACZ;AACE;AACA;AACA;AACF,4DAAiC;AACjC,oCAAiC;AACjC,oCAAiC;AACjC,oCAAiC;AACjC,oCAAiC;AACjC;AACE;AACA;AACA;AACF,sLAAC","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/components/faq/index.cjs"}
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/components/faq/index.cjs"],"names":[],"mappings":"AAAA,qFAAY;AACZ;AACE;AACF,4DAAiC;AACjC,oCAAiC;AACjC,oCAAiC;AACjC,oCAAiC;AACjC,oCAAiC;AACjC;AACE;AACF,kDAAC","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/components/faq/index.cjs"}
@@ -1,3 +1,2 @@
1
1
  export { FaqSection, type FaqSectionProps } from './faq-section';
2
- export { baseFaqSchema, buildFaqJsonLdFromFaqs, type FaqSchemaOptions } from './faq-json-ld';
3
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/faq/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/faq/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA"}
@@ -1,16 +1,12 @@
1
1
  "use client";
2
2
  import {
3
- FaqSection,
4
- baseFaqSchema,
5
- buildFaqJsonLdFromFaqs
6
- } from "../../chunk-M3I6ZPOM.js";
3
+ FaqSection
4
+ } from "../../chunk-APGMNHPY.js";
7
5
  import "../../chunk-L4LAAA5Q.js";
8
6
  import "../../chunk-LJGBFD52.js";
9
7
  import "../../chunk-XTCBRQN2.js";
10
8
  import "../../chunk-GGWZFCYS.js";
11
9
  export {
12
- FaqSection,
13
- baseFaqSchema,
14
- buildFaqJsonLdFromFaqs
10
+ FaqSection
15
11
  };
16
12
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ // src/components/faq/json-ld.ts
4
+ var DEFAULT_NAME = "Frequently Asked Questions";
5
+ var DEFAULT_DESCRIPTION = "Answers to common questions.";
6
+ function baseFaqSchema(opts = {}) {
7
+ return {
8
+ "@context": "https://schema.org",
9
+ "@type": "FAQPage",
10
+ name: opts.name ?? DEFAULT_NAME,
11
+ description: opts.description ?? DEFAULT_DESCRIPTION,
12
+ ...opts.url ? { url: opts.url } : {}
13
+ };
14
+ }
15
+ function buildFaqJsonLdFromFaqs(faqs, opts = {}) {
16
+ return {
17
+ ...baseFaqSchema(opts),
18
+ mainEntity: faqs.map((faq) => ({
19
+ "@type": "Question",
20
+ name: faq.question,
21
+ acceptedAnswer: {
22
+ "@type": "Answer",
23
+ text: faq.answer
24
+ }
25
+ }))
26
+ };
27
+ }
28
+
29
+ exports.baseFaqSchema = baseFaqSchema;
30
+ exports.buildFaqJsonLdFromFaqs = buildFaqJsonLdFromFaqs;
31
+ //# sourceMappingURL=json-ld.cjs.map
32
+ //# sourceMappingURL=json-ld.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/faq/json-ld.ts"],"names":[],"mappings":";;;AAkBA,IAAM,YAAA,GAAe,4BAAA;AACrB,IAAM,mBAAA,GACJ,8BAAA;AAEK,SAAS,aAAA,CAAc,IAAA,GAAyB,EAAC,EAAG;AACzD,EAAA,OAAO;AAAA,IACL,UAAA,EAAY,oBAAA;AAAA,IACZ,OAAA,EAAS,SAAA;AAAA,IACT,IAAA,EAAM,KAAK,IAAA,IAAQ,YAAA;AAAA,IACnB,WAAA,EAAa,KAAK,WAAA,IAAe,mBAAA;AAAA,IACjC,GAAI,KAAK,GAAA,GAAM,EAAE,KAAK,IAAA,CAAK,GAAA,KAAQ;AAAC,GACtC;AACF;AAEO,SAAS,sBAAA,CAAuB,IAAA,EAAa,IAAA,GAAyB,EAAC,EAAG;AAC/E,EAAA,OAAO;AAAA,IACL,GAAG,cAAc,IAAI,CAAA;AAAA,IACrB,UAAA,EAAY,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,MAC7B,OAAA,EAAS,UAAA;AAAA,MACT,MAAM,GAAA,CAAI,QAAA;AAAA,MACV,cAAA,EAAgB;AAAA,QACd,OAAA,EAAS,QAAA;AAAA,QACT,MAAM,GAAA,CAAI;AAAA;AACZ,KACF,CAAE;AAAA,GACJ;AACF","file":"json-ld.cjs","sourcesContent":["/**\n * Pure FAQ JSON-LD builder. No React, no client-only deps — safe to import from\n * Server Components via the `./components/faq` subpath export. (Do NOT import\n * through the root `./components` barrel — that barrel is `\"use client\"` and\n * dragging it into a Server Component would force the client graph into the\n * server.)\n *\n * The hub used to harcode `name`/`description` here for OpenMSP; in the lib we\n * accept overrides so every embedder can supply its own platform branding.\n */\nimport type { Faq } from '../../types/faq'\n\nexport interface FaqSchemaOptions {\n name?: string\n description?: string\n url?: string\n}\n\nconst DEFAULT_NAME = 'Frequently Asked Questions'\nconst DEFAULT_DESCRIPTION =\n 'Answers to common questions.'\n\nexport function baseFaqSchema(opts: FaqSchemaOptions = {}) {\n return {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n name: opts.name ?? DEFAULT_NAME,\n description: opts.description ?? DEFAULT_DESCRIPTION,\n ...(opts.url ? { url: opts.url } : {}),\n } as const\n}\n\nexport function buildFaqJsonLdFromFaqs(faqs: Faq[], opts: FaqSchemaOptions = {}) {\n return {\n ...baseFaqSchema(opts),\n mainEntity: faqs.map((faq) => ({\n '@type': 'Question',\n name: faq.question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: faq.answer,\n },\n })),\n } as const\n}\n"]}
@@ -36,4 +36,4 @@ export declare function buildFaqJsonLdFromFaqs(faqs: Faq[], opts?: FaqSchemaOpti
36
36
  readonly name: string;
37
37
  readonly description: string;
38
38
  };
39
- //# sourceMappingURL=faq-json-ld.d.ts.map
39
+ //# sourceMappingURL=json-ld.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-ld.d.ts","sourceRoot":"","sources":["../../../src/components/faq/json-ld.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAMD,wBAAgB,aAAa,CAAC,IAAI,GAAE,gBAAqB;;;;;;EAQxD;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,IAAI,GAAE,gBAAqB;;;;;;;;;;;;;;EAY9E"}
@@ -0,0 +1,29 @@
1
+ // src/components/faq/json-ld.ts
2
+ var DEFAULT_NAME = "Frequently Asked Questions";
3
+ var DEFAULT_DESCRIPTION = "Answers to common questions.";
4
+ function baseFaqSchema(opts = {}) {
5
+ return {
6
+ "@context": "https://schema.org",
7
+ "@type": "FAQPage",
8
+ name: opts.name ?? DEFAULT_NAME,
9
+ description: opts.description ?? DEFAULT_DESCRIPTION,
10
+ ...opts.url ? { url: opts.url } : {}
11
+ };
12
+ }
13
+ function buildFaqJsonLdFromFaqs(faqs, opts = {}) {
14
+ return {
15
+ ...baseFaqSchema(opts),
16
+ mainEntity: faqs.map((faq) => ({
17
+ "@type": "Question",
18
+ name: faq.question,
19
+ acceptedAnswer: {
20
+ "@type": "Answer",
21
+ text: faq.answer
22
+ }
23
+ }))
24
+ };
25
+ }
26
+
27
+ export { baseFaqSchema, buildFaqJsonLdFromFaqs };
28
+ //# sourceMappingURL=json-ld.js.map
29
+ //# sourceMappingURL=json-ld.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/faq/json-ld.ts"],"names":[],"mappings":";AAkBA,IAAM,YAAA,GAAe,4BAAA;AACrB,IAAM,mBAAA,GACJ,8BAAA;AAEK,SAAS,aAAA,CAAc,IAAA,GAAyB,EAAC,EAAG;AACzD,EAAA,OAAO;AAAA,IACL,UAAA,EAAY,oBAAA;AAAA,IACZ,OAAA,EAAS,SAAA;AAAA,IACT,IAAA,EAAM,KAAK,IAAA,IAAQ,YAAA;AAAA,IACnB,WAAA,EAAa,KAAK,WAAA,IAAe,mBAAA;AAAA,IACjC,GAAI,KAAK,GAAA,GAAM,EAAE,KAAK,IAAA,CAAK,GAAA,KAAQ;AAAC,GACtC;AACF;AAEO,SAAS,sBAAA,CAAuB,IAAA,EAAa,IAAA,GAAyB,EAAC,EAAG;AAC/E,EAAA,OAAO;AAAA,IACL,GAAG,cAAc,IAAI,CAAA;AAAA,IACrB,UAAA,EAAY,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,MAC7B,OAAA,EAAS,UAAA;AAAA,MACT,MAAM,GAAA,CAAI,QAAA;AAAA,MACV,cAAA,EAAgB;AAAA,QACd,OAAA,EAAS,QAAA;AAAA,QACT,MAAM,GAAA,CAAI;AAAA;AACZ,KACF,CAAE;AAAA,GACJ;AACF","file":"json-ld.js","sourcesContent":["/**\n * Pure FAQ JSON-LD builder. No React, no client-only deps — safe to import from\n * Server Components via the `./components/faq` subpath export. (Do NOT import\n * through the root `./components` barrel — that barrel is `\"use client\"` and\n * dragging it into a Server Component would force the client graph into the\n * server.)\n *\n * The hub used to harcode `name`/`description` here for OpenMSP; in the lib we\n * accept overrides so every embedder can supply its own platform branding.\n */\nimport type { Faq } from '../../types/faq'\n\nexport interface FaqSchemaOptions {\n name?: string\n description?: string\n url?: string\n}\n\nconst DEFAULT_NAME = 'Frequently Asked Questions'\nconst DEFAULT_DESCRIPTION =\n 'Answers to common questions.'\n\nexport function baseFaqSchema(opts: FaqSchemaOptions = {}) {\n return {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n name: opts.name ?? DEFAULT_NAME,\n description: opts.description ?? DEFAULT_DESCRIPTION,\n ...(opts.url ? { url: opts.url } : {}),\n } as const\n}\n\nexport function buildFaqJsonLdFromFaqs(faqs: Faq[], opts: FaqSchemaOptions = {}) {\n return {\n ...baseFaqSchema(opts),\n mainEntity: faqs.map((faq) => ({\n '@type': 'Question',\n name: faq.question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: faq.answer,\n },\n })),\n } as const\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"faq-accordion.d.ts","sourceRoot":"","sources":["../../src/components/faq-accordion.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmD,MAAM,OAAO,CAAA;AAIvE,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,UAAU,iBAAiB;IACzB,KAAK,EAAE,OAAO,EAAE,CAAA;IAChB,cAAc,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;CACrC;AA0BD,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,cAAmB,EAAE,EAAE,iBAAiB,qBA+D7E"}
1
+ {"version":3,"file":"faq-accordion.d.ts","sourceRoot":"","sources":["../../src/components/faq-accordion.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmD,MAAM,OAAO,CAAA;AAIvE,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,UAAU,iBAAiB;IACzB,KAAK,EAAE,OAAO,EAAE,CAAA;IAChB,cAAc,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;CACrC;AA0BD,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,cAAmB,EAAE,EAAE,iBAAiB,qBAgE7E"}