Files
whyrating-engine-legacy/web/lib/categories.ts
2026-02-02 18:19:00 +00:00

151 lines
3.4 KiB
TypeScript

// Category types and utilities
export interface Category {
id: number;
name: string;
slug: string;
path: string;
level: number;
parent_id: number | null;
category_count: number;
children?: Category[];
}
export interface CategoryTreeNode {
id: string;
name: string;
children?: CategoryTreeNode[];
data?: Category;
}
export interface CategoryStats {
total: number;
sectors: number;
business_types: number;
sub_categories: number;
leaf_categories: number;
}
// Convert flat categories to tree structure
export function buildCategoryTree(categories: Category[]): CategoryTreeNode[] {
const map = new Map<string, CategoryTreeNode>();
const roots: CategoryTreeNode[] = [];
// First pass: create all nodes
for (const cat of categories) {
map.set(cat.path, {
id: cat.path,
name: cat.name,
children: [],
data: cat,
});
}
// Second pass: link children to parents
for (const cat of categories) {
const node = map.get(cat.path)!;
const pathParts = cat.path.split('.');
if (pathParts.length === 1) {
// Root node
roots.push(node);
} else {
// Find parent
const parentPath = pathParts.slice(0, -1).join('.');
const parent = map.get(parentPath);
if (parent) {
parent.children!.push(node);
} else {
// Parent not found, add as root
roots.push(node);
}
}
}
// Sort children alphabetically
const sortChildren = (nodes: CategoryTreeNode[]) => {
nodes.sort((a, b) => a.name.localeCompare(b.name));
for (const node of nodes) {
if (node.children && node.children.length > 0) {
sortChildren(node.children);
}
}
};
sortChildren(roots);
return roots;
}
// Convert to react-d3-tree format
export function toD3Tree(nodes: CategoryTreeNode[]): any {
return nodes.map((node) => ({
name: node.name,
attributes: {
level: node.data?.level,
count: node.data?.category_count,
},
children: node.children && node.children.length > 0
? toD3Tree(node.children)
: undefined,
}));
}
// Get breadcrumb path for a category
export function getCategoryBreadcrumb(path: string, categories: Category[]): Category[] {
const parts = path.split('.');
const breadcrumb: Category[] = [];
for (let i = 1; i <= parts.length; i++) {
const partialPath = parts.slice(0, i).join('.');
const cat = categories.find((c) => c.path === partialPath);
if (cat) {
breadcrumb.push(cat);
}
}
return breadcrumb;
}
// Search categories
export function searchCategories(categories: Category[], query: string): Category[] {
const lowerQuery = query.toLowerCase();
return categories.filter(
(cat) =>
cat.name.toLowerCase().includes(lowerQuery) ||
cat.path.toLowerCase().includes(lowerQuery)
);
}
// Get level name
export function getLevelName(level: number): string {
switch (level) {
case 1:
return 'Sector';
case 2:
return 'Business Type';
case 3:
return 'Sub-category';
case 4:
return 'Category';
default:
return 'Unknown';
}
}
// Get level color
export function getLevelColor(level: number): string {
switch (level) {
case 1:
return 'bg-blue-500';
case 2:
return 'bg-green-500';
case 3:
return 'bg-yellow-500';
case 4:
return 'bg-purple-500';
default:
return 'bg-gray-500';
}
}