'use client'; import { useMemo, useState } from 'react'; import Link from 'next/link'; import { Search, X } from 'lucide-react'; import type { LucideIcon } from 'lucide-react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; export interface AdminSection { href: string; label: string; description: string; icon: LucideIcon; } export interface AdminGroup { title: string; description: string; sections: AdminSection[]; } interface AdminSectionsBrowserProps { portSlug: string; groups: AdminGroup[]; } /** * Searchable index of admin settings cards. The unfiltered view renders the * grouped grid (Access / Configuration / Content / …); typing in the search * input collapses every section into a flat result list of matching cards. * * Match is substring against label + description + group title so a search * for "tax" finds Document Templates (description mentions tax-id mergefield) * as well as ID fields, without needing perfect spelling of the label. */ export function AdminSectionsBrowser({ portSlug, groups }: AdminSectionsBrowserProps) { const [query, setQuery] = useState(''); const q = query.trim().toLowerCase(); // Flatten + filter when there's an active query; otherwise let the grouped // view render. The grouped view is also memoised because the section count // is large (30+) and the JSX otherwise rebuilds on every keystroke. const filteredMatches = useMemo(() => { if (!q) return null; const matches: Array = []; for (const g of groups) { for (const s of g.sections) { const hay = `${s.label} ${s.description} ${g.title}`.toLowerCase(); if (hay.includes(q)) matches.push({ ...s, groupTitle: g.title }); } } return matches; }, [q, groups]); return (
setQuery(e.target.value)} className="h-9 pl-9 pr-9" /> {query ? ( ) : null}
{filteredMatches ? ( filteredMatches.length === 0 ? (

No settings match "{query}".

) : (

{filteredMatches.length} match{filteredMatches.length === 1 ? '' : 'es'}

{filteredMatches.map((s) => ( ))}
) ) : ( groups.map((group) => (

{group.title}

{group.description}

{group.sections.map((s) => ( ))}
)) )}
); } function SectionCard({ portSlug, section, groupTitle, }: { portSlug: string; section: AdminSection; /** Optional "from group X" tag for search-result mode. */ groupTitle?: string; }) { const Icon = section.icon; return (
{section.label} {groupTitle ? (

{groupTitle}

) : null}
{section.description}
); }