import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { ArrowLeft, Search, Printer, ChevronsDownUp, ChevronsUpDown } from "lucide-react"; import { useCategoryTaxonomy } from "../hooks/useCategoryTaxonomy"; import CategoryTaxonomyTree, { TAXONOMY_COLLAPSE_ACCESSORS, } from "../components/categories/CategoryTaxonomyTree"; import type { TaxonomyNode } from "../services/categoryTaxonomyService"; import { useCollapsibleGroups } from "../hooks/useCollapsibleGroups"; function countNodes(nodes: TaxonomyNode[]): { roots: number; subcategories: number; leaves: number; } { let roots = 0; let subcategories = 0; let leaves = 0; for (const root of nodes) { roots += 1; for (const child of root.children) { if (child.children.length === 0) { // direct leaf under a root (rare but possible) leaves += 1; } else { subcategories += 1; for (const leaf of child.children) { if (leaf.children.length === 0) leaves += 1; else subcategories += 1; } } } } return { roots, subcategories, leaves }; } // Flattens the taxonomy to a single array so the hook's bulk ops // (expandAll/collapseAll/allExpanded/groupCount) can walk every parent at any depth. function flattenNodes(nodes: TaxonomyNode[]): TaxonomyNode[] { const flat: TaxonomyNode[] = []; const walk = (n: TaxonomyNode) => { flat.push(n); n.children.forEach(walk); }; nodes.forEach(walk); return flat; } export default function CategoriesStandardGuidePage() { const { t } = useTranslation(); const { taxonomy } = useCategoryTaxonomy(); const [search, setSearch] = useState(""); // State machine only (issue #290): in-memory (storageKey null), collapsed by // default so the guide opens on roots — the previous local Set behaviour. const groups = useCollapsibleGroups(null, TAXONOMY_COLLAPSE_ACCESSORS, { defaultExpanded: false, }); const counts = useMemo(() => countNodes(taxonomy.roots), [taxonomy.roots]); const total = counts.roots + counts.subcategories + counts.leaves; // Flattened nodes for the bulk ops (expand/collapse all, allExpanded). const flatNodes = useMemo(() => flattenNodes(taxonomy.roots), [taxonomy.roots]); const handleExpandAll = () => groups.expandAll(flatNodes); const handleCollapseAll = () => groups.collapseAll(flatNodes); const handlePrint = () => { // window.print() opens the browser print dialog; @media print rules strip chrome. window.print(); }; // Correct "all expanded" test (issue #290): every collapsible group must be open, // not merely "at least one node open" (the old expanded.size > 0 bug, which // flipped the button to "Collapse all" after a single node was expanded). const allExpanded = groups.allExpanded(flatNodes); return (
{/* Back link (hidden in print) */}
{t("categoriesSeed.guidePage.backToSettings")}
{/* Header */}

{t("categoriesSeed.guidePage.title")}

{t("categoriesSeed.guidePage.subtitle")}

{/* Intro card */}

{t("categoriesSeed.guidePage.intro.title")}

{t("categoriesSeed.guidePage.intro.body")}

{/* Counter + toolbar */}

{t("categoriesSeed.guidePage.counter", { roots: counts.roots, subcategories: counts.subcategories, leaves: counts.leaves, total, })}

{/* Toolbar: search + actions (hidden in print) */}
{/* Tree */}
); }