fix conflig merge

This commit is contained in:
r2xrzh9q2z-lab
2026-02-05 09:20:12 +07:00
7 changed files with 98 additions and 44 deletions

View File

@@ -1,28 +1,61 @@
'use client'; 'use client';
import { useState } from 'react'; import { useEffect, useState, useCallback } from 'react';
import HeaderTop from './HeaderTop'; import HeaderTop from './HeaderTop';
import HeaderBottom from './HeaderBottom'; import HeaderBottom from './HeaderBottom';
import Offcanvas from './Offcanvas'; import Offcanvas from './Offcanvas';
import { headerMenuService } from '@/services/header-menu.service';
import { HeaderMenu as HeaderMenuType } from '@/types/header-menu';
const Header = () => { const Header = () => {
const [isOffcanvasOpen, setIsOffcanvasOpen] = useState(false); const [isOffcanvasOpen, setIsOffcanvasOpen] = useState(false);
const [isSearchOpen, setIsSearchOpen] = useState(false); const [isSearchOpen, setIsSearchOpen] = useState(false);
const [menuItems, setMenuItems] = useState<any[]>([]);
const [isLoading, setIsLoading] = useState(true);
const toggleOffcanvas = () => setIsOffcanvasOpen(!isOffcanvasOpen); const toggleOffcanvas = () => setIsOffcanvasOpen(!isOffcanvasOpen);
const toggleSearch = () => setIsSearchOpen(!isSearchOpen); const toggleSearch = () => setIsSearchOpen(!isSearchOpen);
// Helper to adapt 'children' from API to 'submenu' for the existing components
const adaptMenu = useCallback((item: HeaderMenuType): any => ({
label: item.title,
href: item.url,
submenu: item.children && item.children.length > 0
? item.children.map((child: HeaderMenuType) => adaptMenu(child))
: undefined
}), []);
useEffect(() => {
const fetchMenu = async () => {
try {
setIsLoading(true);
const data = await headerMenuService.getHeaderMenu();
const mappedData = data.map(item => adaptMenu(item));
setMenuItems(mappedData);
} catch (error) {
console.error('Error fetching menu in Header:', error);
} finally {
setIsLoading(false);
}
};
fetchMenu();
}, [adaptMenu]);
return ( return (
<> <>
<HeaderTop /> <HeaderTop />
<HeaderBottom <HeaderBottom
onToggleOffcanvas={toggleOffcanvas} onToggleOffcanvas={toggleOffcanvas}
onToggleSearch={toggleSearch} onToggleSearch={toggleSearch}
menuItems={menuItems}
isLoading={isLoading}
/> />
<Offcanvas <Offcanvas
isOpen={isOffcanvasOpen} isOpen={isOffcanvasOpen}
onClose={() => setIsOffcanvasOpen(false)} onClose={() => setIsOffcanvasOpen(false)}
menuItems={menuItems}
/> />
{/* Search Popup */} {/* Search Popup */}

View File

@@ -1,52 +1,24 @@
'use client'; 'use client';
import React, { useEffect, useState } from 'react';
import Link from 'next/link'; import Link from 'next/link';
import HeaderMenu from './HeaderMenu'; import HeaderMenu from './HeaderMenu';
import { headerMenuService } from '@/services/header-menu.service';
import headerData from './header.json'; import { HeaderMenu as HeaderMenuType } from '@/types/header-menu';
// Map the JSON data to satisfy the HeaderMenu props interface
interface JsonMenuItem {
label: string;
href: string;
children?: JsonMenuItem[];
}
interface MenuItem {
label: string;
href: string;
submenu?: MenuItem[];
megaMenuContent?: React.ReactNode;
}
// We need to recursively map 'children' to 'submenu'
const mapMenuItems = (items: JsonMenuItem[]): MenuItem[] => {
return items.map(item => {
const newItem: MenuItem = {
label: item.label,
href: item.href,
};
if (item.children && item.children.length > 0) {
newItem.submenu = mapMenuItems(item.children);
}
return newItem;
});
};
const menuItems: MenuItem[] = mapMenuItems(headerData.menu as JsonMenuItem[]);
interface HeaderBottomProps { interface HeaderBottomProps {
onToggleOffcanvas: () => void; onToggleOffcanvas: () => void;
onToggleSearch: () => void; onToggleSearch: () => void;
menuItems: any[];
isLoading: boolean;
} }
const HeaderBottom: React.FC<HeaderBottomProps> = ({ onToggleOffcanvas, onToggleSearch }) => { const HeaderBottom: React.FC<HeaderBottomProps> = ({
onToggleOffcanvas,
onToggleSearch,
menuItems,
isLoading
}) => {
return ( return (
<header id="header-sticky" className="header-1"> <header id="header-sticky" className="header-1">
<div className="container-fluid"> <div className="container-fluid">
@@ -59,7 +31,7 @@ const HeaderBottom: React.FC<HeaderBottomProps> = ({ onToggleOffcanvas, onToggle
</Link> </Link>
</div> </div>
<div className="mean__menu-wrapper"> <div className="mean__menu-wrapper">
<HeaderMenu menuItems={menuItems} /> {!isLoading && <HeaderMenu menuItems={menuItems as any} />}
</div> </div>
</div> </div>
<div className="header-right d-flex align-items-center mt-0"> <div className="header-right d-flex align-items-center mt-0">

View File

@@ -20,9 +20,10 @@ interface MenuItem {
interface OffcanvasProps { interface OffcanvasProps {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
menuItems: any[];
} }
const Offcanvas: React.FC<OffcanvasProps> = ({ isOpen, onClose }) => { const Offcanvas: React.FC<OffcanvasProps> = ({ isOpen, onClose, menuItems }) => {
// Explicitly casting headerData to the expected structure // Explicitly casting headerData to the expected structure
const data = headerData as { const data = headerData as {
top: { top: {
@@ -37,10 +38,10 @@ const Offcanvas: React.FC<OffcanvasProps> = ({ isOpen, onClose }) => {
phone: string; phone: string;
}; };
}; };
menu: MenuItem[];
}; };
const { offcanvas, top, menu } = data; const { offcanvas, top } = data;
const menu = menuItems;
return ( return (
<> <>

20
lib/axios.ts Normal file
View File

@@ -0,0 +1,20 @@
import axios from 'axios';
const axiosInstance = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// Response interceptor for basic error handling
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
console.error('API Error:', error.response?.data || error.message);
return Promise.reject(error);
}
);
export default axiosInstance;

View File

@@ -9777,3 +9777,4 @@ html.lenis body {
a { a {
color: var(--white); color: var(--white);
} /*# sourceMappingURL=main.css.map */ } /*# sourceMappingURL=main.css.map */

View File

@@ -0,0 +1,20 @@
import axiosInstance from '../lib/axios';
import { HeaderMenu } from '../types/header-menu';
export const headerMenuService = {
/**
* Fetch active header menu tree from API
*/
async getHeaderMenu(): Promise<HeaderMenu[]> {
try {
const response = await axiosInstance.get<{ success: boolean; data: HeaderMenu[] }>('/api/header-menu');
if (response.data.success) {
return response.data.data;
}
return [];
} catch (error) {
console.error('Failed to fetch header menu:', error);
return []; // Fallback to empty menu
}
}
};

7
types/header-menu.ts Normal file
View File

@@ -0,0 +1,7 @@
export interface HeaderMenu {
id: string;
title: string;
url: string;
type?: 'internal' | 'external';
children?: HeaderMenu[];
}