first commit

This commit is contained in:
mstfyldz
2026-05-30 18:23:21 +03:00
commit e927cd6af3
57 changed files with 12945 additions and 0 deletions
+555
View File
@@ -0,0 +1,555 @@
"use client";
import { motion, useScroll, useTransform, AnimatePresence } from "framer-motion";
import { useRef, useState, useEffect } from "react";
import Link from "next/link";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import type { Locale } from "@/i18n-config";
const expo: [number, number, number, number] = [0.16, 1, 0.3, 1];
const IconArrowLeft = () => (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="19" y1="12" x2="5" y2="12" />
<polyline points="12 19 5 12 12 5" />
</svg>
);
const IconArrowRight = () => (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="5" y1="12" x2="19" y2="12" />
<polyline points="12 5 19 12 12 19" />
</svg>
);
const IconCheck = () => (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0A0A0A" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<polyline points="20 6 9 17 4 12" />
</svg>
);
// Tag colors per category
const accentFor = (tag: string): string => {
if (/ai|yz|ml/i.test(tag)) return "#FFE600";
if (/web3|blockchain|chain/i.test(tag)) return "#FF4500";
if (/mobile|mobil/i.test(tag)) return "#0A0A0A";
return "#00CC77";
};
interface Props {
lang: Locale;
dict: any;
project: any;
prev: any | null;
next: any | null;
}
const screenshotsFor = (slug: string): string[] => {
const mapping: Record<string, string[]> = {
"financeai-dashboard": [
"https://images.unsplash.com/photo-1551288049-bebda4e38f71?auto=format&fit=crop&w=1200&q=80",
"https://images.unsplash.com/photo-1460925895917-afdab827c52f?auto=format&fit=crop&w=1200&q=80",
"https://images.unsplash.com/photo-1551836022-d5d88e9218df?auto=format&fit=crop&w=1200&q=80",
],
"chainsupply-network": [
"https://images.unsplash.com/photo-1508873535684-277a3cbcc4e8?auto=format&fit=crop&w=1200&q=80",
"https://images.unsplash.com/photo-1639762681485-074b7f938ba0?auto=format&fit=crop&w=1200&q=80",
"https://images.unsplash.com/photo-1504384308090-c894fdcc538d?auto=format&fit=crop&w=1200&q=80",
],
"meditrack-mobile": [
"https://images.unsplash.com/photo-1576091160399-112ba8d25d1d?auto=format&fit=crop&w=1200&q=80",
"https://images.unsplash.com/photo-1584982751601-97dcc096659c?auto=format&fit=crop&w=1200&q=80",
"https://images.unsplash.com/photo-1530026405186-ed1ea0ac7a63?auto=format&fit=crop&w=1200&q=80",
],
"novamart-platform": [
"https://images.unsplash.com/photo-1460925895917-afdab827c52f?auto=format&fit=crop&w=1200&q=80",
"https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?auto=format&fit=crop&w=1200&q=80",
"https://images.unsplash.com/photo-1523474253046-8cd2748b5fd2?auto=format&fit=crop&w=1200&q=80",
],
};
return mapping[slug] || [];
};
export default function WorkDetailClient({ lang, dict, project, prev, next }: Props) {
const heroRef = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({ target: heroRef, offset: ["start start", "end start"] });
const heroY = useTransform(scrollYProgress, [0, 1], ["0%", "30%"]);
const [activeImage, setActiveImage] = useState<string | null>(null);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") setActiveImage(null);
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, []);
const accent = accentFor(project.tag);
let screenshots = screenshotsFor(project.slug);
if (project.gallery && project.gallery.length > 0) {
screenshots = project.gallery;
}
return (
<div className="bg-[#F4F0E8] text-[#0A0A0A] font-body min-h-screen">
<Header lang={lang} dict={dict.nav} />
{/* ── HERO ── */}
<section
ref={heroRef}
className="relative min-h-[70vh] flex flex-col justify-end overflow-hidden border-b border-[#C8C2B8]"
>
{/* Dot grid */}
<motion.div
className="absolute inset-0"
style={{ y: heroY }}
>
{project.image ? (
<>
<div
className="absolute inset-0 bg-cover bg-center"
style={{ backgroundImage: `url(${project.image})` }}
/>
<div className="absolute inset-0 bg-[#F4F0E8]/80" />
</>
) : (
<div className="absolute inset-0 dot-grid opacity-50" />
)}
</motion.div>
{/* Accent left vertical bar */}
<div className="absolute top-0 left-0 w-1 h-full" style={{ backgroundColor: accent }} />
{/* Big number watermark */}
<motion.div
className="absolute right-0 bottom-0 font-display font-black select-none pointer-events-none text-[#E8E3DC] leading-none"
style={{ fontSize: "clamp(8rem, 25vw, 20rem)" }}
initial={{ opacity: 0, x: 40 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 1, delay: 0.3 }}
>
{project.num}
</motion.div>
<div className="relative z-10 max-w-7xl mx-auto px-6 w-full pb-16 pt-36">
{/* Breadcrumb */}
<motion.div
className="flex items-center gap-3 mb-10"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.1 }}
>
<Link
href={`/${lang}/work`}
className="flex items-center gap-2 font-mono text-[10px] tracking-[0.25em] uppercase text-[#A0998E] hover:text-[#0A0A0A] transition-colors"
>
<IconArrowLeft /> {dict.nav.work}
</Link>
<span className="text-[#C8C2B8]">/</span>
<span className="font-mono text-[10px] tracking-[0.25em] uppercase text-[#0A0A0A]">
{project.num}
</span>
</motion.div>
{/* Tag badge */}
<motion.div
className="mb-5"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2, duration: 0.5 }}
>
<span
className="inline-block font-mono text-[10px] tracking-[0.25em] uppercase px-3 py-1.5 border border-[#C8C2B8]"
style={{ borderLeftColor: accent, borderLeftWidth: 3 }}
>
{project.tag}
</span>
</motion.div>
{/* Title */}
<div className="overflow-hidden mb-8">
<motion.h1
className="font-display font-black uppercase leading-[0.85] tracking-tight text-[#0A0A0A]"
style={{ fontSize: "clamp(3rem, 8vw, 7rem)" }}
initial={{ y: "110%" }}
animate={{ y: 0 }}
transition={{ duration: 0.85, ease: expo, delay: 0.25 }}
>
{project.title}
</motion.h1>
</div>
{/* Meta row */}
<motion.div
className="grid grid-cols-2 md:grid-cols-4 gap-px bg-[#C8C2B8] border-t border-[#C8C2B8]"
initial={{ opacity: 0, y: 15 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.5, duration: 0.6 }}
>
{[
{ label: lang === "tr" ? "Yıl" : "Year", value: project.year },
{ label: lang === "tr" ? "Süre" : "Duration", value: project.duration },
{ label: lang === "tr" ? "Sektör" : "Sector", value: project.tag },
{ label: lang === "tr" ? "Müşteri" : "Client", value: project.client },
].map((m, i) => (
<div key={i} className="bg-[#F4F0E8] px-5 py-4">
<div className="font-mono text-[9px] tracking-[0.3em] uppercase text-[#A0998E] mb-1">{m.label}</div>
<div className="font-display font-black text-[13px] uppercase text-[#0A0A0A] truncate">{m.value}</div>
</div>
))}
</motion.div>
</div>
</section>
{/* ── CONTENT ── */}
<main className="max-w-7xl mx-auto px-6 py-20">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-16">
{/* Left column: main content */}
<div className="lg:col-span-8 space-y-20">
{/* Overview */}
<motion.section
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.7 }}
>
<div className="flex items-center gap-3 mb-6">
<div className="w-4 h-4 flex-shrink-0" style={{ backgroundColor: accent }} />
<span className="font-mono text-[10px] tracking-[0.3em] uppercase text-[#A0998E]">
{lang === "tr" ? "Genel Bakış" : "Overview"}
</span>
<div className="flex-1 h-px bg-[#C8C2B8]" />
</div>
<p className="text-[#4A4440] text-lg leading-relaxed border-l-4 pl-6" style={{ borderColor: accent }}>
{project.desc}
</p>
</motion.section>
{/* Challenge */}
<motion.section
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.7 }}
>
<div className="flex items-center gap-3 mb-6">
<div className="w-4 h-4 border-2 border-[#0A0A0A] flex-shrink-0" />
<span className="font-mono text-[10px] tracking-[0.3em] uppercase text-[#A0998E]">
{lang === "tr" ? "Problem" : "The Challenge"}
</span>
<div className="flex-1 h-px bg-[#C8C2B8]" />
</div>
<h2 className="font-display font-black text-2xl uppercase text-[#0A0A0A] mb-4">
{lang === "tr" ? "Ne Sorununu Çözdük?" : "What Problem Did We Solve?"}
</h2>
<p className="text-[#6A6460] leading-relaxed">
{project.challenge}
</p>
</motion.section>
{/* Solution */}
<motion.section
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.7 }}
>
<div className="flex items-center gap-3 mb-6">
<div className="w-4 h-4 border-2 flex-shrink-0" style={{ borderColor: accent }} />
<span className="font-mono text-[10px] tracking-[0.3em] uppercase text-[#A0998E]">
{lang === "tr" ? "Çözüm" : "Our Solution"}
</span>
<div className="flex-1 h-px bg-[#C8C2B8]" />
</div>
<h2 className="font-display font-black text-2xl uppercase text-[#0A0A0A] mb-4">
{lang === "tr" ? "Nasıl İnşa Ettik?" : "How Did We Build It?"}
</h2>
<p className="text-[#6A6460] leading-relaxed">
{project.solution}
</p>
</motion.section>
{/* Results */}
<motion.section
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.7 }}
>
<div className="flex items-center gap-3 mb-8">
<div className="w-4 h-4 flex-shrink-0" style={{ backgroundColor: accent }} />
<span className="font-mono text-[10px] tracking-[0.3em] uppercase text-[#A0998E]">
{lang === "tr" ? "Sonuçlar" : "Results"}
</span>
<div className="flex-1 h-px bg-[#C8C2B8]" />
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-px bg-[#C8C2B8]">
{project.results.map((r: string, idx: number) => (
<motion.div
key={idx}
className="bg-[#F4F0E8] p-6 flex items-start gap-4 group"
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ delay: idx * 0.1 }}
whileHover={{ backgroundColor: "#EDE8E0" }}
>
<div
className="w-6 h-6 flex-shrink-0 flex items-center justify-center mt-0.5"
style={{ backgroundColor: accent }}
>
<IconCheck />
</div>
<p className="font-display font-bold text-[14px] uppercase text-[#0A0A0A] leading-snug">
{r}
</p>
</motion.div>
))}
</div>
</motion.section>
</div>
{/* Right column: sidebar */}
<aside className="lg:col-span-4 space-y-0">
<div className="sticky top-24">
{/* Perf spec */}
<motion.div
className="border border-[#C8C2B8] mb-px"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.4 }}
>
<div className="border-b border-[#C8C2B8] px-6 py-3 flex items-center gap-2">
<div className="w-2 h-2" style={{ backgroundColor: accent }} />
<span className="font-mono text-[9px] tracking-[0.3em] uppercase text-[#A0998E]">
{dict.work.specsLabel}
</span>
</div>
<div className="px-6 py-5">
<p className="font-display font-black text-sm uppercase text-[#0A0A0A] leading-snug">
{project.spec}
</p>
</div>
</motion.div>
{/* Tech stack */}
<motion.div
className="border border-[#C8C2B8] mb-px"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 }}
>
<div className="border-b border-[#C8C2B8] px-6 py-3">
<span className="font-mono text-[9px] tracking-[0.3em] uppercase text-[#A0998E]">
{lang === "tr" ? "Teknoloji Yığını" : "Tech Stack"}
</span>
</div>
<div className="px-6 py-5 flex flex-wrap gap-2">
{project.tech.map((t: string) => (
<span
key={t}
className="font-mono text-[10px] tracking-wider uppercase px-2.5 py-1.5 border border-[#C8C2B8] text-[#6A6460] hover:border-[#0A0A0A] hover:text-[#0A0A0A] transition-colors cursor-default"
>
{t}
</span>
))}
</div>
</motion.div>
{/* Website */}
{project.website && (
<motion.div
className="border border-[#C8C2B8] mb-px"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.55 }}
>
<div className="border-b border-[#C8C2B8] px-6 py-3 flex items-center gap-2">
<div className="w-2 h-2" style={{ backgroundColor: accent }} />
<span className="font-mono text-[9px] tracking-[0.3em] uppercase text-[#A0998E]">
{lang === "tr" ? "Canlı Proje" : "Live Project"}
</span>
</div>
<div className="px-6 py-5">
<a
href={project.website.startsWith('http') ? project.website : `https://${project.website}`}
target="_blank"
rel="noopener noreferrer"
className="inline-block font-mono text-[10px] tracking-wider uppercase px-4 py-2 border border-[#0A0A0A] hover:bg-[#0A0A0A] hover:text-[#FFE600] transition-colors cursor-pointer w-full text-center"
>
{lang === "tr" ? "Siteyi Ziyaret Et" : "Visit Website"}
</a>
</div>
</motion.div>
)}
{/* CTA */}
<motion.div
className="border-2 border-[#0A0A0A] p-6"
style={{ backgroundColor: accent }}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.6 }}
>
<p className="font-display font-black text-sm uppercase text-[#0A0A0A] mb-4 leading-snug">
{lang === "tr" ? "Benzer bir proje mi istiyorsunuz?" : "Want a similar project?"}
</p>
<Link
href={`/${lang}/contact`}
className="btn-brutal inline-flex items-center gap-2 px-5 py-3 font-display font-black text-[11px] tracking-widest uppercase cursor-pointer bg-[#0A0A0A] text-[#F4F0E8] border-[#0A0A0A]"
>
{dict.nav.quote} <IconArrowRight />
</Link>
</motion.div>
</div>
</aside>
</div>
</main>
{/* ── PROJECT SCREENSHOTS GALLERY ── */}
{screenshots.length > 0 && (
<section className="py-24 border-t border-[#C8C2B8] bg-[#EDE8E0]">
<div className="max-w-7xl mx-auto px-6">
<div className="flex items-center gap-3 mb-12">
<div className="w-3 h-3" style={{ backgroundColor: accent }} />
<span className="font-mono text-[10px] tracking-[0.3em] uppercase text-[#A0998E]">
{dict.work.galleryTitle || "EKRAN GÖRÜNTÜLERİ"}
</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{screenshots.map((src, idx) => (
<motion.div
key={idx}
className="group bg-[#F4F0E8] border border-[#C8C2B8] hover:border-[#0A0A0A] p-3 relative overflow-hidden transition-all duration-300 cursor-zoom-in"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: idx * 0.1, duration: 0.6 }}
onClick={() => setActiveImage(src)}
whileHover={{ y: -6, boxShadow: `4px 4px 0px 0px ${accent}` }}
>
<div className="aspect-[16/10] w-full overflow-hidden border border-[#C8C2B8] group-hover:border-[#0A0A0A] bg-[#EDE8E0] transition-colors relative">
<img
src={src}
alt={`${project.title} screenshot ${idx + 1}`}
className="w-full h-full object-cover grayscale group-hover:grayscale-0 contrast-[1.1] brightness-[0.98] group-hover:scale-105 transition-all duration-700"
/>
<div className="absolute inset-0 bg-[#0A0A0A]/5 opacity-100 group-hover:opacity-0 transition-opacity duration-300" />
</div>
<div className="mt-3 flex justify-between items-center font-mono text-[9px] text-[#A0998E] group-hover:text-[#0A0A0A] transition-colors">
<span>SCREEN // 0{idx + 1}</span>
<span className="uppercase tracking-widest">[ ZOOM ]</span>
</div>
</motion.div>
))}
</div>
</div>
</section>
)}
{/* Lightbox Modal */}
<AnimatePresence>
{activeImage && (
<motion.div
className="fixed inset-0 z-[100] flex items-center justify-center bg-black/85 backdrop-blur-md px-6 cursor-zoom-out"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setActiveImage(null)}
>
<button
onClick={() => setActiveImage(null)}
className="absolute top-6 right-6 w-12 h-12 border border-white/20 hover:border-white/80 hover:bg-white/10 text-white flex items-center justify-center font-mono text-[10px] uppercase tracking-widest cursor-pointer transition-colors duration-200"
>
[ CLOSE ]
</button>
<motion.div
className="max-w-5xl max-h-[85vh] border-2 border-white/10 bg-[#0A0A0A] p-2 relative"
initial={{ scale: 0.9, y: 20 }}
animate={{ scale: 1, y: 0 }}
exit={{ scale: 0.9, y: 20 }}
transition={{ duration: 0.4, ease: expo }}
onClick={(e) => e.stopPropagation()}
>
<img
src={activeImage}
alt="Enlarged project screenshot"
className="max-w-full max-h-[80vh] object-contain block"
/>
</motion.div>
</motion.div>
)}
</AnimatePresence>
{/* ── PREV / NEXT NAVIGATION ── */}
<div className="border-t border-[#C8C2B8]">
<div className="max-w-7xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-px bg-[#C8C2B8]">
{/* Prev */}
<div>
{prev ? (
<Link
href={`/${lang}/work/${prev.slug}`}
className="group flex flex-col p-10 bg-[#F4F0E8] hover:bg-[#EDE8E0] transition-colors h-full"
>
<div className="flex items-center gap-2 font-mono text-[9px] tracking-[0.3em] uppercase text-[#A0998E] group-hover:text-[#0A0A0A] transition-colors mb-4">
<IconArrowLeft />
{lang === "tr" ? "Önceki Proje" : "Previous Project"}
</div>
<div className="font-display font-black text-xl uppercase text-[#0A0A0A] mt-auto">
{prev.title}
</div>
<div className="font-mono text-[10px] tracking-wider uppercase text-[#A0998E] mt-1">
{prev.tag}
</div>
</Link>
) : (
<div className="p-10 bg-[#EDE8E0]" />
)}
</div>
{/* Next */}
<div>
{next ? (
<Link
href={`/${lang}/work/${next.slug}`}
className="group flex flex-col items-end p-10 bg-[#F4F0E8] hover:bg-[#EDE8E0] transition-colors h-full text-right"
>
<div className="flex items-center gap-2 font-mono text-[9px] tracking-[0.3em] uppercase text-[#A0998E] group-hover:text-[#0A0A0A] transition-colors mb-4">
{lang === "tr" ? "Sonraki Proje" : "Next Project"}
<IconArrowRight />
</div>
<div className="font-display font-black text-xl uppercase text-[#0A0A0A] mt-auto">
{next.title}
</div>
<div className="font-mono text-[10px] tracking-wider uppercase text-[#A0998E] mt-1">
{next.tag}
</div>
</Link>
) : (
<Link
href={`/${lang}/work`}
className="group flex flex-col items-end p-10 bg-[#F4F0E8] hover:bg-[#EDE8E0] transition-colors h-full text-right"
>
<div className="flex items-center gap-2 font-mono text-[9px] tracking-[0.3em] uppercase text-[#A0998E] group-hover:text-[#0A0A0A] transition-colors mb-4">
{lang === "tr" ? "Tüm Projeler" : "All Projects"}
<IconArrowRight />
</div>
<div className="font-display font-black text-xl uppercase text-[#C8C2B8] mt-auto">
{lang === "tr" ? "Portfolyo" : "Portfolio"}
</div>
</Link>
)}
</div>
</div>
</div>
<Footer lang={lang} dict={dict} />
</div>
);
}