116 lines
3.5 KiB
TypeScript
116 lines
3.5 KiB
TypeScript
import { CategoryResponse } from '@/services/api/catalogService';
|
|
|
|
export type SearchCategoryOption = {
|
|
category_id: string;
|
|
slug: string;
|
|
name: string;
|
|
label: string;
|
|
};
|
|
|
|
export function isActiveCategory(category: CategoryResponse): boolean {
|
|
return category.is_active !== false;
|
|
}
|
|
|
|
export function sortCategories(list: CategoryResponse[]): CategoryResponse[] {
|
|
return [...list].sort((a, b) => {
|
|
const orderA = parseInt(String(a.sort_order || '0'), 10);
|
|
const orderB = parseInt(String(b.sort_order || '0'), 10);
|
|
if (orderA !== orderB) return orderA - orderB;
|
|
return (a.name || '').localeCompare(b.name || '', undefined, { sensitivity: 'base' });
|
|
});
|
|
}
|
|
|
|
export function normalizeCategories(list: CategoryResponse[] | null | undefined): CategoryResponse[] {
|
|
return sortCategories((list || []).filter(isActiveCategory));
|
|
}
|
|
|
|
function parentExists(category: CategoryResponse, all: CategoryResponse[]): boolean {
|
|
if (!category.parent_category_id) return false;
|
|
return all.some((item) => item.category_id === category.parent_category_id);
|
|
}
|
|
|
|
export function getTopLevelCategories(list: CategoryResponse[]): CategoryResponse[] {
|
|
const all = normalizeCategories(list);
|
|
return all.filter((category) => !parentExists(category, all));
|
|
}
|
|
|
|
export function getChildCategories(list: CategoryResponse[], parentId: string): CategoryResponse[] {
|
|
return normalizeCategories(list).filter((category) => category.parent_category_id === parentId);
|
|
}
|
|
|
|
export function getDescendantIds(list: CategoryResponse[], categoryId: string): string[] {
|
|
const ids = [categoryId];
|
|
const walk = (parentId: string) => {
|
|
getChildCategories(list, parentId).forEach((child) => {
|
|
ids.push(child.category_id);
|
|
walk(child.category_id);
|
|
});
|
|
};
|
|
walk(categoryId);
|
|
return ids;
|
|
}
|
|
|
|
export function findCategory(
|
|
list: CategoryResponse[],
|
|
slugOrId: string | null | undefined
|
|
): CategoryResponse | undefined {
|
|
if (!slugOrId) return undefined;
|
|
const needle = String(slugOrId).trim().toLowerCase();
|
|
if (!needle) return undefined;
|
|
return normalizeCategories(list).find(
|
|
(category) =>
|
|
category.category_id === slugOrId ||
|
|
category.slug.toLowerCase() === needle ||
|
|
category.name.toLowerCase() === needle
|
|
);
|
|
}
|
|
|
|
export type CategoryNavNode = CategoryResponse & {
|
|
children: CategoryResponse[];
|
|
};
|
|
|
|
export function getCategoryNavTree(list: CategoryResponse[]): CategoryNavNode[] {
|
|
return getTopLevelCategories(list).map((root) => ({
|
|
...root,
|
|
children: getChildCategories(list, root.category_id),
|
|
}));
|
|
}
|
|
|
|
export function getSearchCategoryOptions(list: CategoryResponse[]): SearchCategoryOption[] {
|
|
const result: SearchCategoryOption[] = [];
|
|
const topLevel = getTopLevelCategories(list);
|
|
|
|
topLevel.forEach((parent) => {
|
|
result.push({
|
|
category_id: parent.category_id,
|
|
slug: parent.slug,
|
|
name: parent.name,
|
|
label: parent.name,
|
|
});
|
|
|
|
const children = getChildCategories(list, parent.category_id);
|
|
children.forEach((child) => {
|
|
result.push({
|
|
category_id: child.category_id,
|
|
slug: child.slug,
|
|
name: child.name,
|
|
label: `${parent.name} → ${child.name}`,
|
|
});
|
|
});
|
|
});
|
|
|
|
// Include any orphan active categories as well
|
|
const includedIds = new Set(result.map((r) => r.category_id));
|
|
normalizeCategories(list).forEach((cat) => {
|
|
if (!includedIds.has(cat.category_id)) {
|
|
result.push({
|
|
category_id: cat.category_id,
|
|
slug: cat.slug,
|
|
name: cat.name,
|
|
label: cat.name,
|
|
});
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|