// lib/api/menu-api.ts import { fetchGraphQL } from './graphql-client.js'; export interface MenuItem { id: string; databaseId: number; uri: string; url: string; order: number; label: string; parentId: string | null; target: string; cssClasses: string[]; description: string; menuItemColor?: string | null; childItems?: { nodes: MenuItem[]; }; } export interface Menu { id: string; databaseId: number; name: string; slug: string; locations: string[]; menuItems: { nodes: MenuItem[]; }; } export type MenuIdentifier = | { id: number } // По ID меню | { location: string } // По локации | { slug: string } // По слагу | { name: string }; // По имени /** * Получить меню по идентификатору */ export async function fetchMenu(identifier: MenuIdentifier): Promise { try { // Определяем тип запроса на основе переданного идентификатора if ('id' in identifier) { return await fetchMenuById(identifier.id); } if ('location' in identifier) { return await fetchMenuByLocation(identifier.location); } if ('slug' in identifier) { return await fetchMenuBySlug(identifier.slug); } if ('name' in identifier) { return await fetchMenuByName(identifier.name); } return null; } catch (error) { console.error('Error fetching menu:', error); return null; } } /** * Получить меню по ID (самый надежный способ) */ async function fetchMenuById(id: number): Promise { const query = ` query GetMenuById($id: ID!) { menu(id: $id, idType: DATABASE_ID) { id databaseId name slug locations menuItems(first: 100) { nodes { id databaseId uri url order label parentId target cssClasses description menuItemColor childItems(first: 50) { nodes { id databaseId label uri url order menuItemColor } } } } } } `; const variables = { id }; const data = await fetchGraphQL(query, variables); if (data?.menu) { return { ...data.menu, menuItems: data.menu.menuItems || { nodes: [] } }; } return null; } /** * Получить меню по локации */ async function fetchMenuByLocation(location: string): Promise { const query = ` query GetMenuByLocation($location: MenuLocationEnum!) { menus(where: { location: $location }, first: 1) { nodes { id databaseId name slug locations menuItems(first: 100) { nodes { id databaseId uri url order label parentId target cssClasses description menuItemColor childItems(first: 50) { nodes { id databaseId label uri url order menuItemColor } } } } } } } `; const variables = { location }; const data = await fetchGraphQL(query, variables); return data?.menus?.nodes?.[0] || null; } /** * Получить меню по слагу */ async function fetchMenuBySlug(slug: string): Promise { const query = ` query GetMenuBySlug($slug: String!) { menus(where: { slug: $slug }, first: 1) { nodes { id databaseId name slug locations menuItems(first: 100) { nodes { id databaseId uri url order label parentId target cssClasses description menuItemColor } } } } } `; const variables = { slug }; const data = await fetchGraphQL(query, variables); return data?.menus?.nodes?.[0] || null; } /** * Получить меню по имени */ async function fetchMenuByName(name: string): Promise { const query = ` query GetMenuByName($name: String!) { menus(where: { name: $name }, first: 1) { nodes { id databaseId name slug locations menuItems(first: 100) { nodes { id databaseId uri url order label parentId target cssClasses description menuItemColor } } } } } `; const variables = { name }; const data = await fetchGraphQL(query, variables); return data?.menus?.nodes?.[0] || null; } /** * Преобразовать плоский список элементов меню в иерархическую структуру */ export function buildMenuHierarchy(menuItems: MenuItem[]): MenuItem[] { const itemsMap = new Map(); const rootItems: MenuItem[] = []; // Создаем map всех элементов menuItems.forEach(item => { itemsMap.set(item.id, { ...item, childItems: { nodes: [] } }); }); // Строим иерархию menuItems.forEach(item => { const menuItem = itemsMap.get(item.id)!; if (item.parentId && itemsMap.has(item.parentId)) { const parent = itemsMap.get(item.parentId)!; if (!parent.childItems) { parent.childItems = { nodes: [] }; } parent.childItems.nodes.push(menuItem); } else { rootItems.push(menuItem); } }); // Сортируем элементы по order const sortByOrder = (items: MenuItem[]) => items.sort((a, b) => a.order - b.order); // Рекурсивно сортируем все уровни function sortRecursive(items: MenuItem[]) { sortByOrder(items); items.forEach(item => { if (item.childItems?.nodes.length) { sortRecursive(item.childItems.nodes); } }); } sortRecursive(rootItems); return rootItems; } /** * Получить меню в виде иерархической структуры */ export async function getHierarchicalMenu(identifier: MenuIdentifier): Promise { const menu = await fetchMenu(identifier); if (!menu || !menu.menuItems?.nodes?.length) { return []; } return buildMenuHierarchy(menu.menuItems.nodes); } /** * Получить меню в виде плоского списка */ export async function getFlatMenu(identifier: MenuIdentifier): Promise { const menu = await fetchMenu(identifier); if (!menu || !menu.menuItems?.nodes?.length) { return []; } return menu.menuItems.nodes.sort((a, b) => a.order - b.order); }