add new routing

This commit is contained in:
Andrey Kuvshinov
2025-12-17 23:05:49 +03:00
parent a72d47f6d9
commit 033158e384
9 changed files with 572 additions and 73 deletions

View File

@@ -1,71 +1,308 @@
// lib/api/menu-api.ts
import { fetchGraphQL } from './graphql-client.js';
interface MenuItemNode {
uri: string;
url: string;
order: number;
label: string;
export interface MenuItem {
id: string;
databaseId: number;
uri: string;
url: string;
order: number;
label: string;
parentId: string | null;
target: string;
cssClasses: string[];
description: string;
childItems?: {
nodes: MenuItem[];
};
}
interface MenuNode {
name: string;
menuItems: {
nodes: MenuItemNode[];
};
export interface Menu {
id: string;
databaseId: number;
name: string;
slug: string;
locations: string[];
menuItems: {
nodes: MenuItem[];
};
}
interface MenusResponse {
menus: {
nodes: MenuNode[];
};
export type MenuIdentifier =
| { id: number } // По ID меню
| { location: string } // По локации
| { slug: string } // По слагу
| { name: string }; // По имени
/**
* Получить меню по идентификатору
*/
export async function fetchMenu(identifier: MenuIdentifier): Promise<Menu | null> {
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;
}
}
/**
* Get navigation menu from WordPress
* Получить меню по ID (самый надежный способ)
*/
export async function navQuery(): Promise<MenusResponse> {
try {
const query = `{
menus(where: {location: PRIMARY}) {
nodes {
name
menuItems {
nodes {
uri
url
order
label
async function fetchMenuById(id: number): Promise<Menu | null> {
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
childItems(first: 50) {
nodes {
id
databaseId
label
uri
url
order
}
}
}
}
}
}
}
}
}`;
`;
return await executeQuery<MenusResponse>(query, {}, "navigation");
} catch (error) {
log.error("Error fetching nav: " + error);
// Return fallback data for development
return {
menus: {
nodes: [
{
name: "Primary",
menuItems: {
nodes: [
{ uri: "/", url: "/", order: 1, label: "Home" },
{ uri: "/about/", url: "/about/", order: 2, label: "About" },
{
uri: "/contact/",
url: "/contact/",
order: 3,
label: "Contact",
},
],
},
},
],
},
};
}
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<Menu | null> {
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
childItems(first: 50) {
nodes {
id
databaseId
label
uri
url
order
}
}
}
}
}
}
}
`;
const variables = { location };
const data = await fetchGraphQL(query, variables);
return data?.menus?.nodes?.[0] || null;
}
/**
* Получить меню по слагу
*/
async function fetchMenuBySlug(slug: string): Promise<Menu | null> {
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
}
}
}
}
}
`;
const variables = { slug };
const data = await fetchGraphQL(query, variables);
return data?.menus?.nodes?.[0] || null;
}
/**
* Получить меню по имени
*/
async function fetchMenuByName(name: string): Promise<Menu | null> {
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
}
}
}
}
}
`;
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<string, MenuItem>();
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<MenuItem[]> {
const menu = await fetchMenu(identifier);
if (!menu || !menu.menuItems?.nodes?.length) {
return [];
}
return buildMenuHierarchy(menu.menuItems.nodes);
}
/**
* Получить меню в виде плоского списка
*/
export async function getFlatMenu(identifier: MenuIdentifier): Promise<MenuItem[]> {
const menu = await fetchMenu(identifier);
if (!menu || !menu.menuItems?.nodes?.length) {
return [];
}
return menu.menuItems.nodes.sort((a, b) => a.order - b.order);
}

View File

@@ -13,6 +13,9 @@ export async function fetchGraphQL(query, variables = {}) {
const json = await res.json();
console.log("Query:\n", query);
console.log("Variables:\n", JSON.stringify(variables, null, 2));
if (json.errors) {
// Выводим полный запрос и переменные для IDE