From b712760825c05046552a199d6cd25b82e1617273 Mon Sep 17 00:00:00 2001 From: ayrisdev Date: Wed, 10 Jun 2026 04:25:54 +0300 Subject: [PATCH] feat: setup database-driven admin user management --- app/[lang]/aktiviteler/page.tsx | 15 +++ app/[lang]/etkinlikler/page.tsx | 18 ++- app/[lang]/galeri/layout.tsx | 19 +++ app/[lang]/hakkimizda/page.tsx | 15 +++ app/[lang]/iletisim/page.tsx | 15 +++ app/[lang]/layout.tsx | 98 +++++++++++++- app/[lang]/page.tsx | 66 +++++++-- app/[lang]/restoran/page.tsx | 15 +++ app/actions/auth.ts | 68 +++++++--- app/actions/users.ts | 114 ++++++++++++++++ app/admin/components/SidebarNav.tsx | 3 +- app/admin/users/UsersClient.tsx | 201 ++++++++++++++++++++++++++++ app/admin/users/page.tsx | 13 ++ app/llms.txt/route.ts | 58 ++++++++ app/manifest.ts | 16 +++ app/robots.ts | 23 ++++ app/sitemap.ts | 29 ++++ components/Navbar.tsx | 2 +- lib/metadata.ts | 24 ++++ next.config.ts | 3 + package-lock.json | 18 +++ package.json | 2 + prisma/schema.prisma | 9 ++ 23 files changed, 804 insertions(+), 40 deletions(-) create mode 100644 app/[lang]/galeri/layout.tsx create mode 100644 app/actions/users.ts create mode 100644 app/admin/users/UsersClient.tsx create mode 100644 app/admin/users/page.tsx create mode 100644 app/llms.txt/route.ts create mode 100644 app/manifest.ts create mode 100644 app/robots.ts create mode 100644 app/sitemap.ts create mode 100644 lib/metadata.ts diff --git a/app/[lang]/aktiviteler/page.tsx b/app/[lang]/aktiviteler/page.tsx index 1a2ae83..5698fb3 100644 --- a/app/[lang]/aktiviteler/page.tsx +++ b/app/[lang]/aktiviteler/page.tsx @@ -4,6 +4,21 @@ import { ArrowRight, Clock, Users } from "lucide-react"; import ScrollReveal from "@/components/ScrollReveal"; import prisma from "@/lib/prisma"; +import { buildMeta } from "@/lib/metadata"; +import type { Metadata } from "next"; + +export const revalidate = 3600; + +export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { + const { lang } = await params; + return buildMeta(lang, "/aktiviteler", { + title: "Aktiviteler | Kite Beach Akyaka", + description: "Kitesurf, yoga, SUP ve bisiklet kiralama. Akyaka'nın doğasında IKO sertifikalı eğitmenlerle unutulmaz deneyimler.", + }, { + title: "Activities | Kite Beach Akyaka", + description: "Kitesurf, yoga, SUP and bicycle rental. Unforgettable experiences in Akyaka with IKO certified instructors.", + }); +} export default async function AktivitelerPage({ params }: { params: Promise<{ lang: string }> }) { const { lang } = await params; diff --git a/app/[lang]/etkinlikler/page.tsx b/app/[lang]/etkinlikler/page.tsx index c1801a9..b2bc296 100644 --- a/app/[lang]/etkinlikler/page.tsx +++ b/app/[lang]/etkinlikler/page.tsx @@ -4,11 +4,21 @@ import { ArrowRight, Calendar, Clock, MapPin, Music } from "lucide-react"; import ScrollReveal from "@/components/ScrollReveal"; import prisma from "@/lib/prisma"; +import { buildMeta } from "@/lib/metadata"; +import type { Metadata } from "next"; -export const metadata = { - title: "Etkinlikler | Kite Beach Akyaka", - description: "Mahmut Orhan konserinden Kitesurf festivallerine, Akyaka'daki en özel etkinlikler ve gün batımı partileri.", -}; +export const revalidate = 3600; + +export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { + const { lang } = await params; + return buildMeta(lang, "/etkinlikler", { + title: "Etkinlikler | Kite Beach Akyaka", + description: "Mahmut Orhan konserinden Kitesurf festivallerine, Akyaka'daki en özel etkinlikler ve gün batımı partileri.", + }, { + title: "Events | Kite Beach Akyaka", + description: "From Mahmut Orhan concerts to Kitesurf festivals, the most special events and sunset parties in Akyaka.", + }); +} export default async function EventsPage({ params }: { params: Promise<{ lang: string }> }) { const { lang } = await params; diff --git a/app/[lang]/galeri/layout.tsx b/app/[lang]/galeri/layout.tsx new file mode 100644 index 0000000..cb8d718 --- /dev/null +++ b/app/[lang]/galeri/layout.tsx @@ -0,0 +1,19 @@ +import { buildMeta } from "@/lib/metadata"; +import type { Metadata } from "next"; + +export const revalidate = 3600; + +export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { + const { lang } = await params; + return buildMeta(lang, "/galeri", { + title: "Galeri | Kite Beach Akyaka", + description: "Kite Beach Akyaka'nın fotoğraf galerisini keşfedin. Odalar, aktiviteler, restoran ve Akyaka'nın eşsiz doğasından kareler.", + }, { + title: "Gallery | Kite Beach Akyaka", + description: "Explore the photo gallery of Kite Beach Akyaka. Rooms, activities, restaurant and moments from the unique nature of Akyaka.", + }); +} + +export default function GaleriLayout({ children }: { children: React.ReactNode }) { + return <>{children}; +} diff --git a/app/[lang]/hakkimizda/page.tsx b/app/[lang]/hakkimizda/page.tsx index 1e3d253..91a7949 100644 --- a/app/[lang]/hakkimizda/page.tsx +++ b/app/[lang]/hakkimizda/page.tsx @@ -1,6 +1,21 @@ import Image from "next/image"; import { Leaf, ChefHat, Award } from "lucide-react"; import ScrollReveal from "@/components/ScrollReveal"; +import { buildMeta } from "@/lib/metadata"; +import type { Metadata } from "next"; + +export const revalidate = 3600; + +export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { + const { lang } = await params; + return buildMeta(lang, "/hakkimizda", { + title: "Hakkımızda | Kite Beach Akyaka", + description: "10+ yıllık deneyim, 500+ mutlu misafir. Azmak nehri kıyısında doğa, kitesurf tutku ve yerel lezzetlerin buluşma noktası.", + }, { + title: "About Us | Kite Beach Akyaka", + description: "10+ years of experience, 500+ happy guests. Where nature, kitesurf passion and local flavors meet on the banks of Azmak river.", + }); +} const values = [ { diff --git a/app/[lang]/iletisim/page.tsx b/app/[lang]/iletisim/page.tsx index ffb1cef..f2bbec3 100644 --- a/app/[lang]/iletisim/page.tsx +++ b/app/[lang]/iletisim/page.tsx @@ -1,5 +1,20 @@ import { MapPin, Phone, Mail, Clock } from "lucide-react"; import ScrollReveal from "@/components/ScrollReveal"; +import { buildMeta } from "@/lib/metadata"; +import type { Metadata } from "next"; + +export const revalidate = 3600; + +export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }): Promise { + const { lang } = await params; + return buildMeta(lang, "/iletisim", { + title: "İletişim & Rezervasyon | Kite Beach Akyaka", + description: "Kite Beach Akyaka ile iletişime geçin. Muğla Akyaka'da kitesurf dersi, konaklama ve etkinlik rezervasyonu için bize yazın.", + }, { + title: "Contact & Reservation | Kite Beach Akyaka", + description: "Contact Kite Beach Akyaka. Write to us for kitesurf lessons, accommodation and event reservations in Akyaka, Muğla.", + }); +} export default function IletisimPage() { return ( diff --git a/app/[lang]/layout.tsx b/app/[lang]/layout.tsx index 1cab333..a983e9f 100644 --- a/app/[lang]/layout.tsx +++ b/app/[lang]/layout.tsx @@ -1,4 +1,4 @@ -import type { Metadata } from "next"; +import type { Metadata, Viewport } from "next"; import { Inter, Playfair_Display } from "next/font/google"; import "../globals.css"; import Navbar from "@/components/Navbar"; @@ -8,22 +8,106 @@ import WhatsAppButton from "@/components/WhatsAppButton"; const inter = Inter({ variable: "--font-inter", subsets: ["latin"], + display: "swap", }); const playfair = Playfair_Display({ variable: "--font-playfair", subsets: ["latin"], + display: "swap", }); -export const metadata: Metadata = { - title: "Kite Beach Akyaka | Kitesurf & Butik Otel", - description: "Nehrin huzuru, denizin enerjisi — tek bir noktada. Kite Beach Akyaka butik otel ve kitesurf merkezine hoş geldiniz.", +const baseUrl = "https://kitebeachakyaka.com.tr"; + +export const viewport: Viewport = { + width: "device-width", + initialScale: 1, + maximumScale: 5, + themeColor: "#0D3D50", }; +export async function generateMetadata( + { params }: { params: Promise<{ lang: string }> } +): Promise { + const { lang } = await params; + const isEn = lang === "en"; + return { + title: isEn + ? "Kite Beach Akyaka | Kitesurf & Boutique Hotel" + : "Kite Beach Akyaka | Kitesurf & Butik Otel", + description: isEn + ? "Tranquility of the river, energy of the sea — in one spot. Welcome to Kite Beach Akyaka boutique hotel and kitesurf center." + : "Nehrin huzuru, denizin enerjisi — tek bir noktada. Kite Beach Akyaka butik otel ve kitesurf merkezine hoş geldiniz.", + metadataBase: new URL(baseUrl), + openGraph: { + locale: isEn ? "en_US" : "tr_TR", + alternateLocale: isEn ? "tr_TR" : "en_US", + siteName: "Kite Beach Akyaka", + type: "website", + url: `${baseUrl}/${lang}`, + images: [{ url: `${baseUrl}/og-image.jpg`, width: 1200, height: 630, alt: "Kite Beach Akyaka" }], + }, + twitter: { + card: "summary_large_image", + images: [`${baseUrl}/og-image.jpg`], + }, + }; +} + export async function generateStaticParams() { return [{ lang: 'tr' }, { lang: 'en' }] } +function buildJsonLd(lang: string) { + const isEn = lang === "en"; + return { + "@context": "https://schema.org", + "@graph": [ + { + "@type": "LodgingBusiness", + "@id": `${baseUrl}/#business`, + name: "Kite Beach Akyaka", + url: baseUrl, + logo: `${baseUrl}/logo.png`, + image: `${baseUrl}/og-image.jpg`, + description: isEn + ? "Boutique hotel and kitesurf center on the banks of Azmak River in Akyaka, Muğla." + : "Akyaka, Muğla'da Azmak Nehri kıyısında butik otel ve kitesurf merkezi.", + telephone: "+905330816181", + email: "info@kitebeachakyaka.com.tr", + address: { + "@type": "PostalAddress", + streetAddress: "Akyaka Mahallesi, Akçapınar Mevkii", + addressLocality: "Akyaka", + addressRegion: "Muğla", + addressCountry: "TR", + }, + geo: { + "@type": "GeoCoordinates", + latitude: 37.0543, + longitude: 28.3286, + }, + sameAs: [ + "https://instagram.com/kitebeachakyaka", + ], + amenityFeature: [ + { "@type": "LocationFeatureSpecification", name: "Kitesurf School", value: true }, + { "@type": "LocationFeatureSpecification", name: "Restaurant & Bar", value: true }, + { "@type": "LocationFeatureSpecification", name: "Yoga Platform", value: true }, + ], + }, + { + "@type": "WebSite", + "@id": `${baseUrl}/#website`, + url: baseUrl, + name: "Kite Beach Akyaka", + publisher: { "@id": `${baseUrl}/#business` }, + inLanguage: [isEn ? "en" : "tr"], + }, + ], + }; +} + export default async function RootLayout({ children, params, @@ -39,6 +123,12 @@ export default async function RootLayout({ suppressHydrationWarning > +