initial commit: project completion with proper gitignore
This commit is contained in:
65
components/BottomBar.tsx
Normal file
65
components/BottomBar.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
"use client";
|
||||
|
||||
import { Instagram, Twitter } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
interface StatItemProps {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
sublabel: string;
|
||||
}
|
||||
|
||||
function StatItem({ icon, label, sublabel }: StatItemProps) {
|
||||
return (
|
||||
<div className="glass p-4 rounded-2xl flex items-center gap-4 min-w-[200px]">
|
||||
<div className="w-10 h-10 glass rounded-lg flex items-center justify-center text-white/40">
|
||||
{icon}
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-white font-bold text-sm leading-tight">{label}</h4>
|
||||
<p className="text-white/40 text-[10px] font-medium">{sublabel}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function BottomBar() {
|
||||
return (
|
||||
<div className="absolute bottom-0 left-0 w-full px-6 md:px-8 py-6 md:py-10 flex flex-col md:flex-row items-center md:items-end justify-between gap-6 pointer-events-none">
|
||||
{/* Stats Section - Hidden on mobile */}
|
||||
<div className="hidden md:flex items-center gap-4 pointer-events-auto">
|
||||
<StatItem
|
||||
icon={<div className="text-[#1e9a83]">📸</div>}
|
||||
label="500+"
|
||||
sublabel="Tamamlanan Çekim"
|
||||
/>
|
||||
<StatItem
|
||||
icon={<div className="text-[#1e9a83]">🏢</div>}
|
||||
label="150+"
|
||||
sublabel="Mutlu Müşteri"
|
||||
/>
|
||||
<StatItem
|
||||
icon={<div className="text-[#1e9a83]">🚁</div>}
|
||||
label="Drone"
|
||||
sublabel="Profesyonel Çekim"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Socials & Scroll Section */}
|
||||
<div className="flex flex-col items-center md:items-end gap-4 md:gap-6 pointer-events-auto mb-2">
|
||||
<div className="flex items-center gap-6">
|
||||
<span className="text-[10px] font-bold tracking-[0.2em] text-white/30 uppercase">Bizi Takip Edin</span>
|
||||
<div className="flex items-center gap-4 text-white/60">
|
||||
<Link href="#" className="hover:text-white transition-colors"><Instagram className="w-4 h-4" /></Link>
|
||||
<Link href="#" className="hover:text-white transition-colors"><Twitter className="w-4 h-4" /></Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Copyright */}
|
||||
<div className="md:absolute md:bottom-4 md:left-8 text-[10px] text-white/20 font-medium text-center md:text-left">
|
||||
© {new Date().getFullYear()} Muğla Dijital Medya Ajansı. Tüm hakları saklıdır.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
83
components/Capabilities.tsx
Normal file
83
components/Capabilities.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { getFeaturedServices } from "@/app/actions";
|
||||
import * as LucideIcons from "lucide-react";
|
||||
import {
|
||||
ArrowRight,
|
||||
Layers
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function Capabilities() {
|
||||
const [services, setServices] = useState<any[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchServices() {
|
||||
const data = await getFeaturedServices();
|
||||
|
||||
if (data && data.length > 0) {
|
||||
setServices(data);
|
||||
}
|
||||
}
|
||||
fetchServices();
|
||||
}, []);
|
||||
|
||||
const DynamicIcon = ({ name, className }: { name: string, className?: string }) => {
|
||||
const IconComponent = (LucideIcons as any)[name] || Layers;
|
||||
return <IconComponent className={className} />;
|
||||
};
|
||||
|
||||
// Fallback static data if database is empty
|
||||
const displayData = services.length > 0 ? services : [
|
||||
{ title: "Art Direction", description: "Conceptualizing visual narratives that resonate. We define the look and feel before the camera even rolls.", icon_name: "Palette" },
|
||||
{ title: "Cinematography", description: "Capturing light and shadow with state-of-the-art gear. 8K workflows and cinema-grade optics.", icon_name: "Video" },
|
||||
{ title: "Motion Graphics", description: "Adding kinetic energy to static visuals. 2D and 3D animation that enhances the storytelling.", icon_name: "Clapperboard" },
|
||||
{ title: "Color Grading", description: "Setting the mood with precise color science. We ensure your visuals look perfect on every screen.", icon_name: "Contrast" }
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="py-16 md:py-24 px-6 md:px-16 lg:px-24">
|
||||
<div className="max-w-7xl mx-auto flex flex-col lg:flex-row gap-8 md:gap-16 items-start">
|
||||
{/* Left Content */}
|
||||
<div className="lg:w-1/3">
|
||||
<h2 className="text-4xl md:text-5xl font-bold mb-6 text-white italic tracking-tighter uppercase">Yeteneklerimiz</h2>
|
||||
<p className="text-white/60 text-lg mb-8 leading-relaxed">
|
||||
Fikir aşamasından final renk düzenlemesine kadar, görsel üretim sürecinin her adımını takıntılı derecede yüksek standartlarla yönetiyoruz.
|
||||
</p>
|
||||
<Link
|
||||
href="/services"
|
||||
className="group flex items-center gap-2 text-[#1e9a83] font-semibold hover:text-white transition-colors"
|
||||
>
|
||||
Tüm Hizmetleri Gör
|
||||
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Right Grid - Restored to Original Design */}
|
||||
<div className="lg:w-2/3 w-full border border-white/10 rounded-3xl overflow-hidden grid grid-cols-1 md:grid-cols-2">
|
||||
{services.map((service, index) => (
|
||||
<div
|
||||
key={service.id || index}
|
||||
className={`p-10 flex flex-col gap-4 hover:bg-white/[0.02] transition-colors border-white/10
|
||||
${index === 0 ? "border-b md:border-r" : ""}
|
||||
${index === 1 ? "border-b md:border-b" : ""}
|
||||
${index === 2 ? "md:border-r border-b md:border-b-0" : ""}
|
||||
${index === 3 ? "" : ""}
|
||||
`}
|
||||
>
|
||||
<div className="w-12 h-12 rounded-xl bg-[#1e9a83]/10 flex items-center justify-center text-[#1e9a83]">
|
||||
<DynamicIcon name={service.icon_name} className="w-6 h-6" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-white mt-2 uppercase italic tracking-tight">{service.title}</h3>
|
||||
<p className="text-white/40 leading-relaxed text-sm font-medium">
|
||||
{service.description}
|
||||
</p>
|
||||
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
16
components/ClientWrapper.tsx
Normal file
16
components/ClientWrapper.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import { usePathname } from "next/navigation";
|
||||
import Navbar from "./Navbar";
|
||||
|
||||
export default function ClientLayout({ children }: { children: React.ReactNode }) {
|
||||
const pathname = usePathname();
|
||||
const isAdmin = pathname?.startsWith("/admin");
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isAdmin && <Navbar />}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
232
components/Contact.tsx
Normal file
232
components/Contact.tsx
Normal file
@@ -0,0 +1,232 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { MapPin, Mail, Send, ChevronDown, Instagram, Twitter, Linkedin, CheckCircle2, AlertCircle } from "lucide-react";
|
||||
import { getSettings, submitLead } from "@/app/actions";
|
||||
|
||||
export default function Contact() {
|
||||
const [formData, setFormData] = useState({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
projectType: "Drone Çekimi",
|
||||
message: ""
|
||||
});
|
||||
|
||||
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
||||
const [settings, setSettings] = useState<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchSettings() {
|
||||
const data = await getSettings();
|
||||
if (data) setSettings(data);
|
||||
}
|
||||
fetchSettings();
|
||||
}, []);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setStatus('loading');
|
||||
|
||||
try {
|
||||
const result = await submitLead(formData);
|
||||
|
||||
if (result.error) throw new Error(result.error);
|
||||
setStatus('success');
|
||||
setFormData({ firstName: "", lastName: "", email: "", projectType: "Drone Çekimi", message: "" });
|
||||
} catch (error) {
|
||||
console.error('Error submitting form:', error);
|
||||
setStatus('error');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="min-h-screen py-24 px-6 md:px-12 bg-[#f5f5f0]">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Header Area */}
|
||||
<div className="mb-20">
|
||||
<span className="text-[10px] tracking-[0.2em] uppercase text-black/40 block mb-6">İletişim</span>
|
||||
<h1 className="editorial-headline text-4xl md:text-6xl text-black reveal opacity-0 uppercase">
|
||||
Markanızı Dijitalde <br /> <span className="text-primary">Büyütelim.</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-16 items-start border-t border-black/10 pt-16">
|
||||
|
||||
{/* Left Side - Info */}
|
||||
<div className="lg:col-span-5 space-y-16">
|
||||
<div className="space-y-6">
|
||||
<p className="text-black/60 text-[14px] leading-relaxed max-w-sm">
|
||||
{settings?.site_description || "Dijital dünyada fark yaratmaya hazır mısınız? Sosyal medya yönetimi, reklam stratejileri veya SEO çözümleri için vizyonunuzu hayata geçirelim."}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Contact Channels */}
|
||||
<div className="space-y-10">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 border border-black/10 flex items-center justify-center text-primary shrink-0">
|
||||
<MapPin className="w-5 h-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-[10px] tracking-[0.2em] uppercase text-black/40 mb-2">Konum</h3>
|
||||
<p className="text-[13px] text-black font-medium leading-relaxed">
|
||||
{settings?.office_address || "Muğla / Marmaris\nDijital Medya Merkezi"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 border border-black/10 flex items-center justify-center text-primary shrink-0">
|
||||
<Mail className="w-5 h-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-[10px] tracking-[0.2em] uppercase text-black/40 mb-2">E-Posta & Telefon</h3>
|
||||
<p className="text-[13px] text-black font-medium hover:text-primary transition-colors cursor-pointer mb-1">
|
||||
{settings?.contact_email || "hello@mugladijital.com"}
|
||||
</p>
|
||||
<p className="text-[13px] text-black font-medium">
|
||||
{settings?.contact_phone || "+90 (555) 000 00 00"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Social Links */}
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-[10px] tracking-[0.2em] uppercase text-black/40">Sosyal Medya</h3>
|
||||
<div className="flex gap-4">
|
||||
{[
|
||||
{ icon: Instagram, label: "Instagram", href: settings?.instagram_url },
|
||||
{ icon: Twitter, label: "Twitter", href: settings?.twitter_url },
|
||||
{ icon: Linkedin, label: "LinkedIn", href: settings?.linkedin_url }
|
||||
].map((social) => (
|
||||
<a
|
||||
key={social.label}
|
||||
href={social.href || "#"}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-10 h-10 border border-black/10 flex items-center justify-center text-black/40 hover:text-primary hover:border-primary/50 transition-all"
|
||||
aria-label={social.label}
|
||||
>
|
||||
<social.icon className="w-4 h-4" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Form */}
|
||||
<div className="lg:col-span-7">
|
||||
<div className="border border-black/10 p-8 md:p-12 relative overflow-hidden bg-white/30 backdrop-blur-sm">
|
||||
{status === 'success' ? (
|
||||
<div className="flex flex-col items-center justify-center text-center py-12 space-y-6">
|
||||
<div className="w-16 h-16 border border-primary/20 flex items-center justify-center text-primary">
|
||||
<CheckCircle2 className="w-8 h-8" />
|
||||
</div>
|
||||
<h3 className="editorial-headline text-2xl text-black uppercase">Mesajınız Alındı!</h3>
|
||||
<p className="text-black/40 text-[12px] max-w-sm">
|
||||
Ekibimiz en kısa sürede sizinle iletişime geçecektir.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setStatus('idle')}
|
||||
className="button-secondary text-[11px]"
|
||||
>
|
||||
Yeni Mesaj Gönder
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="space-y-10">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-10">
|
||||
<div className="space-y-3">
|
||||
<label className="text-[10px] tracking-[0.2em] uppercase text-black/40 ml-1">Adınız</label>
|
||||
<input
|
||||
required
|
||||
type="text"
|
||||
value={formData.firstName}
|
||||
onChange={(e) => setFormData({ ...formData, firstName: e.target.value })}
|
||||
placeholder="Adınız"
|
||||
className="w-full bg-transparent border-b border-black/10 py-3 text-[13px] text-black placeholder:text-black/10 outline-none focus:border-primary transition-colors"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<label className="text-[10px] tracking-[0.2em] uppercase text-black/40 ml-1">Soyadınız</label>
|
||||
<input
|
||||
required
|
||||
type="text"
|
||||
value={formData.lastName}
|
||||
onChange={(e) => setFormData({ ...formData, lastName: e.target.value })}
|
||||
placeholder="Soyadınız"
|
||||
className="w-full bg-transparent border-b border-black/10 py-3 text-[13px] text-black placeholder:text-black/10 outline-none focus:border-primary transition-colors"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<label className="text-[10px] tracking-[0.2em] uppercase text-black/40 ml-1">E-posta Adresiniz</label>
|
||||
<input
|
||||
required
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||
placeholder="ornek@email.com"
|
||||
className="w-full bg-transparent border-b border-black/10 py-3 text-[13px] text-black placeholder:text-black/10 outline-none focus:border-primary transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 relative">
|
||||
<label className="text-[10px] tracking-[0.2em] uppercase text-black/40 ml-1">Hizmet Türü</label>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={formData.projectType}
|
||||
onChange={(e) => setFormData({ ...formData, projectType: e.target.value })}
|
||||
className="w-full bg-transparent border-b border-black/10 py-3 text-[13px] text-black/60 appearance-none outline-none focus:border-primary transition-colors cursor-pointer"
|
||||
>
|
||||
<option>Drone Çekimi</option>
|
||||
<option>Fotoğraf & Video Çekimi</option>
|
||||
<option>Düğün / Nişan Çekimi</option>
|
||||
<option>Sosyal Medya Yönetimi</option>
|
||||
<option>Meta Reklam Yönetimi</option>
|
||||
<option>Google Reklam Yönetimi</option>
|
||||
<option>Web Site Tasarımı</option>
|
||||
<option>SEO Optimizasyonu</option>
|
||||
<option>Otel Tanıtım Çekimi</option>
|
||||
</select>
|
||||
<ChevronDown className="absolute right-0 top-1/2 -translate-y-1/2 w-4 h-4 text-black/20 pointer-events-none" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<label className="text-[10px] tracking-[0.2em] uppercase text-black/40 ml-1">Mesajınız</label>
|
||||
<textarea
|
||||
required
|
||||
rows={4}
|
||||
value={formData.message}
|
||||
onChange={(e) => setFormData({ ...formData, message: e.target.value })}
|
||||
placeholder="Projenizden bahsedin..."
|
||||
className="w-full bg-transparent border-b border-black/10 py-3 text-[13px] text-black placeholder:text-black/10 outline-none focus:border-primary transition-colors resize-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{status === 'error' && (
|
||||
<div className="flex items-center gap-2 text-red-600 text-[11px] font-bold bg-red-50 p-4 border border-red-100">
|
||||
<AlertCircle className="w-4 h-4" />
|
||||
Bir hata oluştu. Lütfen tekrar deneyin.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
disabled={status === 'loading'}
|
||||
className="button-primary w-full justify-center group disabled:opacity-50"
|
||||
>
|
||||
{status === 'loading' ? 'Gönderiliyor...' : 'Ücretsiz Analiz Talebi Gönder'}
|
||||
<Send className="w-3 h-3 group-hover:translate-x-1 group-hover:-translate-y-1 transition-transform" />
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
39
components/DynamicLogo.tsx
Normal file
39
components/DynamicLogo.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
"use client";
|
||||
|
||||
interface DynamicLogoProps {
|
||||
src: string;
|
||||
color?: string;
|
||||
width?: string;
|
||||
height?: string;
|
||||
size?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function DynamicLogo({
|
||||
src,
|
||||
color = "currentColor",
|
||||
width = "100%",
|
||||
height = "40px",
|
||||
size = "contain",
|
||||
className = ""
|
||||
}: DynamicLogoProps) {
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
maskImage: `url(${src})`,
|
||||
WebkitMaskImage: `url(${src})`,
|
||||
maskRepeat: "no-repeat",
|
||||
maskPosition: "center",
|
||||
maskSize: size,
|
||||
WebkitMaskRepeat: "no-repeat",
|
||||
WebkitMaskPosition: "center",
|
||||
WebkitMaskSize: size,
|
||||
width: width,
|
||||
height: height,
|
||||
transition: "background-color 0.3s ease",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
91
components/Footer.tsx
Normal file
91
components/Footer.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { getSettings } from "@/app/actions";
|
||||
import Link from "next/link";
|
||||
import DynamicLogo from "./DynamicLogo";
|
||||
|
||||
export default function Footer() {
|
||||
const [settings, setSettings] = useState<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchSettings() {
|
||||
const data = await getSettings();
|
||||
if (data) setSettings(data);
|
||||
}
|
||||
fetchSettings();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<footer className="border-t border-black/10">
|
||||
{/* Main Footer Content */}
|
||||
<div className="max-w-7xl mx-auto px-6 md:px-12 py-16 md:py-20">
|
||||
<div className="grid grid-cols-1 md:grid-cols-12 gap-12 md:gap-8">
|
||||
|
||||
{/* Left Column - Logo & Description */}
|
||||
<div className="md:col-span-5 flex flex-col justify-between gap-12">
|
||||
{/* Logo */}
|
||||
<Link href="/" className="inline-block">
|
||||
<div className="relative w-40 h-12">
|
||||
<DynamicLogo
|
||||
src="/logo.png"
|
||||
color="black"
|
||||
width="100%"
|
||||
height="100%"
|
||||
size="contain"
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-[11px] text-black/40 leading-[1.8] max-w-xs">
|
||||
{settings?.footer_description || "Muğla Dijital olarak markaların dijital dünyada özgün bir şekilde var olmasını sağlıyoruz. Drone çekimi, video prodüksiyon ve sosyal medya yönetimi ile gerçek bağlantılar kuruyoruz."}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Center Column - Menu label + divider */}
|
||||
<div className="md:col-span-2 flex flex-col items-center gap-6 hidden md:flex">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="w-px h-8 bg-black/10" />
|
||||
<span className="text-[10px] tracking-[0.2em] uppercase text-black/30">Menu</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Navigation Links */}
|
||||
<div className="md:col-span-5 flex flex-col items-start md:items-end gap-3">
|
||||
{[
|
||||
{ label: "Çalışmalar", href: "/works" },
|
||||
{ label: "Hizmetler", href: "/services" },
|
||||
{ label: "Partnerler", href: "/partners" },
|
||||
{ label: "Hakkımızda", href: "/about" },
|
||||
{ label: "İletişim", href: "/contact" },
|
||||
].map((item) => (
|
||||
<Link
|
||||
key={item.label}
|
||||
href={item.href}
|
||||
className="text-2xl md:text-4xl font-extrabold uppercase tracking-tight text-black hover:text-primary transition-colors leading-tight"
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Bar */}
|
||||
<div className="border-t border-black/10">
|
||||
<div className="max-w-7xl mx-auto px-6 md:px-12 py-6 flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
||||
{/* Copyright */}
|
||||
<p className="text-[10px] text-black/25 tracking-[0.05em]">
|
||||
© {settings?.site_name || "Muğla Dijital"} {new Date().getFullYear()}. Dijital çözümler üreten ajans.
|
||||
</p>
|
||||
|
||||
{/* Credit */}
|
||||
<a href="https://ayris.tech" target="_blank" rel="noopener noreferrer" className="text-[10px] text-black/25 tracking-[0.05em] hover:text-primary transition-colors">
|
||||
created by ayris.tech
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
64
components/Hero.tsx
Normal file
64
components/Hero.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export default function Hero() {
|
||||
return (
|
||||
<section className="pt-32 pb-0 px-6 md:px-12">
|
||||
{/* Main Headline */}
|
||||
<div className="max-w-7xl mx-auto py-16 md:py-24">
|
||||
<motion.h1
|
||||
initial={{ y: 40, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ duration: 1, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="editorial-headline text-4xl md:text-6xl lg:text-[5.5rem] text-black uppercase"
|
||||
>
|
||||
Dijital Varlık ve Görsel<br />
|
||||
Hikaye <span className="text-primary">Mimarlığı.</span>
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ duration: 1, delay: 0.3, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="text-[11px] md:text-[13px] tracking-[0.15em] text-black/40 max-w-2xl leading-[2] mt-8"
|
||||
>
|
||||
Profesyonel prodüksiyondan akıllı reklam yönetimine kadar markanızın tüm dijital ekosistemini estetikle şekillendiriyoruz.
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<motion.div
|
||||
initial={{ scaleX: 0 }}
|
||||
animate={{ scaleX: 1 }}
|
||||
transition={{ duration: 1.5, delay: 0.5, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="section-divider origin-left"
|
||||
/>
|
||||
|
||||
{/* Bottom info row */}
|
||||
<motion.div
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ duration: 1, delay: 0.8, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="max-w-7xl mx-auto grid grid-cols-1 md:grid-cols-3 py-8 gap-8"
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-[10px] tracking-[0.2em] uppercase text-black/40">Konum</span>
|
||||
<span className="text-[11px] text-black/70">Muğla, Türkiye</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-[10px] tracking-[0.2em] uppercase text-black/40">Hizmetler</span>
|
||||
<span className="text-[11px] text-black/70">Drone · Video · Sosyal Medya · Reklam</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 md:justify-end">
|
||||
<Link href="/contact" className="button-primary group">
|
||||
Teklif Al
|
||||
<ArrowRight className="w-3 h-3 group-hover:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
83
components/Navbar.tsx
Normal file
83
components/Navbar.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import DynamicLogo from "./DynamicLogo";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export default function Navbar() {
|
||||
return (
|
||||
<motion.nav
|
||||
initial={{ y: -100 }}
|
||||
animate={{ y: 0 }}
|
||||
transition={{ duration: 1, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="fixed top-0 left-0 w-full z-50 bg-[#f5f5f0]/90 backdrop-blur-md border-b border-black/10"
|
||||
>
|
||||
<div className="flex items-center justify-between px-6 md:px-12 py-5">
|
||||
{/* Logo */}
|
||||
<Link href="/" className="flex items-center gap-3">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="relative w-36 h-10"
|
||||
>
|
||||
<DynamicLogo
|
||||
src="/logo.png"
|
||||
color="black"
|
||||
width="100%"
|
||||
height="100%"
|
||||
size="contain"
|
||||
/>
|
||||
</motion.div>
|
||||
</Link>
|
||||
|
||||
{/* Center Nav */}
|
||||
<div className="hidden lg:flex items-center">
|
||||
{[
|
||||
{ label: "Çalışmalar", href: "/works" },
|
||||
{ label: "Hizmetler", href: "/services" },
|
||||
{ label: "Partnerler", href: "/partners" },
|
||||
{ label: "Hakkımızda", href: "/about" },
|
||||
{ label: "İletişim", href: "/contact" },
|
||||
].map((item, idx) => (
|
||||
<div key={item.label} className="flex items-center">
|
||||
{/* Diagonal separator */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scaleY: 0 }}
|
||||
animate={{ opacity: 1, scaleY: 1 }}
|
||||
transition={{ delay: 0.2 + idx * 0.1 }}
|
||||
className="w-16 h-10 relative mx-1"
|
||||
>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="w-px h-14 bg-black/10 rotate-[-25deg]" />
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.3 + idx * 0.1 }}
|
||||
>
|
||||
<Link
|
||||
href={item.href}
|
||||
className="text-[11px] tracking-[0.15em] uppercase text-black/60 hover:text-black transition-colors nav-link-hover px-3 py-2"
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right - Brand name */}
|
||||
<motion.span
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 1 }}
|
||||
className="text-[13px] font-bold tracking-[0.1em] uppercase text-black"
|
||||
>
|
||||
Muğla Dijital
|
||||
</motion.span>
|
||||
</div>
|
||||
</motion.nav>
|
||||
);
|
||||
}
|
||||
120
components/Partners.tsx
Normal file
120
components/Partners.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { getPartners } from "@/app/actions";
|
||||
import { motion } from "framer-motion";
|
||||
import DynamicLogo from "./DynamicLogo";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function Partners() {
|
||||
const [partners, setPartners] = useState<any[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchPartners() {
|
||||
const data = await getPartners();
|
||||
setPartners(data || []);
|
||||
}
|
||||
fetchPartners();
|
||||
}, []);
|
||||
|
||||
if (partners.length === 0) return null;
|
||||
|
||||
return (
|
||||
<section className="border-y border-black/10 bg-[#f5f5f0] overflow-hidden">
|
||||
<div className="max-w-7xl mx-auto px-6 md:px-12 flex items-stretch min-h-[100px] md:min-h-[140px]">
|
||||
{/* Left Label Section */}
|
||||
<motion.div
|
||||
initial={{ x: -20, opacity: 0 }}
|
||||
whileInView={{ x: 0, opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
className="flex items-center gap-4 py-6 md:py-8 pr-8 border-r border-black/10 shrink-0"
|
||||
>
|
||||
<span className="text-[9px] md:text-[10px] tracking-[0.2em] uppercase text-black/30 font-bold">Partners</span>
|
||||
</motion.div>
|
||||
|
||||
{/* Partners List Section */}
|
||||
<div className="flex-1 flex items-center pl-4 md:pl-8 pr-4 md:pr-8 overflow-hidden">
|
||||
<div className="flex flex-nowrap items-center justify-start w-full gap-4 md:gap-10">
|
||||
{partners.slice(0, 5).map((partner, index) => {
|
||||
const content = (
|
||||
<motion.div
|
||||
initial={{ y: 10, opacity: 0 }}
|
||||
whileInView={{ y: 0, opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1 + 0.5 }}
|
||||
className="relative flex items-center justify-center group"
|
||||
>
|
||||
{partner.logo ? (
|
||||
<div className="relative h-[60px] md:h-[75px] w-[90px] md:w-[130px] overflow-hidden">
|
||||
<motion.div
|
||||
whileHover={{ y: "-50%" }}
|
||||
transition={{ duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="flex flex-col items-center h-[200%]"
|
||||
>
|
||||
{/* Normal State */}
|
||||
<div className="h-1/2 w-full flex items-center justify-center opacity-70">
|
||||
<DynamicLogo
|
||||
src={partner.logo}
|
||||
color="black"
|
||||
width="100%"
|
||||
height="100%"
|
||||
size="220%"
|
||||
/>
|
||||
</div>
|
||||
{/* Hover State */}
|
||||
<div className="h-1/2 w-full flex items-center justify-center">
|
||||
<DynamicLogo
|
||||
src={partner.logo}
|
||||
color="#ff5c00" // primary color
|
||||
width="100%"
|
||||
height="100%"
|
||||
size="220%"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-[14px] md:text-[18px] tracking-tight font-medium text-black/30 whitespace-nowrap">
|
||||
{partner.name}
|
||||
</span>
|
||||
)}
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
if (partner.project_slug) {
|
||||
return (
|
||||
<Link key={index} href={`/works/${partner.project_slug}`}>
|
||||
{content}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return <div key={index}>{content}</div>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right CTA Section */}
|
||||
<motion.div
|
||||
initial={{ x: 20, opacity: 0 }}
|
||||
whileInView={{ x: 0, opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
className="flex items-center py-6 md:py-8 pl-8 border-l border-black/10 shrink-0"
|
||||
>
|
||||
<a
|
||||
href="/partners"
|
||||
className="text-[10px] md:text-[11px] tracking-[0.1em] font-bold uppercase text-black hover:text-primary transition-colors flex items-center gap-2 group"
|
||||
>
|
||||
Hepsini Gör
|
||||
<motion.span
|
||||
animate={{ x: [0, 5, 0] }}
|
||||
transition={{ repeat: Infinity, duration: 2 }}
|
||||
>
|
||||
→
|
||||
</motion.span>
|
||||
</a>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
92
components/PartnersList.tsx
Normal file
92
components/PartnersList.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import DynamicLogo from "./DynamicLogo";
|
||||
import Link from "next/link";
|
||||
|
||||
interface Partner {
|
||||
id: number;
|
||||
name: string;
|
||||
logo?: string;
|
||||
url?: string;
|
||||
project_slug?: string;
|
||||
}
|
||||
|
||||
export default function PartnersList({ partners }: { partners: Partner[] }) {
|
||||
return (
|
||||
<section className="py-24 px-6 md:px-12">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 border-t border-l border-black/10">
|
||||
{partners.map((partner, index) => {
|
||||
const card = (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.05 }}
|
||||
className="p-4 md:p-8 border-r border-b border-black/10 flex items-center justify-center transition-all group relative bg-white/10 overflow-hidden cursor-pointer"
|
||||
>
|
||||
<div className="relative h-[140px] md:h-[180px] w-full overflow-hidden">
|
||||
<motion.div
|
||||
whileHover={{ y: "-50%" }}
|
||||
transition={{ duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="flex flex-col items-center h-[200%]"
|
||||
>
|
||||
{/* First state */}
|
||||
<div className="h-1/2 w-full flex items-center justify-center opacity-70 group-hover:opacity-100 transition-all">
|
||||
{partner.logo ? (
|
||||
<DynamicLogo
|
||||
src={partner.logo}
|
||||
color="black"
|
||||
width="100%"
|
||||
height="100%"
|
||||
size="140%"
|
||||
/>
|
||||
) : (
|
||||
<span className="editorial-headline text-xl md:text-2xl text-black uppercase">
|
||||
{partner.name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Second state (Slot effect) */}
|
||||
<div className="h-1/2 w-full flex items-center justify-center">
|
||||
{partner.logo ? (
|
||||
<DynamicLogo
|
||||
src={partner.logo}
|
||||
color="#ff5c00" // primary color
|
||||
width="100%"
|
||||
height="100%"
|
||||
size="140%"
|
||||
/>
|
||||
) : (
|
||||
<span className="editorial-headline text-xl md:text-2xl text-primary uppercase">
|
||||
{partner.name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Subtle diagonal hover effect */}
|
||||
<div className="absolute top-0 right-0 w-8 h-8 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none">
|
||||
<div className="absolute top-0 right-0 w-px h-12 bg-black/5 rotate-[-45deg] origin-top-right" />
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
if (partner.project_slug) {
|
||||
return (
|
||||
<Link key={partner.id} href={`/works/${partner.project_slug}`} className="block">
|
||||
{card}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return <div key={partner.id}>{card}</div>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
108
components/SelectedWorks.tsx
Normal file
108
components/SelectedWorks.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { ArrowRight, Loader2 } from "lucide-react";
|
||||
import { getFeaturedProjects } from "@/app/actions";
|
||||
|
||||
interface ProjectCardProps {
|
||||
hero_image: string;
|
||||
category: string;
|
||||
title: string;
|
||||
year: string;
|
||||
subtitle: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
function ProjectCard({ hero_image, category, title, year, subtitle, slug }: ProjectCardProps) {
|
||||
return (
|
||||
<Link href={`/works/${slug}`} className="group cursor-pointer block">
|
||||
<div className="space-y-5">
|
||||
<div className="relative aspect-video overflow-hidden bg-black/5 border border-black/10">
|
||||
<Image
|
||||
src={hero_image || "https://images.unsplash.com/photo-1550745165-9bc0b252726f"}
|
||||
alt={title}
|
||||
fill
|
||||
className="object-cover transition-all duration-700 group-hover:scale-105 grayscale group-hover:grayscale-0"
|
||||
/>
|
||||
|
||||
{/* Category Badge */}
|
||||
<div className="absolute top-4 right-4">
|
||||
<span className="bg-white/90 backdrop-blur-sm border border-black/10 px-3 py-1 text-[9px] tracking-[0.15em] uppercase text-black/60">
|
||||
{category}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content Below Image */}
|
||||
<div>
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<h3 className="text-[14px] tracking-[0.05em] uppercase text-black group-hover:text-primary transition-colors leading-tight">
|
||||
{title}
|
||||
</h3>
|
||||
<span className="text-[10px] text-black/30 tracking-[0.1em] uppercase mt-0.5">
|
||||
{year}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-[11px] text-black/40 leading-relaxed line-clamp-1">
|
||||
{subtitle}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SelectedWorks() {
|
||||
const [projects, setProjects] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchFeatured() {
|
||||
setLoading(true);
|
||||
const data = await getFeaturedProjects();
|
||||
|
||||
if (data && data.length > 0) {
|
||||
setProjects(data);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
fetchFeatured();
|
||||
}, []);
|
||||
|
||||
if (loading) return (
|
||||
<div className="py-32 flex justify-center">
|
||||
<Loader2 className="w-8 h-8 text-primary animate-spin" />
|
||||
</div>
|
||||
);
|
||||
|
||||
if (projects.length === 0) return null;
|
||||
|
||||
return (
|
||||
<section className="py-24 px-6 md:px-12 border-t border-black/10">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Header Section */}
|
||||
<div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-8 mb-16">
|
||||
<div>
|
||||
<span className="text-[10px] tracking-[0.2em] uppercase text-black/40 block mb-4">Son Projeler</span>
|
||||
<h2 className="editorial-headline text-4xl md:text-5xl text-black">
|
||||
Çalışma Örnekleri
|
||||
</h2>
|
||||
</div>
|
||||
<Link href="/works" className="button-primary group">
|
||||
Tüm Portfolyo
|
||||
<ArrowRight className="w-3 h-3 group-hover:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10">
|
||||
{projects.map((project, index) => (
|
||||
<ProjectCard key={index} {...project} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
128
components/ServicesClient.tsx
Normal file
128
components/ServicesClient.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import * as LucideIcons from "lucide-react";
|
||||
import {
|
||||
ArrowRight,
|
||||
Layers,
|
||||
Search,
|
||||
Clapperboard,
|
||||
Camera,
|
||||
Zap
|
||||
} from "lucide-react";
|
||||
import Footer from "@/components/Footer";
|
||||
|
||||
const processSteps = [
|
||||
{ icon: Search, title: "Analiz", desc: "İşletmenizi ve hedef kitlenizi analiz ediyoruz." },
|
||||
{ icon: Clapperboard, title: "Strateji", desc: "Size özel dijital strateji planlıyoruz." },
|
||||
{ icon: Camera, title: "Uygulama", desc: "Çekim, tasarım ve kampanya yönetimi." },
|
||||
{ icon: Zap, title: "Raporlama", desc: "Sonuçları ölçüyor ve optimize ediyoruz." }
|
||||
];
|
||||
|
||||
export default function ServicesClient({ services: initialServices }: { services: any[] }) {
|
||||
const [services] = useState<any[]>(initialServices);
|
||||
|
||||
const DynamicIcon = ({ name, className }: { name: string, className?: string }) => {
|
||||
const IconComponent = (LucideIcons as any)[name] || Layers;
|
||||
return <IconComponent className={className} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-[#f5f5f0] text-black pt-24">
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="pt-24 pb-16 px-6 md:px-12 border-b border-black/10">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<span className="text-[10px] tracking-[0.2em] uppercase text-black/40 block mb-6">Hizmetlerimiz</span>
|
||||
<h1 className="editorial-headline text-4xl md:text-6xl lg:text-[5.5rem] text-black reveal opacity-0 uppercase">
|
||||
Drone · Video <br /> <span className="text-primary">Reklam · Dijital</span>
|
||||
</h1>
|
||||
<p className="text-black/40 text-[14px] max-w-2xl leading-relaxed mt-10 reveal reveal-delayed-1 opacity-0">
|
||||
Profesyonel drone çekimlerinden sosyal medya yönetimine, Google reklamlarından web tasarıma — işletmenizin dijital dünyada parlaması için ihtiyacınız olan her şey burada.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Services Grid - Editorial Bento */}
|
||||
<section className="py-24 px-6 md:px-12 border-b border-black/10">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 border-t border-l border-black/10">
|
||||
{services.map((item) => (
|
||||
<div key={item.id} className="p-10 border-r border-b border-black/10 relative group hover:bg-black/[0.01] transition-colors overflow-hidden flex flex-col justify-between min-h-[350px]">
|
||||
<div>
|
||||
<div className="w-10 h-10 border border-black/10 flex items-center justify-center text-primary mb-8">
|
||||
<DynamicIcon name={item.icon_name} className="w-5 h-5" />
|
||||
</div>
|
||||
<h3 className="editorial-headline text-2xl text-black mb-4 uppercase">{item.title}</h3>
|
||||
<p className="text-[12px] text-black/40 leading-relaxed font-medium mb-8">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
<ul className="space-y-3">
|
||||
{(item.sub_services || []).slice(0, 3).map((sub: string) => (
|
||||
<li key={sub} className="flex items-center gap-3 text-[9px] font-bold tracking-widest text-black/20 uppercase">
|
||||
<div className="w-1 h-1 rounded-full bg-primary" />
|
||||
{sub}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* Subtle diagonal on hover */}
|
||||
<div className="absolute top-0 right-0 w-16 h-16 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<div className="absolute top-0 right-0 w-px h-24 bg-black/5 rotate-[-45deg] origin-top-right" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* The Process - Editorial Layout */}
|
||||
<section className="py-24 px-6 md:px-12 border-b border-black/10">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="mb-20">
|
||||
<span className="text-[10px] tracking-[0.2em] uppercase text-black/40 block mb-4">İş Akışımız</span>
|
||||
<h2 className="editorial-headline text-3xl md:text-5xl text-black uppercase">Süreç Nasıl İlerler?</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 border-t border-black/10">
|
||||
{processSteps.map((step, idx) => (
|
||||
<div key={step.title} className="py-12 md:px-8 border-b md:border-b-0 md:border-r border-black/10 last:border-r-0 group">
|
||||
<div className="text-4xl font-black text-black/5 mb-8 group-hover:text-primary/20 transition-colors">0{idx + 1}</div>
|
||||
<div className="w-8 h-8 border border-black/10 flex items-center justify-center text-primary mb-6">
|
||||
<step.icon className="w-4 h-4" />
|
||||
</div>
|
||||
<h3 className="editorial-headline text-lg text-black mb-4 uppercase">{step.title}</h3>
|
||||
<p className="text-[12px] text-black/40 leading-relaxed">{step.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<section className="py-24 px-6 text-center">
|
||||
<div className="max-w-4xl mx-auto space-y-12">
|
||||
<h2 className="editorial-headline text-4xl md:text-6xl text-black uppercase leading-tight">
|
||||
Sıradışı Bir Şeyler <br /> <span className="text-primary">Yaratmaya Hazır Mısınız?</span>
|
||||
</h2>
|
||||
<p className="text-black/40 text-[13px] tracking-[0.1em] max-w-xl mx-auto">
|
||||
Drone çekimi, sosyal medya yönetimi veya dijital reklam — ne ihtiyacınız varsa, birlikte çözüm üretelim.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-6">
|
||||
<Link href="/contact" className="button-primary px-10">
|
||||
Hemen Başlayalım
|
||||
</Link>
|
||||
<Link href="/works" className="button-secondary px-10">
|
||||
Portfolyomuz
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
143
components/ServicesGrid.tsx
Normal file
143
components/ServicesGrid.tsx
Normal file
@@ -0,0 +1,143 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { getFeaturedServices } from "@/app/actions";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export default function ServicesGrid() {
|
||||
const [services, setServices] = useState<any[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchServices() {
|
||||
const data = await getFeaturedServices();
|
||||
if (data && data.length > 0) setServices(data);
|
||||
}
|
||||
fetchServices();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section className="border-t border-black/10">
|
||||
<div className="max-w-7xl mx-auto px-6 md:px-12">
|
||||
{/* Grid - Editorial bento layout */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-12 border-b border-black/10">
|
||||
|
||||
{/* Left large cell with image */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 1 }}
|
||||
className="md:col-span-4 border-b md:border-b-0 md:border-r border-black/10 relative overflow-hidden group"
|
||||
>
|
||||
<div className="relative aspect-square md:aspect-auto md:h-full min-h-[400px]">
|
||||
<Image
|
||||
src="https://images.unsplash.com/photo-1473968512647-3e447244af8f?q=80&w=800"
|
||||
alt="Drone çekimi"
|
||||
fill
|
||||
className="object-cover grayscale group-hover:grayscale-0 transition-all duration-700 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Right content area */}
|
||||
<div className="md:col-span-8 grid grid-cols-1 md:grid-cols-2">
|
||||
{/* Top-left cell: Services list */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.2 }}
|
||||
className="p-8 md:p-12 border-b border-r border-black/10 flex flex-col justify-between min-h-[200px]"
|
||||
>
|
||||
<div>
|
||||
<span className="text-[10px] tracking-[0.2em] uppercase text-black/40 block mb-6">Hizmetler</span>
|
||||
<div className="space-y-2">
|
||||
{["Sosyal Medya Yönetimi", "Drone Çekimi", "Video Prodüksiyon", "Reklam Yönetimi", "SEO"].map((item) => (
|
||||
<div key={item} className="text-[12px] text-black/60 hover:text-primary transition-colors cursor-pointer">
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Top-right cell: Capabilities */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.4 }}
|
||||
className="p-8 md:p-12 border-b border-black/10 flex flex-col justify-between min-h-[200px]"
|
||||
>
|
||||
<div>
|
||||
<span className="text-[10px] tracking-[0.2em] uppercase text-black/40 block mb-6">Çözümler</span>
|
||||
<div className="space-y-2">
|
||||
{["Fotoğraf Çekimi", "Web Tasarım", "Otel Tanıtım", "Düğün Çekimi", "İçerik Üretimi"].map((item) => (
|
||||
<div key={item} className="text-[12px] text-black/60 hover:text-primary transition-colors cursor-pointer">
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Bottom-left cell with diagonal */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.6 }}
|
||||
className="p-8 md:p-12 border-r border-black/10 relative cell-diagonal min-h-[200px] flex items-end"
|
||||
>
|
||||
<h3 className="editorial-headline text-2xl md:text-3xl text-black uppercase">
|
||||
Profesyonel<br />
|
||||
Prodüksiyon
|
||||
</h3>
|
||||
</motion.div>
|
||||
|
||||
{/* Bottom-right cell */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.8 }}
|
||||
className="p-8 md:p-12 relative cell-diagonal min-h-[200px] flex items-end"
|
||||
>
|
||||
<h3 className="editorial-headline text-2xl md:text-3xl text-black uppercase">
|
||||
Dijital<br />
|
||||
Pazarlama
|
||||
</h3>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description row */}
|
||||
<div className="py-8 grid grid-cols-1 md:grid-cols-12 gap-8">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="md:col-span-4 flex items-center"
|
||||
>
|
||||
<p className="text-[12px] text-black/50 leading-relaxed">
|
||||
Drone çekimlerinden sosyal medya yönetimine, markanızın dijitaldeki her ihtiyacını karşılıyoruz.
|
||||
</p>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="md:col-span-4 md:col-start-9 flex items-center md:justify-end"
|
||||
>
|
||||
<Link href="/services" className="button-primary group">
|
||||
Tüm Hizmetler
|
||||
<span className="group-hover:translate-x-1 transition-transform">→</span>
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
193
components/WorkDetailClient.tsx
Normal file
193
components/WorkDetailClient.tsx
Normal file
@@ -0,0 +1,193 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { ArrowRight, Share2 } from "lucide-react";
|
||||
import Footer from "@/components/Footer";
|
||||
|
||||
export default function WorkDetailClient({ project, nextProject }: { project: any, nextProject: any }) {
|
||||
const isInstagram = (url: string) => {
|
||||
return url.includes('instagram.com');
|
||||
};
|
||||
|
||||
const isVideo = (url: string) => {
|
||||
return url.includes('youtube.com') || url.includes('vimeo.com') || url.endsWith('.mp4');
|
||||
};
|
||||
|
||||
// Ultra-safety check for gallery and category arrays
|
||||
const parseSafe = (data: any) => {
|
||||
let current = data;
|
||||
try {
|
||||
// Aggressively parse up to 3 times to handle double-quotes and double-encoding
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (typeof current === 'string' && (current.trim().startsWith('[') || current.trim().startsWith('"'))) {
|
||||
current = JSON.parse(current);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Array.isArray(current) ? current : (typeof current === 'string' && current ? [current] : []);
|
||||
} catch (e) {
|
||||
return Array.isArray(current) ? current : (typeof current === 'string' && current ? [current] : []);
|
||||
}
|
||||
};
|
||||
|
||||
const gallery = parseSafe(project.gallery);
|
||||
const categories = parseSafe(project.category);
|
||||
|
||||
// Helper to get Instagram embed URL
|
||||
const getInstaEmbedUrl = (url: string) => {
|
||||
const cleanUrl = url.split('?')[0];
|
||||
return `${cleanUrl}${cleanUrl.endsWith('/') ? '' : '/'}embed`;
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-[#f5f5f0] text-black pt-24">
|
||||
|
||||
{/* 1. Editorial Header Section */}
|
||||
<section className="pt-32 pb-20 px-6 md:px-12 border-b border-black/10">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Top Line */}
|
||||
<div className="w-full h-px bg-black/10 mb-20" />
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-16 items-start">
|
||||
{/* Left: Client Name */}
|
||||
<div className="lg:col-span-5">
|
||||
<span className="text-[10px] tracking-[0.3em] uppercase text-black/30 block mb-6">
|
||||
{categories.join(" / ")} / {project.year}
|
||||
</span>
|
||||
<h1 className="editorial-headline text-5xl md:text-7xl lg:text-8xl text-black uppercase leading-none">
|
||||
{project.client || project.title}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Middle: Minimalist Line (Reference style) */}
|
||||
<div className="hidden lg:block lg:col-span-2 pt-12">
|
||||
<div className="w-20 h-px bg-black/40 mx-auto" />
|
||||
</div>
|
||||
|
||||
{/* Right: Description */}
|
||||
<div className="lg:col-span-5 pt-2 lg:pt-8">
|
||||
<p className="text-[15px] md:text-[18px] text-black/70 leading-[1.8] font-medium italic">
|
||||
{project.narrative_desc || project.subtitle}
|
||||
</p>
|
||||
<div className="mt-12 flex flex-wrap gap-x-8 gap-y-4">
|
||||
<div>
|
||||
<h4 className="text-[9px] tracking-[0.2em] uppercase text-black/30 mb-1">Rol</h4>
|
||||
<span className="text-[11px] font-bold uppercase">{project.role || "Kreatif Direktörlük"}</span>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-[9px] tracking-[0.2em] uppercase text-black/30 mb-1">Konum</h4>
|
||||
<span className="text-[11px] font-bold uppercase">{project.location || "Muğla / Dijital"}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Line */}
|
||||
<div className="w-full h-px bg-black/10 mt-20" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 2. Project Feed (Gallery & Instagram) */}
|
||||
<section className="py-24 px-6 md:px-12">
|
||||
<div className="max-w-4xl mx-auto space-y-32">
|
||||
{/* Hero Image First */}
|
||||
<div className="relative aspect-[16/10] w-full overflow-hidden border border-black/5 shadow-2xl bg-[#e2d1c1] p-1">
|
||||
<div className="relative w-full h-full overflow-hidden">
|
||||
<Image
|
||||
src={project.hero_image || "https://images.unsplash.com/photo-1550745165-9bc0b252726f"}
|
||||
alt={project.title}
|
||||
fill
|
||||
className="object-cover"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Gallery / Instagram Feed */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-12 gap-y-24 mt-24">
|
||||
{gallery.map((item: string, idx: number) => (
|
||||
<div key={idx} className="space-y-8 animate-reveal">
|
||||
<div className="flex items-center gap-4 text-[10px] font-bold text-black/20 tracking-widest uppercase">
|
||||
<span>0{idx + 1}</span>
|
||||
<div className="h-px flex-grow bg-black/5" />
|
||||
<span>{isInstagram(item) ? "INSTAGRAM FEED" : "PRODUCTION VIEW"}</span>
|
||||
</div>
|
||||
|
||||
{isInstagram(item) ? (
|
||||
<div className="flex justify-center">
|
||||
<div className="w-full max-w-[540px] border border-black/10 bg-white shadow-xl overflow-hidden rounded-xl">
|
||||
<iframe
|
||||
src={getInstaEmbedUrl(item)}
|
||||
className="w-full h-[700px] border-0"
|
||||
scrolling="no"
|
||||
allowTransparency={true}
|
||||
allowFullScreen
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative aspect-[16/10] border border-black/5 bg-black/5 shadow-lg overflow-hidden group">
|
||||
{isVideo(item) ? (
|
||||
<iframe
|
||||
src={item}
|
||||
className="w-full h-full"
|
||||
allowFullScreen
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={item}
|
||||
alt={`Gallery ${idx}`}
|
||||
fill
|
||||
className="object-cover transition-transform duration-1000 group-hover:scale-105"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Minimal CTA Section */}
|
||||
<section className="py-32 px-6 md:px-12 bg-[#f5f5f0]">
|
||||
<div className="max-w-7xl mx-auto text-center">
|
||||
<div className="w-full h-px bg-black/10 mb-24" />
|
||||
<h2 className="editorial-headline text-3xl md:text-5xl lg:text-6xl text-black uppercase leading-tight mb-16 max-w-4xl mx-auto">
|
||||
CESUR, İNSANCIL VE <br /> İZ BIRAKAN PROJELERİ <br /> <span className="text-primary">BİRLİKTE</span> ŞEKİLLENDİRELİM.
|
||||
</h2>
|
||||
|
||||
<Link
|
||||
href="/contact"
|
||||
className="inline-flex items-center justify-center w-32 h-32 md:w-40 md:h-40 rounded-full bg-primary text-white text-[10px] font-bold uppercase tracking-widest hover:scale-110 transition-all duration-500 shadow-xl shadow-primary/20 group"
|
||||
>
|
||||
<span className="group-hover:scale-110 transition-transform">İLETİŞİME GEÇ</span>
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Next Project - Editorial Style */}
|
||||
{nextProject && (
|
||||
<section className="relative border-y border-black/10 group overflow-hidden bg-[#f9f6ef] hover:bg-[#e2d1c1] transition-colors duration-700">
|
||||
<Link href={`/works/${nextProject.slug}`} className="block py-40 px-6 md:px-12">
|
||||
<div className="max-w-7xl mx-auto text-center space-y-12">
|
||||
<span className="text-[10px] tracking-[0.5em] text-black/30 uppercase block">SIRADAKİ ÇALIŞMA</span>
|
||||
<h2 className="editorial-headline text-5xl md:text-8xl lg:text-9xl text-black uppercase transition-all duration-700 group-hover:scale-[0.98]">
|
||||
{nextProject.title}
|
||||
</h2>
|
||||
<div className="flex items-center justify-center gap-6 text-black/40 font-bold text-[12px] tracking-[0.3em] uppercase">
|
||||
<div className="w-12 h-px bg-black/20" />
|
||||
PROJEYİ GÖR
|
||||
<div className="w-12 h-px bg-black/20" />
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<Footer />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
187
components/WorksClient.tsx
Normal file
187
components/WorksClient.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
import Footer from "@/components/Footer";
|
||||
|
||||
interface ProjectCardProps {
|
||||
hero_image: string;
|
||||
category: string;
|
||||
title: string;
|
||||
year: string;
|
||||
client: string;
|
||||
slug: string;
|
||||
narrative_desc: string;
|
||||
index: number;
|
||||
}
|
||||
|
||||
function ProjectCard({ hero_image, category, title, year, client, slug, narrative_desc, index }: ProjectCardProps) {
|
||||
// Alternating background colors like in the reference screenshot
|
||||
const bgColors = ["bg-[#e2d1c1]", "bg-[#f9f6ef]", "bg-[#d8c7b8]", "bg-[#f5f1e8]"];
|
||||
const bgColor = bgColors[index % bgColors.length];
|
||||
|
||||
return (
|
||||
<Link href={`/works/${slug}`} className="group cursor-pointer block h-full">
|
||||
<div className={`p-8 md:p-10 h-full flex flex-col transition-all duration-500 group-hover:translate-y-[-8px] ${bgColor}`}>
|
||||
{/* 1. Image Area */}
|
||||
<div className="relative aspect-[16/10] overflow-hidden mb-12 shadow-sm">
|
||||
<Image
|
||||
src={hero_image || "https://images.unsplash.com/photo-1536440136628-849c177e76a1"}
|
||||
alt={title}
|
||||
fill
|
||||
className="object-cover transition-transform duration-1000 group-hover:scale-105"
|
||||
/>
|
||||
{/* Floating Title on Image like reference */}
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center text-center p-6 bg-black/10">
|
||||
<span className="text-[10px] tracking-[0.2em] uppercase text-white/80 mb-2">
|
||||
{(() => {
|
||||
let current = category;
|
||||
try {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (typeof current === 'string' && (current.trim().startsWith('[') || current.trim().startsWith('"'))) {
|
||||
current = JSON.parse(current);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Array.isArray(current) ? current.join(" / ") : current;
|
||||
} catch (e) {
|
||||
return current;
|
||||
}
|
||||
})()}
|
||||
</span>
|
||||
<h3 className="editorial-headline text-3xl md:text-4xl text-white uppercase leading-tight drop-shadow-md">
|
||||
{title}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 2. Description Area */}
|
||||
<div className="flex-grow mb-12">
|
||||
<p className="text-black/70 text-[13px] leading-[1.8] line-clamp-6 font-medium italic">
|
||||
{narrative_desc || "Dijital dünyada markanızın sesini duyurmak ve özgün bir kimlik kazandırmak için tasarladığımız özel projelerimizden biri."}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 3. Footer / Client Area */}
|
||||
<div className="border-t border-black/10 pt-8 mt-auto">
|
||||
<div className="flex justify-between items-end">
|
||||
<div>
|
||||
<span className="text-[9px] tracking-[0.2em] uppercase text-black/30 block mb-2">Müşteri</span>
|
||||
<h4 className="editorial-headline text-3xl text-black uppercase opacity-80 group-hover:opacity-100 group-hover:text-primary transition-all">
|
||||
{client || "Muğla Dijital"}
|
||||
</h4>
|
||||
</div>
|
||||
<div className="text-[10px] font-black text-black/20 group-hover:text-primary transition-colors border border-black/5 px-2 py-1 rounded">
|
||||
{year}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default function WorksClient({ projects: initialProjects }: { projects: any[] }) {
|
||||
const [activeCategory, setActiveCategory] = useState("Hepsi");
|
||||
|
||||
const categories = ["Hepsi", ...Array.from(new Set(initialProjects.flatMap(p => {
|
||||
let current = p.category;
|
||||
try {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (typeof current === 'string' && (current.trim().startsWith('[') || current.trim().startsWith('"'))) {
|
||||
current = JSON.parse(current);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
return Array.isArray(current) ? current : (typeof current === 'string' && current ? [current] : []);
|
||||
})))];
|
||||
|
||||
const filteredProjects = activeCategory === "Hepsi"
|
||||
? initialProjects
|
||||
: initialProjects.filter(p => {
|
||||
let current = p.category;
|
||||
try {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (typeof current === 'string' && (current.trim().startsWith('[') || current.trim().startsWith('"'))) {
|
||||
current = JSON.parse(current);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
const cats = Array.isArray(current) ? current : (typeof current === 'string' && current ? [current] : []);
|
||||
return cats.includes(activeCategory);
|
||||
});
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-[#f5f5f0] text-black pt-24">
|
||||
<section className="pt-24 pb-24 px-6 md:px-12">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Header Area */}
|
||||
<div className="mb-20">
|
||||
<span className="text-[10px] tracking-[0.2em] uppercase text-black/40 block mb-6">Portfolyo</span>
|
||||
<h1 className="editorial-headline text-4xl md:text-6xl lg:text-[5.5rem] text-black reveal opacity-0 uppercase">
|
||||
Seçilmiş <br /> <span className="text-primary">Projeler.</span>
|
||||
</h1>
|
||||
<p className="text-black/40 text-[14px] max-w-2xl leading-relaxed mt-10 reveal reveal-delayed-1 opacity-0">
|
||||
Markanızın dijital dünyadaki serüvenini profesyonel dokunuşlarla şekillendiriyoruz. Global vizyon, yerel strateji ile başarı hikayeleri yazıyoruz.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Filter Section */}
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-8 mb-16 border-y border-black/10 py-8">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
{categories.map((cat) => (
|
||||
<button
|
||||
key={cat}
|
||||
onClick={() => setActiveCategory(cat)}
|
||||
className={`px-6 py-2 rounded-full text-[10px] font-bold uppercase tracking-widest transition-all border
|
||||
${activeCategory === cat
|
||||
? "bg-black text-white border-black"
|
||||
: "text-black/40 border-black/10 hover:border-black/30 hover:text-black"
|
||||
}`}
|
||||
>
|
||||
{cat}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="hidden md:block">
|
||||
<span className="text-[10px] font-bold text-black/20 uppercase tracking-widest">
|
||||
{filteredProjects.length} Proje
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-12">
|
||||
{filteredProjects.map((project, index) => (
|
||||
<ProjectCard key={index} {...project} index={index} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{filteredProjects.length === 0 && (
|
||||
<div className="py-32 text-center border border-black/10">
|
||||
<p className="text-black/40 text-sm">Bu kategoride henüz bir proje bulunmuyor.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* CTA Section Area */}
|
||||
<div className="mt-32 border-t border-black/10 pt-24 text-center space-y-10">
|
||||
<h2 className="editorial-headline text-3xl md:text-5xl text-black uppercase">Sıradaki Başarı Hikayesi <br /><span className="text-primary">Sizinle</span> Yazılsın.</h2>
|
||||
<Link href="/contact" className="button-primary mx-auto">
|
||||
Projeyi Başlat
|
||||
<ArrowUpRight className="w-4 h-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user