Files
2026-02-02 18:19:00 +00:00

200 lines
6.1 KiB
TypeScript

'use client';
import { useState, useEffect, useCallback } from 'react';
import TreeNode from './TreeNode';
import { taxonomy, getDomainSubcodeCount, getSubcodeCount } from '@/lib/taxonomy/data';
import type { SelectedSubcode } from '@/lib/taxonomy/types';
interface TaxonomyTreeProps {
searchQuery: string;
searchResults: {
domains: string[];
categories: string[];
subcodes: string[];
};
selectedSubcode: SelectedSubcode | null;
onSelectSubcode: (subcode: SelectedSubcode | null) => void;
}
export default function TaxonomyTree({
searchQuery,
searchResults,
selectedSubcode,
onSelectSubcode,
}: TaxonomyTreeProps) {
const [expandedDomains, setExpandedDomains] = useState<Set<string>>(new Set());
const [expandedCategories, setExpandedCategories] = useState<Set<string>>(new Set());
// Auto-expand nodes when search results change
useEffect(() => {
if (searchQuery) {
const domainsToExpand = new Set<string>();
const categoriesToExpand = new Set<string>();
// Expand domains that have matches
searchResults.domains.forEach((d) => domainsToExpand.add(d));
// Expand parent domains for matched categories
searchResults.categories.forEach((c) => {
domainsToExpand.add(c[0]);
categoriesToExpand.add(c);
});
// Expand parent domains and categories for matched subcodes
searchResults.subcodes.forEach((s) => {
const domainKey = s[0];
const categoryKey = s.slice(0, 2);
domainsToExpand.add(domainKey);
categoriesToExpand.add(categoryKey);
});
setExpandedDomains(domainsToExpand);
setExpandedCategories(categoriesToExpand);
}
}, [searchQuery, searchResults]);
const toggleDomain = useCallback((domainKey: string) => {
setExpandedDomains((prev) => {
const next = new Set(prev);
if (next.has(domainKey)) {
next.delete(domainKey);
} else {
next.add(domainKey);
}
return next;
});
}, []);
const toggleCategory = useCallback((categoryKey: string) => {
setExpandedCategories((prev) => {
const next = new Set(prev);
if (next.has(categoryKey)) {
next.delete(categoryKey);
} else {
next.add(categoryKey);
}
return next;
});
}, []);
const handleSelectSubcode = (
subcodeKey: string,
domainKey: string,
domainName: string,
categoryKey: string,
categoryName: string,
subcode: SelectedSubcode['subcode']
) => {
onSelectSubcode({
code: subcodeKey,
domainKey,
domainName,
categoryKey,
categoryName,
subcode,
});
};
const isSubcodeSelected = (subcodeKey: string) => {
return selectedSubcode?.code === subcodeKey;
};
const isSearchMatch = (key: string) => {
if (!searchQuery) return false;
return (
searchResults.domains.includes(key) ||
searchResults.categories.includes(key) ||
searchResults.subcodes.includes(key)
);
};
// Filter to show only matches when searching
const shouldShowDomain = (domainKey: string) => {
if (!searchQuery) return true;
// Show domain if it matches or any of its children match
if (searchResults.domains.includes(domainKey)) return true;
if (searchResults.categories.some((c) => c.startsWith(domainKey))) return true;
if (searchResults.subcodes.some((s) => s.startsWith(domainKey))) return true;
return false;
};
const shouldShowCategory = (categoryKey: string) => {
if (!searchQuery) return true;
if (searchResults.categories.includes(categoryKey)) return true;
if (searchResults.subcodes.some((s) => s.startsWith(categoryKey))) return true;
return false;
};
const shouldShowSubcode = (subcodeKey: string) => {
if (!searchQuery) return true;
return searchResults.subcodes.includes(subcodeKey);
};
return (
<div className="flex flex-col gap-0.5">
{Object.entries(taxonomy.domains).map(([domainKey, domain]) => {
if (!shouldShowDomain(domainKey)) return null;
return (
<TreeNode
key={domainKey}
code={domainKey}
name={domain.name}
count={getDomainSubcodeCount(domainKey)}
isExpanded={expandedDomains.has(domainKey)}
level="domain"
domainKey={domainKey}
onToggle={() => toggleDomain(domainKey)}
searchMatch={isSearchMatch(domainKey)}
>
{Object.entries(domain.categories).map(([categoryKey, category]) => {
if (!shouldShowCategory(categoryKey)) return null;
return (
<TreeNode
key={categoryKey}
code={categoryKey}
name={category.name}
count={getSubcodeCount(categoryKey)}
isExpanded={expandedCategories.has(categoryKey)}
level="category"
domainKey={domainKey}
onToggle={() => toggleCategory(categoryKey)}
searchMatch={isSearchMatch(categoryKey)}
>
{Object.entries(category.subcodes).map(([subcodeKey, subcode]) => {
if (!shouldShowSubcode(subcodeKey)) return null;
return (
<TreeNode
key={subcodeKey}
code={subcodeKey}
name={subcode.name}
isLeaf
isSelected={isSubcodeSelected(subcodeKey)}
level="subcode"
domainKey={domainKey}
onClick={() =>
handleSelectSubcode(
subcodeKey,
domainKey,
domain.name,
categoryKey,
category.name,
subcode
)
}
searchMatch={isSearchMatch(subcodeKey)}
/>
);
})}
</TreeNode>
);
})}
</TreeNode>
);
})}
</div>
);
}