import { useMemo, useState, Fragment } from 'react';
import { useTranslation } from 'react-i18next';
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from '@/components/ui/accordion';
import { useAppContext } from '@/hooks/useAppContext';
import { getFAQCategories, type FAQCategory, type FAQItem } from '@/lib/helpContent';
import { renderInlineMarkup } from '@/lib/helpMarkup';
import { cn } from '@/lib/utils';
// ── Component ─────────────────────────────────────────────────────────────────
interface HelpFAQSectionProps {
/** Show only these category IDs. Omit to show all. */
categories?: string[];
/** Show only these item IDs (across all categories). Omit to show all. */
items?: string[];
/** Hide category headings (useful when showing a single category or filtered items). */
hideHeadings?: boolean;
/** Additional class names for the wrapper. */
className?: string;
/**
* Rendering variant.
*
* - `'list'` (default): flat accordion list grouped by category. Used
* inside contextual settings pages where the FAQ is a small inline
* reference.
* - `'cards'`: each FAQ item renders as its own rounded card with a
* single-item accordion inside. Used by the About page so the FAQ
* reads as a designed section rather than an undifferentiated dump.
*/
variant?: 'list' | 'cards';
/**
* When `variant='cards'`, render a row of category-tab pills above the
* cards. Only one category is shown at a time. No effect on `'list'`.
*/
tabs?: boolean;
/**
* When `variant='list'`, choose between the default pill-tinted category
* heading (used by inline contextual FAQs in settings pages) and a
* quieter 'reference' tone used by the About page's `Need help?`
* section where the FAQ is the main content rather than a sidebar.
*/
listTone?: 'default' | 'reference';
}
/**
* Reusable FAQ accordion section.
*
* Renders FAQ items from `helpContent.ts` in collapsible accordions grouped by
* category. Accepts filter props so it can be dropped into any page to show a
* relevant subset of questions.
*
* @example
* // Full FAQ (legacy list layout)
*
*
* // About-page integrated FAQ (cards + category tabs)
*
*
* // Only payments questions (wallet settings page)
*
*
* // Specific questions (onboarding)
*
*/
export function HelpFAQSection({
categories,
items,
hideHeadings,
className,
variant = 'list',
tabs = false,
listTone = 'default',
}: HelpFAQSectionProps) {
const { config } = useAppContext();
const { i18n } = useTranslation();
// `getFAQCategories` resolves strings against `i18n.language` at call
// time. Capturing the language in a local lets us include it as a memo
// dep so a language switch re-runs the resolver.
const lng = i18n.language;
const filteredCategories = useMemo(() => {
let cats: FAQCategory[] = getFAQCategories(config.appName);
// Drop hidden categories from the default render. They still exist in
// the underlying template so `HelpTip` can look up individual items by
// ID, but they don't show up in the FAQ accordion.
if (!categories && !items) {
cats = cats.filter((c) => !c.hidden);
}
// Filter to specific categories
if (categories) {
cats = cats.filter((c) => categories.includes(c.id));
}
// Filter to specific items
if (items) {
cats = cats
.map((c) => ({
...c,
items: c.items.filter((i) => items.includes(i.id)),
}))
.filter((c) => c.items.length > 0);
}
return cats;
// `lng` is in the dep list to force re-resolution on a language switch.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [categories, items, config.appName, lng]);
// Tab state: first category is selected by default.
const [activeTab, setActiveTab] = useState(
filteredCategories[0]?.id ?? null,
);
if (filteredCategories.length === 0) return null;
// ── Card variant ─────────────────────────────────────────────────────────
if (variant === 'cards') {
const showTabs = tabs && filteredCategories.length > 1;
const visibleCategories = showTabs
? filteredCategories.filter((c) => c.id === activeTab)
: filteredCategories;
return (
);
}
function FAQAccordionItem({
item,
reference,
}: {
item: FAQItem;
reference?: boolean;
}) {
if (reference) {
// 'reference' mode: each Q&A reads as a substantial card-row with
// a left orange-accent rule that lights up on hover and open. Used
// by the About page where the FAQ is a first-class chapter rather
// than a sidebar.
return (
{/* Left accent rule: orange when open, transparent otherwise */}
{item.question}
{item.answer.map((paragraph, i) => (
))}
);
}
/**
* A single FAQ entry rendered as its own rounded card. Wraps a one-item
* Radix Accordion so the click-to-expand UX is preserved. Used by the
* About page's card-variant FAQ section.
*/
function FAQCard({ item }: { item: FAQItem }) {
return (