feat: setup database-driven admin user management
This commit is contained in:
+52
-14
@@ -1,9 +1,21 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import * as Icons from "lucide-react";
|
||||
import { ArrowRight, Star } from "lucide-react";
|
||||
import {
|
||||
ArrowRight, Star, Wind, Anchor, Utensils, BedDouble, Sun,
|
||||
Waves, Bike, Coffee, Music, MapPin, Camera, Heart, Compass,
|
||||
Fish, Mountain, Dumbbell, Leaf, Shield, Umbrella, Flame,
|
||||
type LucideIcon,
|
||||
} from "lucide-react";
|
||||
import ScrollReveal from "@/components/ScrollReveal";
|
||||
|
||||
export const revalidate = 3600;
|
||||
|
||||
const ICON_MAP: Record<string, LucideIcon> = {
|
||||
Wind, Anchor, Utensils, BedDouble, Sun, Star, Waves, Bike,
|
||||
Coffee, Music, MapPin, Camera, Heart, Compass, Fish, Mountain,
|
||||
Dumbbell, Leaf, Shield, Umbrella, Flame,
|
||||
};
|
||||
|
||||
import HeroVideo from "@/components/HeroVideo";
|
||||
|
||||
const defaultFeatures = [
|
||||
@@ -64,13 +76,32 @@ const testimonials = [
|
||||
|
||||
import { getDictionary, Locale } from "./dictionaries";
|
||||
import prisma from "@/lib/prisma";
|
||||
import { buildMeta } from "@/lib/metadata";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export async function generateMetadata(
|
||||
{ params }: { params: Promise<{ lang: string }> }
|
||||
): Promise<Metadata> {
|
||||
const { lang } = await params;
|
||||
return buildMeta(lang, "", {
|
||||
title: "Kite Beach Akyaka | Kitesurf & Butik Otel",
|
||||
description: "Nehrin huzuru, denizin enerjisi — tek bir noktada. Akyaka'nın kalbinde kitesurf ve butik otel deneyimi.",
|
||||
}, {
|
||||
title: "Kite Beach Akyaka | Kitesurf & Boutique Hotel",
|
||||
description: "Tranquility of the river, energy of the sea — in one spot. Kitesurf and boutique hotel experience in the heart of Akyaka.",
|
||||
});
|
||||
}
|
||||
|
||||
async function getInstagramData() {
|
||||
if (!process.env.RAPIDAPI_KEY) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const url = 'https://instagram120.p.rapidapi.com/api/instagram/posts';
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-rapidapi-key': process.env.RAPIDAPI_KEY || '',
|
||||
'x-rapidapi-key': process.env.RAPIDAPI_KEY,
|
||||
'x-rapidapi-host': 'instagram120.p.rapidapi.com',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
@@ -83,11 +114,14 @@ async function getInstagramData() {
|
||||
|
||||
try {
|
||||
const res = await fetch(url, options);
|
||||
if (!res.ok) throw new Error('Instagram fetch failed');
|
||||
if (!res.ok) {
|
||||
console.warn('Instagram API error or missing limits. Status:', res.status);
|
||||
return [];
|
||||
}
|
||||
const data = await res.json();
|
||||
return data.result?.edges?.slice(0, 4).map((edge: any) => edge.node) || [];
|
||||
} catch (err) {
|
||||
console.error("Instagram fetch error:", err);
|
||||
console.warn("Instagram fetch error:", err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -96,7 +130,15 @@ export default async function Home({ params }: { params: Promise<{ lang: string
|
||||
const { lang } = await params;
|
||||
const dict = await getDictionary(lang as Locale);
|
||||
|
||||
const heroDb = await prisma.heroSection.findUnique({ where: { lang } });
|
||||
const [heroDb, activities, events, dbFeatures, aboutDb, instagramPosts] = await Promise.all([
|
||||
prisma.heroSection.findUnique({ where: { lang } }),
|
||||
prisma.activity.findMany({ where: { lang }, orderBy: { order: "asc" } }),
|
||||
prisma.event.findMany({ where: { lang }, orderBy: { date: "asc" } }),
|
||||
prisma.feature.findMany({ where: { lang }, orderBy: { order: "asc" } }),
|
||||
prisma.aboutSection.findUnique({ where: { lang } }),
|
||||
getInstagramData(),
|
||||
]);
|
||||
|
||||
const heroData = heroDb || {
|
||||
location: dict.home.location,
|
||||
welcome: dict.home.welcome,
|
||||
@@ -107,11 +149,6 @@ export default async function Home({ params }: { params: Promise<{ lang: string
|
||||
scroll: dict.home.scroll,
|
||||
};
|
||||
|
||||
const activities = await prisma.activity.findMany({ where: { lang }, orderBy: { order: "asc" } });
|
||||
const events = await prisma.event.findMany({ where: { lang }, orderBy: { date: "asc" } });
|
||||
const dbFeatures = await prisma.feature.findMany({ where: { lang }, orderBy: { order: "asc" } });
|
||||
|
||||
const aboutDb = await prisma.aboutSection.findUnique({ where: { lang } });
|
||||
const aboutData = aboutDb || {
|
||||
badge: lang === "en" ? "Our Story" : "Hikayemiz",
|
||||
titleLine1: lang === "en" ? "In the" : "Doğanın",
|
||||
@@ -133,7 +170,6 @@ export default async function Home({ params }: { params: Promise<{ lang: string
|
||||
};
|
||||
|
||||
const displayFeatures = dbFeatures.length > 0 ? dbFeatures : defaultFeatures;
|
||||
const instagramPosts = await getInstagramData();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen">
|
||||
@@ -205,7 +241,7 @@ export default async function Home({ params }: { params: Promise<{ lang: string
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 divide-x divide-white/8">
|
||||
{displayFeatures.map(({ icon, label, sub }, i) => {
|
||||
const IconComponent = (Icons as any)[icon as string] || Icons.Star;
|
||||
const IconComponent = ICON_MAP[icon as string] || Star;
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
@@ -391,6 +427,7 @@ export default async function Home({ params }: { params: Promise<{ lang: string
|
||||
src={act.image}
|
||||
alt={act.title}
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
className="object-cover group-hover:scale-105 transition-transform duration-700"
|
||||
/>
|
||||
<div className={`absolute inset-0 bg-gradient-to-t ${i === 0 ? "from-dark/90 via-dark/20" : "from-dark/80"} to-transparent`} />
|
||||
@@ -453,6 +490,7 @@ export default async function Home({ params }: { params: Promise<{ lang: string
|
||||
src={ev.image}
|
||||
alt={ev.title}
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, 33vw"
|
||||
className="object-cover group-hover:scale-105 transition-transform duration-700"
|
||||
/>
|
||||
</div>
|
||||
@@ -522,7 +560,7 @@ export default async function Home({ params }: { params: Promise<{ lang: string
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-dark/60 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex flex-col items-center justify-center text-white p-6 text-center">
|
||||
<Icons.Camera className="w-8 h-8 mb-3" />
|
||||
<Camera className="w-8 h-8 mb-3" />
|
||||
<p className="text-xs line-clamp-3">
|
||||
{post.caption?.text}
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user