Initial commit - WhyRating Engine (Google Reviews Scraper)
This commit is contained in:
150
web/lib/categories.ts
Normal file
150
web/lib/categories.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
// 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';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user