langueages
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import React, { useEffect, useState, useRef, Fragment } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { MenuCategory, MenuItem as MenuItemType } from "@/data/menu";
|
||||
import { CategoryNav } from "@/components/CategoryNav";
|
||||
import { MenuItem } from "@/components/MenuItem";
|
||||
import { useTranslations, useLocale } from "next-intl";
|
||||
import { useRouter, usePathname } from "@/i18n/routing";
|
||||
|
||||
export const CATEGORY_ICONS: Record<string, string> = {
|
||||
"kahvaltiliklar": "🌅",
|
||||
@@ -41,6 +43,10 @@ export default function MenuClient({ initialCategories, siteSettings }: { initia
|
||||
);
|
||||
const [selectedItem, setSelectedItem] = useState<MenuItemType | null>(null);
|
||||
const sectionRefs = useRef<(HTMLElement | null)[]>([]);
|
||||
const t = useTranslations("Menu");
|
||||
const locale = useLocale();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
@@ -120,8 +126,28 @@ export default function MenuClient({ initialCategories, siteSettings }: { initia
|
||||
className="text-xs font-sans tracking-wide"
|
||||
style={{ color: "rgba(255,218,168,0.5)" }}
|
||||
>
|
||||
Akyaka'nın en keyifli menüsü
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
|
||||
<div className="mt-8 flex items-center gap-4">
|
||||
{['tr', 'en', 'ru', 'de'].map((l, index) => (
|
||||
<React.Fragment key={l}>
|
||||
<button
|
||||
onClick={() => router.replace(pathname, { locale: l })}
|
||||
className={`text-[11px] font-sans tracking-[0.25em] uppercase transition-all duration-300 ${
|
||||
locale === l
|
||||
? 'text-white opacity-100 scale-105'
|
||||
: 'text-white/40 hover:text-white/70 hover:scale-100'
|
||||
}`}
|
||||
>
|
||||
{l}
|
||||
</button>
|
||||
{index < 3 && (
|
||||
<div className="w-px h-3" style={{ background: "rgba(255,190,100,0.2)" }} />
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
@@ -170,7 +196,7 @@ export default function MenuClient({ initialCategories, siteSettings }: { initia
|
||||
{CATEGORY_ICONS[category.id] ?? "🍽️"}
|
||||
</span>
|
||||
<h2
|
||||
className="font-display text-[22px] leading-tight font-semibold italic"
|
||||
className="font-display text-[22px] leading-tight font-semibold italic uppercase tracking-wider"
|
||||
style={{ color: "var(--stone-ink)" }}
|
||||
>
|
||||
{category.title}
|
||||
@@ -217,8 +243,17 @@ export default function MenuClient({ initialCategories, siteSettings }: { initia
|
||||
className="text-[11px] font-sans"
|
||||
style={{ color: "rgba(120,90,60,0.5)" }}
|
||||
>
|
||||
© 2026 Moy Beach. Tüm hakları saklıdır.
|
||||
© 2026 {siteSettings.restaurantName}. {t('allRightsReserved')}
|
||||
</p>
|
||||
<a
|
||||
href="https://ayris.tech"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="mt-6 text-[10px] font-sans tracking-[0.2em] uppercase transition-colors hover:text-white/80"
|
||||
style={{ color: "rgba(160,100,50,0.4)" }}
|
||||
>
|
||||
Created by <span className="font-semibold">ayris.tech</span>
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -240,7 +275,7 @@ export default function MenuClient({ initialCategories, siteSettings }: { initia
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{selectedItem.image && (
|
||||
<div className="w-full h-56 relative">
|
||||
<div className="w-full h-80 relative">
|
||||
<img src={selectedItem.image} alt={selectedItem.name} className="w-full h-full object-cover" />
|
||||
<button
|
||||
className="absolute top-3 right-3 bg-black/50 text-white w-8 h-8 rounded-full flex items-center justify-center backdrop-blur-md transition-colors hover:bg-black/70"
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Cormorant_Garamond, Plus_Jakarta_Sans } from "next/font/google";
|
||||
import {NextIntlClientProvider} from 'next-intl';
|
||||
import {getMessages, setRequestLocale} from 'next-intl/server';
|
||||
import {routing} from '@/i18n/routing';
|
||||
import {notFound} from 'next/navigation';
|
||||
import "../globals.css";
|
||||
|
||||
const cormorant = Cormorant_Garamond({
|
||||
variable: "--font-cormorant",
|
||||
subsets: ["latin"],
|
||||
weight: ["300", "400", "500", "600", "700"],
|
||||
style: ["normal", "italic"],
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
const jakarta = Plus_Jakarta_Sans({
|
||||
variable: "--font-jakarta",
|
||||
subsets: ["latin"],
|
||||
weight: ["300", "400", "500", "600", "700"],
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Moy Beach Akyaka | Menü",
|
||||
description: "Moy Beach Akyaka dijital menü.",
|
||||
};
|
||||
|
||||
export function generateStaticParams() {
|
||||
return routing.locales.map((locale) => ({locale}));
|
||||
}
|
||||
|
||||
export default async function LocaleLayout({
|
||||
children,
|
||||
params
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
params: Promise<{locale: string}>;
|
||||
}) {
|
||||
const {locale} = await params;
|
||||
|
||||
if (!routing.locales.includes(locale as any)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
setRequestLocale(locale);
|
||||
const messages = await getMessages();
|
||||
|
||||
return (
|
||||
<html lang={locale} className={`${cormorant.variable} ${jakarta.variable} h-full`}>
|
||||
<body className="min-h-full flex flex-col antialiased" suppressHydrationWarning>
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
{children}
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import MenuClient from "./MenuClient";
|
||||
import { MenuCategory } from "@/data/menu";
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
import { routing } from '@/i18n/routing';
|
||||
|
||||
async function getMenuData(): Promise<MenuCategory[]> {
|
||||
async function getMenuData(locale: string): Promise<MenuCategory[]> {
|
||||
const dbCategories = await prisma.categories.findMany({
|
||||
where: { status: 1 },
|
||||
orderBy: { order_num: "asc" },
|
||||
@@ -17,11 +19,11 @@ async function getMenuData(): Promise<MenuCategory[]> {
|
||||
const categoryProducts = dbProducts.filter((p) => p.category_id === cat.id);
|
||||
return {
|
||||
id: cat.slug,
|
||||
title: cat.name_tr,
|
||||
title: (cat.name_i18n as Record<string, string>)?.[locale] || (locale === 'tr' ? cat.name_tr : cat.name),
|
||||
items: categoryProducts.map((p) => ({
|
||||
id: p.id.toString(),
|
||||
name: p.name_tr,
|
||||
description: p.description_tr,
|
||||
name: (p.name_i18n as Record<string, string>)?.[locale] || (locale === 'tr' ? p.name_tr : p.name),
|
||||
description: (p.description_i18n as Record<string, string>)?.[locale] || (locale === 'tr' ? p.description_tr : (p.description || '')),
|
||||
price: p.price.toString() + " ₺",
|
||||
image: p.image_url || '/default-product.png',
|
||||
})),
|
||||
@@ -43,11 +45,19 @@ async function getSiteSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
export default async function Page() {
|
||||
export default async function Page({
|
||||
params
|
||||
}: {
|
||||
params: Promise<{ locale: string }>
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
|
||||
const [menuData, siteSettings] = await Promise.all([
|
||||
getMenuData(),
|
||||
getMenuData(locale),
|
||||
getSiteSettings(),
|
||||
]);
|
||||
|
||||
return <MenuClient initialCategories={menuData} siteSettings={siteSettings} />;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export async function createCategory(_prevState: unknown, formData: FormData) {
|
||||
data: {
|
||||
name,
|
||||
name_tr,
|
||||
name_i18n: { en: name, tr: name_tr, ru: '', de: '' },
|
||||
slug: name_tr.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
|
||||
order_num,
|
||||
status,
|
||||
@@ -50,6 +51,7 @@ export async function updateCategory(id: number, _prevState: unknown, formData:
|
||||
data: {
|
||||
name,
|
||||
name_tr,
|
||||
name_i18n: { en: name, tr: name_tr, ru: '', de: '' },
|
||||
slug: name_tr.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
|
||||
order_num,
|
||||
status,
|
||||
|
||||
@@ -22,10 +22,12 @@ export async function createProduct(_prevState: unknown, formData: FormData) {
|
||||
data: {
|
||||
name,
|
||||
name_tr,
|
||||
name_i18n: { en: name, tr: name_tr, ru: '', de: '' },
|
||||
slug: name_tr.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
|
||||
price,
|
||||
category_id,
|
||||
description_tr,
|
||||
description_i18n: { en: '', tr: description_tr, ru: '', de: '' },
|
||||
image_url,
|
||||
status,
|
||||
}
|
||||
@@ -59,10 +61,12 @@ export async function updateProduct(id: number, _prevState: unknown, formData: F
|
||||
data: {
|
||||
name,
|
||||
name_tr,
|
||||
name_i18n: { en: name, tr: name_tr, ru: '', de: '' },
|
||||
slug: name_tr.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
|
||||
price,
|
||||
category_id,
|
||||
description_tr,
|
||||
description_i18n: { en: '', tr: description_tr, ru: '', de: '' },
|
||||
image_url,
|
||||
status,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Cormorant_Garamond, Plus_Jakarta_Sans } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import "../globals.css";
|
||||
|
||||
const cormorant = Cormorant_Garamond({
|
||||
variable: "--font-cormorant",
|
||||
Reference in New Issue
Block a user