feat: add dynamic site settings, hero media, admin panels, and database integration
This commit is contained in:
+10
-6
@@ -2,7 +2,7 @@
|
||||
|
||||
import { MapPin, Phone, Mail, Clock } from "lucide-react";
|
||||
|
||||
export default function Contact({ dict }: { dict: any }) {
|
||||
export default function Contact({ dict, dbSettings }: { dict: any, dbSettings?: any }) {
|
||||
return (
|
||||
<section id="contact" className="py-24 bg-sand-light text-sea-dark relative">
|
||||
<div className="container mx-auto px-6 max-w-7xl">
|
||||
@@ -22,8 +22,8 @@ export default function Contact({ dict }: { dict: any }) {
|
||||
<div>
|
||||
<h4 className="font-bold text-xl mb-2 font-serif">{dict.contact.address.title}</h4>
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
{dict.contact.address.text1}<br />
|
||||
{dict.contact.address.text2}
|
||||
{dbSettings?.addressText1 || dict.contact.address.text1}<br />
|
||||
{dbSettings?.addressText2 || dict.contact.address.text2}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -35,7 +35,9 @@ export default function Contact({ dict }: { dict: any }) {
|
||||
<div>
|
||||
<h4 className="font-bold text-xl mb-2 font-serif">{dict.contact.phone}</h4>
|
||||
<p className="text-gray-600">
|
||||
<a href="tel:+905344656235" className="hover:text-coral transition-colors">+90 534 465 62 35</a>
|
||||
<a href={`tel:${dbSettings?.phone?.replace(/\s+/g, '') || '+905344656235'}`} className="hover:text-coral transition-colors">
|
||||
{dbSettings?.phone || '+90 534 465 62 35'}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,7 +49,9 @@ export default function Contact({ dict }: { dict: any }) {
|
||||
<div>
|
||||
<h4 className="font-bold text-xl mb-2 font-serif">{dict.contact.email}</h4>
|
||||
<p className="text-gray-600">
|
||||
<a href="mailto:info@moybeachakyaka.com" className="hover:text-coral transition-colors">info@moybeachakyaka.com</a>
|
||||
<a href={`mailto:${dbSettings?.email || 'info@moybeachakyaka.com'}`} className="hover:text-coral transition-colors">
|
||||
{dbSettings?.email || 'info@moybeachakyaka.com'}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,7 +63,7 @@ export default function Contact({ dict }: { dict: any }) {
|
||||
<div>
|
||||
<h4 className="font-bold text-xl mb-2 font-serif">{dict.contact.hours.title}</h4>
|
||||
<p className="text-gray-600">
|
||||
{dict.contact.hours.text}
|
||||
{dbSettings?.workingHours || dict.contact.hours.text}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+17
-10
@@ -33,7 +33,7 @@ const socials = [
|
||||
},
|
||||
];
|
||||
|
||||
export default function Footer({ dict }: { dict: any }) {
|
||||
export default function Footer({ dict, dbSettings }: { dict: any, dbSettings?: any }) {
|
||||
const links = [
|
||||
{ label: dict.nav.about, href: "#about" },
|
||||
{ label: dict.nav.services, href: "#services" },
|
||||
@@ -41,6 +41,13 @@ export default function Footer({ dict }: { dict: any }) {
|
||||
{ label: dict.nav.contact, href: "#contact" },
|
||||
];
|
||||
|
||||
const dynamicSocials = socials.map(s => {
|
||||
if (s.label === "Instagram" && dbSettings?.instagramUrl) return { ...s, href: dbSettings.instagramUrl };
|
||||
if (s.label === "Facebook" && dbSettings?.facebookUrl) return { ...s, href: dbSettings.facebookUrl };
|
||||
if (s.label === "YouTube" && dbSettings?.youtubeUrl) return { ...s, href: dbSettings.youtubeUrl };
|
||||
return s;
|
||||
});
|
||||
|
||||
return (
|
||||
<footer className="relative bg-sea-dark text-sand-light overflow-hidden">
|
||||
|
||||
@@ -67,7 +74,7 @@ export default function Footer({ dict }: { dict: any }) {
|
||||
|
||||
{/* Social icons */}
|
||||
<div className="flex gap-3">
|
||||
{socials.map((s) => (
|
||||
{dynamicSocials.map((s) => (
|
||||
<a
|
||||
key={s.label}
|
||||
href={s.href}
|
||||
@@ -105,20 +112,20 @@ export default function Footer({ dict }: { dict: any }) {
|
||||
<h4 className="text-[11px] uppercase tracking-[0.25em] text-sand-dark mb-5">{dict.footer.contact}</h4>
|
||||
<ul className="space-y-4 text-sm text-sand-light/65">
|
||||
<li className="leading-relaxed">
|
||||
{dict.contact.address.text1}<br />
|
||||
{dict.contact.address.text2}
|
||||
{dbSettings?.addressText1 || dict.contact.address.text1}<br />
|
||||
{dbSettings?.addressText2 || dict.contact.address.text2}
|
||||
</li>
|
||||
<li>
|
||||
<a href="tel:+905344656235" className="hover:text-white transition-colors">
|
||||
+90 534 465 62 35
|
||||
<a href={`tel:${dbSettings?.phone?.replace(/\s+/g, '') || '+905344656235'}`} className="hover:text-white transition-colors">
|
||||
{dbSettings?.phone || '+90 534 465 62 35'}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="mailto:info@moybeachakyaka.com" className="hover:text-white transition-colors">
|
||||
info@moybeachakyaka.com
|
||||
<a href={`mailto:${dbSettings?.email || 'info@moybeachakyaka.com'}`} className="hover:text-white transition-colors">
|
||||
{dbSettings?.email || 'info@moybeachakyaka.com'}
|
||||
</a>
|
||||
</li>
|
||||
<li className="text-sand-light/45">{dict.contact.hours.text}</li>
|
||||
<li className="text-sand-light/45">{dbSettings?.workingHours || dict.contact.hours.text}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -148,7 +155,7 @@ export default function Footer({ dict }: { dict: any }) {
|
||||
|
||||
{/* Floating WhatsApp Button */}
|
||||
<a
|
||||
href="https://wa.me/905344656235"
|
||||
href={`https://wa.me/${dbSettings?.whatsappNumber || '905344656235'}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="fixed bottom-6 right-6 z-50 bg-[#25D366] text-white p-3.5 rounded-full shadow-xl hover:scale-110 transition-transform"
|
||||
|
||||
@@ -7,29 +7,31 @@ import Image from "next/image";
|
||||
|
||||
const photos = [
|
||||
{ id: 1, categoryKey: "beach", url: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?auto=format&fit=crop&q=80&w=1200" },
|
||||
{ id: 2, categoryKey: "rooms", url: "https://images.unsplash.com/photo-1611892440504-42a792e24d32?auto=format&fit=crop&q=80&w=1200" },
|
||||
{ id: 3, categoryKey: "restaurant", url: "https://images.unsplash.com/photo-1414235077428-338989a2e8c0?auto=format&fit=crop&q=80&w=1200" },
|
||||
{ id: 4, categoryKey: "beach", url: "https://images.unsplash.com/photo-1519046904884-53103b34b206?auto=format&fit=crop&q=80&w=1200" },
|
||||
{ id: 5, categoryKey: "events", url: "https://images.unsplash.com/photo-1511285560929-80b456fea0bc?auto=format&fit=crop&q=80&w=1200" },
|
||||
{ id: 6, categoryKey: "restaurant", url: "https://images.unsplash.com/photo-1555396273-367ea4eb4db5?auto=format&fit=crop&q=80&w=1200" },
|
||||
];
|
||||
|
||||
export default function Gallery({ dict }: { dict: any }) {
|
||||
export default function Gallery({ dict, dbPhotos }: { dict: any, dbPhotos?: any[] }) {
|
||||
const categories = [
|
||||
{ key: "all", label: dict.gallery.categories.all },
|
||||
{ key: "beach", label: dict.gallery.categories.beach },
|
||||
{ key: "restaurant", label: dict.gallery.categories.restaurant },
|
||||
{ key: "rooms", label: dict.gallery.categories.rooms },
|
||||
{ key: "events", label: dict.gallery.categories.events }
|
||||
];
|
||||
|
||||
const [activeCategoryKey, setActiveCategoryKey] = useState("all");
|
||||
const [selectedImage, setSelectedImage] = useState<string | null>(null);
|
||||
|
||||
const displayPhotos = dbPhotos && dbPhotos.length > 0
|
||||
? dbPhotos.map(p => ({ id: p.id, categoryKey: p.category, url: p.imageUrl, title: p.title }))
|
||||
: photos;
|
||||
|
||||
const filteredPhotos =
|
||||
activeCategoryKey === "all"
|
||||
? photos
|
||||
: photos.filter((p) => p.categoryKey === activeCategoryKey);
|
||||
? displayPhotos
|
||||
: displayPhotos.filter((p) => p.categoryKey === activeCategoryKey);
|
||||
|
||||
return (
|
||||
<section id="gallery" className="py-24 bg-sand-light text-sea-dark">
|
||||
@@ -76,7 +78,7 @@ export default function Gallery({ dict }: { dict: any }) {
|
||||
>
|
||||
<Image
|
||||
src={photo.url}
|
||||
alt={dict.gallery.categories[photo.categoryKey]}
|
||||
alt={(photo as any).title || dict.gallery.categories[photo.categoryKey] || "Galeri Görseli"}
|
||||
fill
|
||||
className="object-cover transform group-hover:scale-108 transition-transform duration-700"
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Menu, X } from "lucide-react";
|
||||
|
||||
export default function Header({ dict, lang }: { dict: any, lang: string }) {
|
||||
export default function Header({ dict, lang, dbSettings }: { dict: any, lang: string, dbSettings?: any }) {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
|
||||
@@ -31,9 +31,9 @@ export default function Header({ dict, lang }: { dict: any, lang: string }) {
|
||||
<div className="container mx-auto px-4 md:px-6 flex justify-between items-center">
|
||||
<a href="#" className="flex items-center">
|
||||
<img
|
||||
src="/logo.png"
|
||||
src={dbSettings?.logoUrl || "/logo.png"}
|
||||
alt="Moy Beach Logo"
|
||||
className={`h-12 w-auto transition-all duration-300 ${!isScrolled ? "brightness-0 invert opacity-90" : ""}`}
|
||||
className={`h-12 w-auto transition-all duration-300 ${(!isScrolled && !dbSettings?.logoUrl) ? "brightness-0 invert opacity-90" : ""}`}
|
||||
/>
|
||||
</a>
|
||||
|
||||
|
||||
+21
-10
@@ -4,22 +4,33 @@ import { motion } from "framer-motion";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Hero({ dict }: { dict: any }) {
|
||||
export default function Hero({ dict, dbHeroMedia }: { dict: any, dbHeroMedia?: any }) {
|
||||
const words = ["MOY", "BEACH"];
|
||||
|
||||
return (
|
||||
<section className="relative h-screen w-full flex items-center justify-center overflow-hidden">
|
||||
|
||||
{/* Background Image */}
|
||||
{/* Background Media */}
|
||||
<div className="absolute inset-0">
|
||||
<Image
|
||||
src="https://images.unsplash.com/photo-1590523277543-a94d2e4eb00b?auto=format&fit=crop&q=85&w=2400"
|
||||
alt="Moy Beach Akyaka"
|
||||
fill
|
||||
className="object-cover object-center"
|
||||
priority
|
||||
sizes="100vw"
|
||||
/>
|
||||
{dbHeroMedia?.type === 'video' ? (
|
||||
<video
|
||||
src={dbHeroMedia.url}
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
className="w-full h-full object-cover object-center"
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={dbHeroMedia?.url || "https://images.unsplash.com/photo-1590523277543-a94d2e4eb00b?auto=format&fit=crop&q=85&w=2400"}
|
||||
alt="Moy Beach Akyaka"
|
||||
fill
|
||||
className="object-cover object-center"
|
||||
priority
|
||||
sizes="100vw"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Multi-layer gradient for depth */}
|
||||
|
||||
+23
-7
@@ -4,8 +4,8 @@ import { motion } from "framer-motion";
|
||||
import { Waves, UtensilsCrossed, Hotel, CalendarHeart } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Services({ dict }: { dict: any }) {
|
||||
const services = [
|
||||
export default function Services({ dict, dbServices }: { dict: any, dbServices?: any[] }) {
|
||||
const defaultServices = [
|
||||
{
|
||||
title: dict.services.items.beach.title,
|
||||
description: dict.services.items.beach.description,
|
||||
@@ -32,6 +32,15 @@ export default function Services({ dict }: { dict: any }) {
|
||||
},
|
||||
];
|
||||
|
||||
const displayServices = dbServices && dbServices.length > 0
|
||||
? dbServices.map(s => ({
|
||||
title: s.title,
|
||||
description: s.description,
|
||||
icon: null, // Dynamic services don't have static icons right now
|
||||
image: s.iconUrl || "https://images.unsplash.com/photo-1519046904884-53103b34b206?auto=format&fit=crop&q=80&w=800"
|
||||
}))
|
||||
: defaultServices;
|
||||
|
||||
return (
|
||||
<section id="services" className="py-24 bg-white text-sea-dark">
|
||||
<div className="container mx-auto px-6 max-w-7xl">
|
||||
@@ -45,8 +54,13 @@ export default function Services({ dict }: { dict: any }) {
|
||||
<h3 className="text-4xl md:text-5xl font-serif">{dict.services.heading}</h3>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
{services.map((service, index) => (
|
||||
<div className={`grid gap-8 justify-center ${
|
||||
displayServices.length === 1 ? 'grid-cols-1 max-w-sm mx-auto' :
|
||||
displayServices.length === 2 ? 'md:grid-cols-2 max-w-3xl mx-auto' :
|
||||
displayServices.length === 3 ? 'md:grid-cols-3 max-w-5xl mx-auto' :
|
||||
'md:grid-cols-2 lg:grid-cols-4'
|
||||
}`}>
|
||||
{displayServices.map((service, index) => (
|
||||
<motion.div
|
||||
key={service.title}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
@@ -72,9 +86,11 @@ export default function Services({ dict }: { dict: any }) {
|
||||
/>
|
||||
|
||||
{/* Icon badge */}
|
||||
<div className="absolute top-4 left-4 z-20 bg-white/90 backdrop-blur-sm p-3 rounded-full shadow-lg ring-2 ring-coral/10 group-hover:ring-coral/50 transition-all duration-300">
|
||||
<service.icon className="w-5 h-5 text-coral" />
|
||||
</div>
|
||||
{service.icon && (
|
||||
<div className="absolute top-4 left-4 z-20 bg-white/90 backdrop-blur-sm p-3 rounded-full shadow-lg ring-2 ring-coral/10 group-hover:ring-coral/50 transition-all duration-300">
|
||||
<service.icon className="w-5 h-5 text-coral" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user