import { useMemo } from 'react'; import type { NamespaceTree as Tree } from '@/api/types'; import type { TranslationStatus } from '@/api/types'; import { STATUS_META } from '@/components/Status'; const FILTERS: { value: TranslationStatus | ''; label: string }[] = [ { value: '', label: 'Toutes' }, { value: 'untranslated', label: STATUS_META.untranslated.label }, { value: 'needs_review', label: STATUS_META.needs_review.label }, { value: 'draft', label: STATUS_META.draft.label }, { value: 'translated', label: STATUS_META.translated.label }, { value: 'reviewed', label: STATUS_META.reviewed.label }, ]; interface Props { tree: Tree | undefined; selected: string; status: TranslationStatus | ''; unassigned: boolean; total: number; onSelectNamespace: (path: string | null) => void; // Statut et bac « non assignées » partent ENSEMBLE : ce sont deux facettes // du même filtre, et les envoyer en deux appels séparés faisait que le // second écrasait le premier. onSelectFilter: (next: { status: TranslationStatus | ''; unassigned: boolean }) => void; } export function NamespaceTree({ tree, selected, status, unassigned, total, onSelectNamespace, onSelectFilter, }: Props) { // Le serveur renvoie un compteur PROPRE à chaque namespace. L'agrégation par // branche se fait ici : le client possède déjà l'arbre entier, la calculer // côté serveur coûterait une requête récursive pour rien. const nodes = useMemo(() => { const list = tree?.namespaces ?? []; return list.map((node) => { const subtotal = list .filter((other) => other.path === node.path || other.path.startsWith(`${node.path}.`)) .reduce((sum, other) => sum + other.actionable, 0); return { ...node, subtotal }; }); }, [tree]); return ( ); }