langueages

This commit is contained in:
2026-06-29 20:29:41 +03:00
parent 958152a688
commit e30cbfa132
19 changed files with 1014 additions and 40 deletions
@@ -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&apos;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"
+58
View File
@@ -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>
);
}
+16 -6
View File
@@ -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 -1
View File
@@ -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",