// faq-article-page.jsx → qa-page.jsx (renamed in v1.36.0 for the question-answer CPT) // Pattern: a single question is answered authoritatively on one page, // surrounded by structured supporting context (key points, applicability, // related topics, follow-up Q&A, assessment CTA). // // Data flow (v1.36.0+): // 1. PHP renders the page via templates/template-claude-page.php // 2. JSX root mounts // 3. QaSingleApp extracts the post slug from window.location.pathname // 4. Fetches /wp-json/claude-pages/v1/qa/{slug} (see includes/rest/qa-endpoints.php) // 5. Passes the returned data object as `data` prop to and // // The design's hardcoded FAQ_DATA constant has been removed in favor of REST-driven data. // The shape returned by the REST endpoint matches what these components expect: // { category, breadcrumb[], updated, readTime, question, tldr, answer[], // keyPoints[], whenApplies[], related[{label,href}], followups[{q,a}], assessmentBlurb } // ============================================================ // FAQ Article Hero — Wise Blue with subtle radial light // Mirrors the dark-hero pattern from Solutions pages but // designed for a single question to read clearly as the H1. // ============================================================ const FaqArticleHero = ({ data }) => (
{/* Subtle radial glow — references illumination, very restrained */}
); const MetaItem = ({ label, value }) => (
{label} {value}
); // ============================================================ // FAQ Article Body — two-column layout // Left: article (Clear Answer → Key Points → When This Applies → Related Q&A → Assessment) // Right: sticky On-this-page TOC + Related Topics // ============================================================ const FaqArticleBody = ({ data }) => { const [open, setOpen] = React.useState(0); return (
{/* ============ ARTICLE COLUMN ============ */}
{/* ----- Clear Answer Explained ----- */}
{data.answer.map((p, i) => (

{p}

))}
{/* ----- Key Points ----- */}
    {data.keyPoints.map((kp, i) => (
  • {kp}
  • ))}
{/* ----- When This Applies ----- */}
    {data.whenApplies.map((w, i) => (
  • {w}
  • ))}
{/* ----- Related Questions (mini accordion) ----- */}
{data.followups.map((it, i) => { const isOpen = open === i; return (

{it.a}

); })}
{/* ============ SIDE RAIL ============ */}
); }; // Sub-section anchor + eyebrow header used inside the article const SectionAnchor = ({ id, eyebrow, title }) => (
{eyebrow}

{title}

); const Divider = () => (
); // ============================================================ // QaSingleApp — REST-driven root wrapper (added v1.36.0) // // Reads the post slug from window.location.pathname (URL shape: // /question-answer/{slug}/) and fetches the matching Q&A from // /wp-json/claude-pages/v1/qa/{slug}. While loading, renders a // minimal skeleton hero. On error renders a polite empty state. // Once resolved, hands the data object to and // . // // Defensive: any of the array/object fields may be missing if the // admin hasn't populated all ACF fields — the components fall back // gracefully (see in-component guards mirroring project-single-page). // ============================================================ const QaSingleApp = () => { const [state, setState] = React.useState({ loading: true, error: null, data: null }); React.useEffect(() => { // Slug comes from URL: /question-answer/{slug}/ → extract {slug} const path = window.location.pathname.replace(/\/$/, ''); const m = path.match(/\/question-answer\/([a-z0-9-]+)$/i); const slug = m ? m[1] : null; if (!slug) { setState({ loading: false, error: 'No Q&A slug in URL', data: null }); return; } const base = (window.GI_REST_BASE || '/wp-json/claude-pages/v1/').replace(/\/?$/, '/'); fetch(base + 'qa/' + encodeURIComponent(slug), { headers: { 'Accept': 'application/json' } }) .then(r => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }) .then(data => setState({ loading: false, error: null, data })) .catch(err => setState({ loading: false, error: String(err), data: null })); }, []); // Lucide re-init after content is rendered React.useEffect(() => { if (state.data && window.lucide) { window.lucide.createIcons({ attrs: { 'stroke-width': 1.5 } }); } }, [state.data]); if (state.loading) { return (
Loading…
); } if (state.error || !state.data) { return (

This Q&A isn't available.

The page you're looking for may have been moved or is being prepared. Return home.

); } return ( <> ); }; // Normalize REST response so missing ACF fields don't crash the components. // Every array field becomes `[]`, every string becomes `''`. The components // then naturally render nothing for empty sections. const normalizeQaData = (d) => ({ category: d.category || '', breadcrumb: Array.isArray(d.breadcrumb) ? d.breadcrumb : ['Resources', 'Q&A'], updated: d.updated || '', readTime: d.readTime || '', question: d.question || '', tldr: d.tldr || '', answer: Array.isArray(d.answer) ? d.answer : [], keyPoints: Array.isArray(d.keyPoints) ? d.keyPoints : [], whenApplies: Array.isArray(d.whenApplies) ? d.whenApplies : [], related: Array.isArray(d.related) ? d.related : [], followups: Array.isArray(d.followups) ? d.followups : [], assessmentBlurb: d.assessmentBlurb || '', }); Object.assign(window, { FaqArticleHero, FaqArticleBody, QaSingleApp });