285 lines
12 KiB
JavaScript
285 lines
12 KiB
JavaScript
'use client';
|
|
|
|
import { Geist, Geist_Mono } from "next/font/google";
|
|
import "./globals.css";
|
|
import "./i18n/config";
|
|
import Link from "next/link";
|
|
import Image from "next/image";
|
|
import { NavLink, MobileNavLink } from "./components/NavLinks";
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Globe } from 'lucide-react';
|
|
import { useState, useEffect } from 'react';
|
|
import { motion } from 'framer-motion';
|
|
|
|
const geistSans = Geist({
|
|
variable: "--font-geist-sans",
|
|
subsets: ["latin"],
|
|
});
|
|
|
|
const geistMono = Geist_Mono({
|
|
variable: "--font-geist-mono",
|
|
subsets: ["latin"],
|
|
});
|
|
|
|
export default function RootLayout({ children }) {
|
|
const { t, i18n } = useTranslation();
|
|
const [currentLanguage, setCurrentLanguage] = useState('en');
|
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
|
const currentYear = new Date().getFullYear();
|
|
|
|
const changeLanguage = (lng) => {
|
|
i18n.changeLanguage(lng);
|
|
setCurrentLanguage(lng);
|
|
localStorage.setItem('language', lng);
|
|
|
|
if (lng === 'ar') {
|
|
document.documentElement.dir = 'rtl';
|
|
document.documentElement.lang = 'ar';
|
|
document.documentElement.classList.add('rtl');
|
|
document.documentElement.classList.remove('ltr');
|
|
} else {
|
|
document.documentElement.dir = 'ltr';
|
|
document.documentElement.lang = 'en';
|
|
document.documentElement.classList.add('ltr');
|
|
document.documentElement.classList.remove('rtl');
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
const savedLanguage = localStorage.getItem('language') || 'en';
|
|
const lng = savedLanguage;
|
|
setCurrentLanguage(lng);
|
|
i18n.changeLanguage(lng);
|
|
|
|
if (lng === 'ar') {
|
|
document.documentElement.dir = 'rtl';
|
|
document.documentElement.lang = 'ar';
|
|
document.documentElement.classList.add('rtl');
|
|
} else {
|
|
document.documentElement.dir = 'ltr';
|
|
document.documentElement.lang = 'en';
|
|
document.documentElement.classList.add('ltr');
|
|
}
|
|
}, [i18n]);
|
|
|
|
const toggleMobileMenu = () => {
|
|
setIsMobileMenuOpen(!isMobileMenuOpen);
|
|
};
|
|
|
|
const closeMobileMenu = () => {
|
|
setIsMobileMenuOpen(false);
|
|
};
|
|
|
|
return (
|
|
<html lang={currentLanguage}>
|
|
<head>
|
|
<title>SweetHome</title>
|
|
<meta name="description" content={currentLanguage === 'ar' ? 'اكتشف أثاث وديكور منزلي فاخر' : 'Discover premium furniture and home decor'} />
|
|
</head>
|
|
<body className={`${geistSans.variable} ${geistMono.variable} antialiased ${currentLanguage === 'ar' ? 'font-arabic' : ''}`}>
|
|
<nav className="fixed top-0 left-0 right-0 bg-white/95 backdrop-blur-sm border-b border-gray-200 z-50 transition-all duration-300 shadow-sm">
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className={`flex justify-between items-center h-20 ${currentLanguage === 'ar' ? 'flex-row-reverse' : ''}`}>
|
|
|
|
<div className="flex items-center">
|
|
<Link href="/" className="flex items-center space-x-3 group">
|
|
<div className="relative w-10 h-10">
|
|
<Image
|
|
src="/logo.png"
|
|
alt={t("logoAlt")}
|
|
fill
|
|
className="object-contain group-hover:scale-105 transition-transform duration-300"
|
|
priority
|
|
sizes="40px"
|
|
/>
|
|
</div>
|
|
<span className="text-3xl font-bold text-gray-800 hidden md:block">
|
|
{t("brandNamePart1")}<span className="text-amber-600">{t("brandNamePart2")}</span>
|
|
</span>
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="hidden md:flex items-center space-x-4">
|
|
<div className={`flex items-center space-x-1 ${currentLanguage === 'ar' ? 'flex-row-reverse space-x-reverse' : ''}`}>
|
|
<NavLink href="/">
|
|
{t("home")}
|
|
</NavLink>
|
|
<NavLink href="/properties">
|
|
{t("ourProducts")}
|
|
</NavLink>
|
|
<NavLink href="/admin">
|
|
{t("admin")}
|
|
</NavLink>
|
|
</div>
|
|
|
|
<motion.button
|
|
whileHover={{ scale: 1.1, rotate: 360 }}
|
|
whileTap={{ scale: 0.9 }}
|
|
onClick={() => changeLanguage(currentLanguage === 'en' ? 'ar' : 'en')}
|
|
className="flex items-center justify-center w-10 h-10 bg-gray-100 hover:bg-gray-200 rounded-full transition-all duration-200 ml-4"
|
|
aria-label={currentLanguage === 'en' ? t("switchToArabic") : t("switchToEnglish")}
|
|
title={currentLanguage === 'en' ? t("switchToArabic") : t("switchToEnglish")}
|
|
>
|
|
<Globe className="w-5 h-5 text-gray-700" />
|
|
</motion.button>
|
|
</div>
|
|
|
|
<div className="md:hidden flex items-center gap-3">
|
|
<motion.button
|
|
whileHover={{ scale: 1.1, rotate: 360 }}
|
|
whileTap={{ scale: 0.9 }}
|
|
onClick={() => changeLanguage(currentLanguage === 'en' ? 'ar' : 'en')}
|
|
className="flex items-center justify-center w-10 h-10 bg-gray-100 hover:bg-gray-200 rounded-full transition-colors"
|
|
aria-label={currentLanguage === 'en' ? t("switchToArabic") : t("switchToEnglish")}
|
|
title={currentLanguage === 'en' ? t("switchToArabic") : t("switchToEnglish")}
|
|
>
|
|
<Globe className="w-5 h-5 text-gray-700" />
|
|
</motion.button>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={toggleMobileMenu}
|
|
className="inline-flex items-center justify-center p-2 rounded-md text-gray-700 hover:text-amber-600 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-amber-500 transition-colors"
|
|
aria-expanded={isMobileMenuOpen}
|
|
aria-label={t("openMainMenu")}
|
|
>
|
|
<span className="sr-only">{t("openMainMenu")}</span>
|
|
{isMobileMenuOpen ? (
|
|
<svg
|
|
className="h-6 w-6"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
) : (
|
|
<svg
|
|
className="h-6 w-6"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
|
</svg>
|
|
)}
|
|
</button>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
{isMobileMenuOpen && (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: -10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -10 }}
|
|
className="md:hidden bg-white border-t border-gray-200 shadow-lg"
|
|
>
|
|
<div className="px-2 pt-2 pb-3 space-y-1">
|
|
<MobileNavLink href="/" onClick={closeMobileMenu}>
|
|
{t("home")}
|
|
</MobileNavLink>
|
|
<MobileNavLink href="/properties" onClick={closeMobileMenu}>
|
|
{t("ourProducts")}
|
|
</MobileNavLink>
|
|
<MobileNavLink href="/admin" onClick={closeMobileMenu}>
|
|
{t("admin")}
|
|
</MobileNavLink>
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</nav>
|
|
|
|
<main className={`pt-16 min-h-screen bg-gradient-to-b from-gray-50 to-white ${currentLanguage === 'ar' ? 'text-right' : 'text-left'}`}>
|
|
{children}
|
|
</main>
|
|
|
|
<footer className="bg-gray-900 text-white py-12">
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className={`grid grid-cols-1 md:grid-cols-4 gap-8 ${currentLanguage === 'ar' ? 'text-right' : 'text-left'}`}>
|
|
<div className="space-y-4">
|
|
<div className={`flex items-center ${currentLanguage === 'ar' ? 'flex-row-reverse' : 'space-x-3'}`}>
|
|
<div className="relative w-10 h-10">
|
|
<Image
|
|
src="/logo.png"
|
|
alt={t("logoAlt")}
|
|
fill
|
|
className="object-contain"
|
|
sizes="40px"
|
|
/>
|
|
</div>
|
|
<span className="text-3xl font-bold">
|
|
{t("brandNamePart1")}<span className="text-amber-400">{t("brandNamePart2")}</span>
|
|
</span>
|
|
</div>
|
|
<p className="text-gray-400 text-sm">
|
|
{t("footerDescription")}
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="text-lg font-semibold mb-4">{t("quickLinks")}</h3>
|
|
<ul className="space-y-2">
|
|
<li>
|
|
<Link href="/" className="text-gray-400 hover:text-white transition-colors block py-1">
|
|
{t("home")}
|
|
</Link>
|
|
</li>
|
|
<li>
|
|
<Link href="/properties" className="text-gray-400 hover:text-white transition-colors block py-1">
|
|
{t("ourProducts")}
|
|
</Link>
|
|
</li>
|
|
<li>
|
|
<Link href="/admin" className="text-gray-400 hover:text-white transition-colors block py-1">
|
|
{t("admin")}
|
|
</Link>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="text-lg font-semibold mb-4">{t("contactUs")}</h3>
|
|
<ul className="space-y-3 text-gray-400">
|
|
<li className={`flex items-center ${currentLanguage === 'ar' ? 'flex-row-reverse' : 'space-x-2'}`}>
|
|
<svg className="w-5 h-5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
|
</svg>
|
|
<span>{t("phone")}</span>
|
|
</li>
|
|
<li className={`flex items-center ${currentLanguage === 'ar' ? 'flex-row-reverse' : 'space-x-2'}`}>
|
|
<svg className="w-5 h-5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
</svg>
|
|
<span>{t("footerEmail")}</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="text-lg font-semibold mb-4">{t("stayUpdated")}</h3>
|
|
<div className="space-y-3">
|
|
<input
|
|
type="email"
|
|
placeholder={t("yourEmail")}
|
|
className="w-full px-4 py-2 bg-gray-800 border border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-transparent"
|
|
/>
|
|
<button className="w-full bg-amber-600 hover:bg-amber-700 text-white px-4 py-2 rounded-lg transition-colors font-medium">
|
|
{t("subscribe")}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-8 pt-8 border-t border-gray-800 text-center text-gray-400 text-sm">
|
|
<p>© {currentYear} {t("copyright")}. {t("allRightsReserved")}</p>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
</body>
|
|
</html>
|
|
);
|
|
} |