1743 lines
82 KiB
TypeScript
1743 lines
82 KiB
TypeScript
"use client";
|
||
|
||
import { motion, AnimatePresence } from "framer-motion";
|
||
import { useState, useEffect } from "react";
|
||
import Link from "next/link";
|
||
import Header from "@/components/Header";
|
||
import type { Locale } from "@/i18n-config";
|
||
import { blogPosts, BlogPost } from "@/data/blog";
|
||
import { saveProject, deleteProject, saveBlogPost, deleteBlogPost, adminLogoutAction, savePartner, deletePartner } from "@/app/actions";
|
||
|
||
const expo = [0.16, 1, 0.3, 1] as [number, number, number, number];
|
||
|
||
interface ToastMsg {
|
||
text: string;
|
||
type: "success" | "info" | "alert";
|
||
}
|
||
|
||
interface ProjectData {
|
||
id?: number;
|
||
num: string;
|
||
slug: string;
|
||
title: string;
|
||
tag: string;
|
||
desc: string;
|
||
spec: string;
|
||
year: string;
|
||
client: string;
|
||
duration: string;
|
||
tech: string[];
|
||
challenge: string;
|
||
solution: string;
|
||
results: string[];
|
||
image: string;
|
||
gallery?: string[];
|
||
website?: string;
|
||
featured?: boolean;
|
||
}
|
||
|
||
interface PartnerData {
|
||
id?: number;
|
||
name: string;
|
||
tag: string;
|
||
mono: string;
|
||
year: string;
|
||
desc: string;
|
||
}
|
||
|
||
export default function AdminClient({
|
||
lang,
|
||
dict,
|
||
initialProjects = [],
|
||
initialBlogPosts = [],
|
||
initialPartners = [],
|
||
}: {
|
||
lang: Locale;
|
||
dict: any;
|
||
initialProjects?: ProjectData[];
|
||
initialBlogPosts?: BlogPost[];
|
||
initialPartners?: PartnerData[];
|
||
}) {
|
||
const [activeTab, setActiveTab] = useState<"projeler" | "blog" | "partnerler" | "ayarlar">("projeler");
|
||
|
||
// Toast state
|
||
const [toast, setToast] = useState<ToastMsg | null>(null);
|
||
const showToast = (text: string, type: "success" | "info" | "alert" = "success") => {
|
||
setToast({ text, type });
|
||
};
|
||
useEffect(() => {
|
||
if (toast) {
|
||
const t = setTimeout(() => setToast(null), 3000);
|
||
return () => clearTimeout(t);
|
||
}
|
||
}, [toast]);
|
||
|
||
// Project CRUD State
|
||
const [projects, setProjects] = useState<ProjectData[]>(initialProjects);
|
||
const [searchQuery, setSearchQuery] = useState("");
|
||
const [editingProject, setEditingProject] = useState<ProjectData | null>(null);
|
||
const [isAddingNew, setIsAddingNew] = useState(false);
|
||
|
||
// Edit / New Project Form State
|
||
const [formTitle, setFormTitle] = useState("");
|
||
const [formTag, setFormTag] = useState("");
|
||
const [formDesc, setFormDesc] = useState("");
|
||
const [formSpec, setFormSpec] = useState("");
|
||
const [formYear, setFormYear] = useState("");
|
||
const [formClient, setFormClient] = useState("");
|
||
const [formDuration, setFormDuration] = useState("");
|
||
const [formTechString, setFormTechString] = useState("");
|
||
const [formChallenge, setFormChallenge] = useState("");
|
||
const [formSolution, setFormSolution] = useState("");
|
||
const [formResults, setFormResults] = useState<string[]>([]);
|
||
const [newResultItem, setNewResultItem] = useState("");
|
||
const [formImage, setFormImage] = useState("");
|
||
const [formGallery, setFormGallery] = useState<string[]>([]);
|
||
const [newGalleryItem, setNewGalleryItem] = useState("");
|
||
const [formWebsite, setFormWebsite] = useState("");
|
||
const [formFeatured, setFormFeatured] = useState(false);
|
||
|
||
const [uploadingImage, setUploadingImage] = useState(false);
|
||
const [uploadingGallery, setUploadingGallery] = useState(false);
|
||
|
||
const handleFileUpload = async (file: File) => {
|
||
const fd = new FormData();
|
||
fd.append('file', file);
|
||
const res = await fetch('/api/upload', { method: 'POST', body: fd });
|
||
const data = await res.json();
|
||
if (!res.ok) throw new Error(data.error || 'Yükleme başarısız');
|
||
return data.url;
|
||
};
|
||
|
||
// Blog CRUD State
|
||
const [posts, setPosts] = useState<BlogPost[]>(initialBlogPosts);
|
||
const [blogSearchQuery, setBlogSearchQuery] = useState("");
|
||
const [editingPost, setEditingPost] = useState<BlogPost | null>(null);
|
||
const [isAddingNewPost, setIsAddingNewPost] = useState(false);
|
||
|
||
// Partner CRUD State
|
||
const [partners, setPartners] = useState<PartnerData[]>(initialPartners);
|
||
const [partnerSearchQuery, setPartnerSearchQuery] = useState("");
|
||
const [editingPartner, setEditingPartner] = useState<PartnerData | null>(null);
|
||
const [isAddingNewPartner, setIsAddingNewPartner] = useState(false);
|
||
|
||
// Edit / New Partner Form State
|
||
const [formPartnerName, setFormPartnerName] = useState("");
|
||
const [formPartnerTag, setFormPartnerTag] = useState("");
|
||
const [formPartnerMono, setFormPartnerMono] = useState("");
|
||
const [formPartnerYear, setFormPartnerYear] = useState("");
|
||
const [formPartnerDesc, setFormPartnerDesc] = useState("");
|
||
|
||
// Blog Modal Form Sub-tabs ("genel" | "tr" | "en")
|
||
const [blogFormSubTab, setBlogFormSubTab] = useState<"genel" | "tr" | "en">("genel");
|
||
const [formBlogSlug, setFormBlogSlug] = useState("");
|
||
const [formBlogDate, setFormBlogDate] = useState("");
|
||
const [formBlogAuthor, setFormBlogAuthor] = useState("");
|
||
const [formBlogAuthorRole, setFormBlogAuthorRole] = useState("");
|
||
const [formBlogImage, setFormBlogImage] = useState("");
|
||
|
||
const [formBlogTrTitle, setFormBlogTrTitle] = useState("");
|
||
const [formBlogTrExcerpt, setFormBlogTrExcerpt] = useState("");
|
||
const [formBlogTrReadingTime, setFormBlogTrReadingTime] = useState("");
|
||
const [formBlogTrCategory, setFormBlogTrCategory] = useState("");
|
||
const [formBlogTrTags, setFormBlogTrTags] = useState("");
|
||
const [formBlogTrContent, setFormBlogTrContent] = useState("");
|
||
|
||
const [formBlogEnTitle, setFormBlogEnTitle] = useState("");
|
||
const [formBlogEnExcerpt, setFormBlogEnExcerpt] = useState("");
|
||
const [formBlogEnReadingTime, setFormBlogEnReadingTime] = useState("");
|
||
const [formBlogEnCategory, setFormBlogEnCategory] = useState("");
|
||
const [formBlogEnTags, setFormBlogEnTags] = useState("");
|
||
const [formBlogEnContent, setFormBlogEnContent] = useState("");
|
||
|
||
// Site Settings Form State
|
||
const [brandName, setBrandName] = useState(dict.nav.brandName || "Ayris");
|
||
const [brandSub, setBrandSub] = useState(dict.nav.brandSub || "Tech");
|
||
const [heroBadge, setHeroBadge] = useState(dict.hero.badge || "Ayris Tech — Mühendislik Zirvesi");
|
||
const [heroTitle1, setHeroTitle1] = useState(dict.hero.titleLine1 || "Geleceği");
|
||
const [heroTitle2, setHeroTitle2] = useState(dict.hero.titleLine2 || "İnşa Ediyoruz");
|
||
const [heroDesc, setHeroDesc] = useState(dict.hero.desc || "");
|
||
const [telemetryEmail, setTelemetryEmail] = useState("info@ayristech.com");
|
||
const [telemetryHub, setTelemetryHub] = useState(dict.contact.infoBaseText || "İstanbul, Türkiye");
|
||
|
||
// Open modal for project editing
|
||
const handleStartEdit = (p: ProjectData) => {
|
||
setEditingProject(p);
|
||
setIsAddingNew(false);
|
||
setFormTitle(p.title);
|
||
setFormTag(p.tag);
|
||
setFormDesc(p.desc);
|
||
setFormSpec(p.spec);
|
||
setFormYear(p.year);
|
||
setFormClient(p.client);
|
||
setFormDuration(p.duration);
|
||
setFormTechString(p.tech.join(", "));
|
||
setFormChallenge(p.challenge);
|
||
setFormSolution(p.solution);
|
||
setFormResults([...p.results]);
|
||
setNewResultItem("");
|
||
setFormImage(p.image || "");
|
||
setFormGallery(p.gallery ? [...p.gallery] : []);
|
||
setNewGalleryItem("");
|
||
setFormWebsite(p.website || "");
|
||
setFormFeatured(p.featured || false);
|
||
};
|
||
|
||
// Open modal for project adding
|
||
const handleStartAdd = () => {
|
||
setEditingProject(null);
|
||
setIsAddingNew(true);
|
||
setFormTitle("");
|
||
setFormTag("");
|
||
setFormDesc("");
|
||
setFormSpec("");
|
||
setFormYear("2026");
|
||
setFormClient("");
|
||
setFormDuration("");
|
||
setFormTechString("");
|
||
setFormChallenge("");
|
||
setFormSolution("");
|
||
setFormResults([]);
|
||
setNewResultItem("");
|
||
setFormImage("");
|
||
setFormGallery([]);
|
||
setNewGalleryItem("");
|
||
setFormWebsite("");
|
||
setFormFeatured(false);
|
||
};
|
||
|
||
// Save project
|
||
const handleSaveProject = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (!formTitle.trim()) {
|
||
showToast("Proje başlığı boş bırakılamaz!", "alert");
|
||
return;
|
||
}
|
||
|
||
const techArray = formTechString.split(",").map(t => t.trim()).filter(Boolean);
|
||
const slugValue = formTitle.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
||
|
||
const nextNum = isAddingNew
|
||
? (projects.length > 0
|
||
? String(Math.max(...projects.map(p => parseInt(p.num, 10))) + 1).padStart(2, '0')
|
||
: "01")
|
||
: editingProject!.num;
|
||
|
||
const projectDataToSave = {
|
||
id: editingProject?.id,
|
||
num: nextNum,
|
||
slug: slugValue,
|
||
title: formTitle,
|
||
tag: formTag || "Web · Yapay Zekâ",
|
||
desc: formDesc,
|
||
spec: formSpec,
|
||
year: formYear,
|
||
client: formClient,
|
||
duration: formDuration,
|
||
tech: techArray,
|
||
challenge: formChallenge,
|
||
solution: formSolution,
|
||
results: formResults.length > 0 ? formResults : ["Proje başarıyla tamamlandı"],
|
||
image: formImage,
|
||
gallery: formGallery,
|
||
website: formWebsite,
|
||
featured: formFeatured,
|
||
};
|
||
|
||
const res = await saveProject(projectDataToSave);
|
||
if (res.success && res.project) {
|
||
if (isAddingNew) {
|
||
setProjects([...projects, { ...projectDataToSave, id: res.project.id }]);
|
||
showToast(`[ YENİ PROJE YARATILDI // ${formTitle.toUpperCase()} ]`, "success");
|
||
setIsAddingNew(false);
|
||
} else {
|
||
const updatedList = projects.map(p => {
|
||
if (p.id === editingProject?.id) {
|
||
return {
|
||
...p,
|
||
...projectDataToSave,
|
||
};
|
||
}
|
||
return p;
|
||
});
|
||
setProjects(updatedList);
|
||
showToast(`[ PROJE BAŞARIYLA DÜZENLENDİ // ${formTitle.toUpperCase()} ]`, "success");
|
||
setEditingProject(null);
|
||
}
|
||
} else {
|
||
showToast(`Hata: ${res.error}`, "alert");
|
||
}
|
||
};
|
||
|
||
// Delete project
|
||
const handleDeleteProject = async (id: number, title: string) => {
|
||
if (confirm(`"${title}" projesini silmek istediğinize emin misiniz?`)) {
|
||
const res = await deleteProject(id);
|
||
if (res.success) {
|
||
setProjects(projects.filter(p => p.id !== id));
|
||
showToast(`[ PROJE SİLİNDİ // ${title.toUpperCase()} ]`, "alert");
|
||
} else {
|
||
showToast(`Hata: ${res.error}`, "alert");
|
||
}
|
||
}
|
||
};
|
||
|
||
// Add list result item
|
||
const handleAddResultItem = () => {
|
||
if (newResultItem.trim()) {
|
||
setFormResults([...formResults, newResultItem.trim()]);
|
||
setNewResultItem("");
|
||
}
|
||
};
|
||
|
||
// Remove list result item
|
||
const handleRemoveResultItem = (idx: number) => {
|
||
setFormResults(formResults.filter((_, i) => i !== idx));
|
||
};
|
||
|
||
// Add gallery item
|
||
const handleAddGalleryItem = () => {
|
||
if (newGalleryItem.trim()) {
|
||
setFormGallery([...formGallery, newGalleryItem.trim()]);
|
||
setNewGalleryItem("");
|
||
}
|
||
};
|
||
|
||
// Remove gallery item
|
||
const handleRemoveGalleryItem = (idx: number) => {
|
||
setFormGallery(formGallery.filter((_, i) => i !== idx));
|
||
};
|
||
|
||
// ── BLOG HANDLERS ──
|
||
|
||
// Open modal for blog editing
|
||
const handleStartEditPost = (post: BlogPost) => {
|
||
setEditingPost(post);
|
||
setIsAddingNewPost(false);
|
||
setBlogFormSubTab("genel");
|
||
setFormBlogSlug(post.slug);
|
||
setFormBlogDate(post.date);
|
||
setFormBlogAuthor(post.author);
|
||
setFormBlogAuthorRole(post.authorRole);
|
||
setFormBlogImage(post.image);
|
||
|
||
setFormBlogTrTitle(post.tr.title);
|
||
setFormBlogTrExcerpt(post.tr.excerpt);
|
||
setFormBlogTrReadingTime(post.tr.readingTime);
|
||
setFormBlogTrCategory(post.tr.category);
|
||
setFormBlogTrTags(post.tr.tags.join(", "));
|
||
setFormBlogTrContent(post.tr.content);
|
||
|
||
setFormBlogEnTitle(post.en.title);
|
||
setFormBlogEnExcerpt(post.en.excerpt);
|
||
setFormBlogEnReadingTime(post.en.readingTime);
|
||
setFormBlogEnCategory(post.en.category);
|
||
setFormBlogEnTags(post.en.tags.join(", "));
|
||
setFormBlogEnContent(post.en.content);
|
||
};
|
||
|
||
// Open modal for blog adding
|
||
const handleStartAddPost = () => {
|
||
setEditingPost(null);
|
||
setIsAddingNewPost(true);
|
||
setBlogFormSubTab("genel");
|
||
setFormBlogSlug("");
|
||
setFormBlogDate(new Date().toISOString().split('T')[0]);
|
||
setFormBlogAuthor("Mustafa Yıldız");
|
||
setFormBlogAuthorRole("Founder & Architect");
|
||
setFormBlogImage("");
|
||
|
||
setFormBlogTrTitle("");
|
||
setFormBlogTrExcerpt("");
|
||
setFormBlogTrReadingTime("5 dk okuma");
|
||
setFormBlogTrCategory("Teknoloji");
|
||
setFormBlogTrTags("AI, Web");
|
||
setFormBlogTrContent("");
|
||
|
||
setFormBlogEnTitle("");
|
||
setFormBlogEnExcerpt("");
|
||
setFormBlogEnReadingTime("5 min read");
|
||
setFormBlogEnCategory("Technology");
|
||
setFormBlogEnTags("AI, Web");
|
||
setFormBlogEnContent("");
|
||
};
|
||
|
||
// Save Blog Post
|
||
const handleSavePost = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (!formBlogTrTitle.trim()) {
|
||
showToast("Türkçe blog başlığı zorunludur!", "alert");
|
||
return;
|
||
}
|
||
|
||
const calculatedSlug = formBlogSlug.trim() ||
|
||
formBlogTrTitle.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
||
|
||
const blogPostDataToSave = {
|
||
id: editingPost?.id,
|
||
slug: calculatedSlug,
|
||
date: formBlogDate || new Date().toISOString().split('T')[0],
|
||
author: formBlogAuthor || "Ayris Council",
|
||
authorRole: formBlogAuthorRole || "Chief Engineer",
|
||
image: formBlogImage || "https://images.unsplash.com/photo-1504384308090-c894fdcc538d?auto=format&fit=crop&w=1200&q=80",
|
||
trTitle: formBlogTrTitle,
|
||
trExcerpt: formBlogTrExcerpt,
|
||
trReadingTime: formBlogTrReadingTime || "5 dk okuma",
|
||
trCategory: formBlogTrCategory || "Teknoloji",
|
||
trTags: formBlogTrTags.split(",").map(t => t.trim()).filter(Boolean),
|
||
trContent: formBlogTrContent || "## Yeni Teknik Blok Başlığı\nİçerik buraya gelecektir.",
|
||
enTitle: formBlogEnTitle || formBlogTrTitle,
|
||
enExcerpt: formBlogEnExcerpt || formBlogTrExcerpt,
|
||
enReadingTime: formBlogEnReadingTime || "5 min read",
|
||
enCategory: formBlogEnCategory || "Technology",
|
||
enTags: formBlogEnTags.split(",").map(t => t.trim()).filter(Boolean),
|
||
enContent: formBlogEnContent || "## New Technical Article\nContent goes here.",
|
||
};
|
||
|
||
const res = await saveBlogPost(blogPostDataToSave);
|
||
if (res.success && res.post) {
|
||
const mappedPost: BlogPost = {
|
||
id: res.post.id,
|
||
slug: res.post.slug,
|
||
date: res.post.date,
|
||
author: res.post.author,
|
||
authorRole: res.post.authorRole,
|
||
image: res.post.image,
|
||
tr: {
|
||
title: res.post.trTitle,
|
||
excerpt: res.post.trExcerpt,
|
||
readingTime: res.post.trReadingTime,
|
||
category: res.post.trCategory,
|
||
tags: res.post.trTags,
|
||
content: res.post.trContent,
|
||
},
|
||
en: {
|
||
title: res.post.enTitle,
|
||
excerpt: res.post.enExcerpt,
|
||
readingTime: res.post.enReadingTime,
|
||
category: res.post.enCategory,
|
||
tags: res.post.enTags,
|
||
content: res.post.enContent,
|
||
}
|
||
};
|
||
|
||
if (isAddingNewPost) {
|
||
setPosts([...posts, mappedPost]);
|
||
showToast(`[ BLOG YAZISI EKLEDİ // ${mappedPost.tr.title.toUpperCase()} ]`, "success");
|
||
setIsAddingNewPost(false);
|
||
} else {
|
||
setPosts(posts.map(p => p.id === editingPost?.id ? mappedPost : p));
|
||
showToast(`[ BLOG YAZISI GÜNCELLEDİ // ${mappedPost.tr.title.toUpperCase()} ]`, "success");
|
||
setEditingPost(null);
|
||
}
|
||
} else {
|
||
showToast(`Hata: ${res.error}`, "alert");
|
||
}
|
||
};
|
||
|
||
// Delete Blog Post
|
||
const handleDeletePost = async (id: number, title: string) => {
|
||
if (confirm(`"${title}" blog yazısını silmek istediğinize emin misiniz?`)) {
|
||
const res = await deleteBlogPost(id);
|
||
if (res.success) {
|
||
setPosts(posts.filter(p => p.id !== id));
|
||
showToast(`[ BLOG YAZISI SİLİNDİ // ${title.toUpperCase()} ]`, "alert");
|
||
} else {
|
||
showToast(`Hata: ${res.error}`, "alert");
|
||
}
|
||
}
|
||
};
|
||
|
||
// ── PARTNER CRUD ACTIONS ──
|
||
|
||
const handleStartEditPartner = (p: PartnerData) => {
|
||
setEditingPartner(p);
|
||
setIsAddingNewPartner(false);
|
||
setFormPartnerName(p.name);
|
||
setFormPartnerTag(p.tag);
|
||
setFormPartnerMono(p.mono);
|
||
setFormPartnerYear(p.year);
|
||
setFormPartnerDesc(p.desc);
|
||
};
|
||
|
||
const handleStartAddPartner = () => {
|
||
setEditingPartner(null);
|
||
setIsAddingNewPartner(true);
|
||
setFormPartnerName("");
|
||
setFormPartnerTag("");
|
||
setFormPartnerMono("");
|
||
setFormPartnerYear("2026");
|
||
setFormPartnerDesc("");
|
||
};
|
||
|
||
const handleSavePartner = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (!formPartnerName.trim() || !formPartnerMono.trim()) {
|
||
showToast("Partner ismi ve monogramı zorunludur!", "alert");
|
||
return;
|
||
}
|
||
|
||
const partnerDataToSave = {
|
||
id: editingPartner?.id,
|
||
name: formPartnerName.trim(),
|
||
tag: formPartnerTag.trim() || "Web3",
|
||
mono: formPartnerMono.trim().toUpperCase(),
|
||
year: formPartnerYear.trim() || "2026",
|
||
desc: formPartnerDesc.trim(),
|
||
};
|
||
|
||
const res = await savePartner(partnerDataToSave);
|
||
if (res.success && res.partner) {
|
||
if (isAddingNewPartner) {
|
||
setPartners([...partners, { ...partnerDataToSave, id: res.partner.id }]);
|
||
showToast(`[ YENİ PARTNER YARATILDI // ${formPartnerName.toUpperCase()} ]`, "success");
|
||
setIsAddingNewPartner(false);
|
||
} else {
|
||
const updatedList = partners.map(p => {
|
||
if (p.id === editingPartner?.id) {
|
||
return {
|
||
...p,
|
||
...partnerDataToSave,
|
||
};
|
||
}
|
||
return p;
|
||
});
|
||
setPartners(updatedList);
|
||
showToast(`[ PARTNER BAŞARIYLA DÜZENLENDİ // ${formPartnerName.toUpperCase()} ]`, "success");
|
||
setEditingPartner(null);
|
||
}
|
||
} else {
|
||
showToast(`Hata: ${res.error}`, "alert");
|
||
}
|
||
};
|
||
|
||
const handleDeletePartner = async (id: number, name: string) => {
|
||
if (confirm(`"${name}" iş ortağını silmek istediğinize emin misiniz?`)) {
|
||
const res = await deletePartner(id);
|
||
if (res.success) {
|
||
setPartners(partners.filter(p => p.id !== id));
|
||
showToast(`[ PARTNER SİLİNDİ // ${name.toUpperCase()} ]`, "alert");
|
||
} else {
|
||
showToast(`Hata: ${res.error}`, "alert");
|
||
}
|
||
}
|
||
};
|
||
|
||
// Save Settings Form
|
||
const handleSaveSettings = (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
showToast("[ SİTE AYARLARI BAŞARIYLA KAYDEDİLDİ VE YENİLENDİ ]", "success");
|
||
};
|
||
|
||
// Logout Handler
|
||
const handleLogout = async () => {
|
||
if (confirm("Sistemden çıkış yapmak istediğinize emin misiniz?")) {
|
||
const res = await adminLogoutAction();
|
||
if (res.success) {
|
||
window.location.href = `/${lang}/admin/login`;
|
||
} else {
|
||
showToast("Çıkış yapılırken bir hata oluştu!", "alert");
|
||
}
|
||
}
|
||
};
|
||
|
||
// Filters
|
||
const filteredProjects = projects.filter(p =>
|
||
p.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||
p.tag.toLowerCase().includes(searchQuery.toLowerCase())
|
||
);
|
||
|
||
const filteredPosts = posts.filter(post =>
|
||
post.tr.title.toLowerCase().includes(blogSearchQuery.toLowerCase()) ||
|
||
post.author.toLowerCase().includes(blogSearchQuery.toLowerCase()) ||
|
||
post.tr.category.toLowerCase().includes(blogSearchQuery.toLowerCase())
|
||
);
|
||
|
||
const filteredPartners = partners.filter(p =>
|
||
p.name.toLowerCase().includes(partnerSearchQuery.toLowerCase()) ||
|
||
p.tag.toLowerCase().includes(partnerSearchQuery.toLowerCase()) ||
|
||
p.mono.toLowerCase().includes(partnerSearchQuery.toLowerCase())
|
||
);
|
||
|
||
return (
|
||
<div className="bg-[#F4F0E8] text-[#0A0A0A] font-body min-h-screen relative selection:bg-[#FFE600] selection:text-[#0A0A0A]">
|
||
<Header lang={lang} dict={dict.nav} />
|
||
|
||
<div className="pt-28 pb-24 px-6 max-w-7xl mx-auto flex flex-col lg:flex-row gap-8">
|
||
|
||
{/* LEFT NAV PANEL - Brutalist Sidebar */}
|
||
<aside className="lg:w-1/4 flex flex-col gap-4">
|
||
<div className="border border-[#C8C2B8] bg-[#EDE8E0] p-6 relative overflow-hidden">
|
||
<div className="font-mono text-[9px] tracking-[0.25em] uppercase text-[#A0998E] mb-2">[ SİSTEM YETKİSİ ]</div>
|
||
<h2 className="font-display font-black text-xl uppercase tracking-tight text-[#0A0A0A] mb-1">ADMİN PORTALI</h2>
|
||
<p className="font-mono text-[9px] uppercase tracking-wider text-[#FF4500]">[ DURUM // DOĞRULANDI ]</p>
|
||
</div>
|
||
|
||
<div className="border border-[#C8C2B8] flex flex-col divide-y divide-[#C8C2B8] bg-[#F4F0E8]">
|
||
<button
|
||
onClick={() => setActiveTab("projeler")}
|
||
className={`text-left px-6 py-4 font-display font-black text-xs tracking-widest uppercase transition-all flex items-center justify-between cursor-pointer ${
|
||
activeTab === "projeler"
|
||
? "bg-[#0A0A0A] text-[#F4F0E8] border-l-4 border-[#FFE600]"
|
||
: "hover:bg-[#EDE8E0] text-[#6A6460] hover:text-[#0A0A0A]"
|
||
}`}
|
||
>
|
||
<span>📁 PROJELERİ YÖNET</span>
|
||
<span className="font-mono text-[9px]">{projects.length} AKTİF</span>
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab("blog")}
|
||
className={`text-left px-6 py-4 font-display font-black text-xs tracking-widest uppercase transition-all flex items-center justify-between cursor-pointer ${
|
||
activeTab === "blog"
|
||
? "bg-[#0A0A0A] text-[#F4F0E8] border-l-4 border-[#FFE600]"
|
||
: "hover:bg-[#EDE8E0] text-[#6A6460] hover:text-[#0A0A0A]"
|
||
}`}
|
||
>
|
||
<span>📝 BLOG YAZILARI</span>
|
||
<span className="font-mono text-[9px]">{posts.length} YAZI</span>
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab("partnerler")}
|
||
className={`text-left px-6 py-4 font-display font-black text-xs tracking-widest uppercase transition-all flex items-center justify-between cursor-pointer ${
|
||
activeTab === "partnerler"
|
||
? "bg-[#0A0A0A] text-[#F4F0E8] border-l-4 border-[#FFE600]"
|
||
: "hover:bg-[#EDE8E0] text-[#6A6460] hover:text-[#0A0A0A]"
|
||
}`}
|
||
>
|
||
<span>🤝 PARTNERLER</span>
|
||
<span className="font-mono text-[9px]">{partners.length} ORTAK</span>
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab("ayarlar")}
|
||
className={`text-left px-6 py-4 font-display font-black text-xs tracking-widest uppercase transition-all flex items-center justify-between cursor-pointer ${
|
||
activeTab === "ayarlar"
|
||
? "bg-[#0A0A0A] text-[#F4F0E8] border-l-4 border-[#FFE600]"
|
||
: "hover:bg-[#EDE8E0] text-[#6A6460] hover:text-[#0A0A0A]"
|
||
}`}
|
||
>
|
||
<span>⚙️ SİTE AYARLARI</span>
|
||
<span className="font-mono text-[9px]">GÜNCEL</span>
|
||
</button>
|
||
</div>
|
||
|
||
<Link
|
||
href={`/${lang}`}
|
||
className="border border-[#0A0A0A] hover:bg-[#0A0A0A] hover:text-[#F4F0E8] text-center py-3.5 font-display font-black text-[10px] tracking-widest uppercase transition-colors"
|
||
>
|
||
← SİTEYE GERİ DÖN
|
||
</Link>
|
||
|
||
<button
|
||
onClick={handleLogout}
|
||
className="border border-[#FF4500] hover:bg-[#FF4500] hover:text-white text-center py-3.5 font-display font-black text-[10px] tracking-widest uppercase transition-colors cursor-pointer"
|
||
>
|
||
❌ SİSTEMDEN ÇIK
|
||
</button>
|
||
</aside>
|
||
|
||
{/* RIGHT MAIN PANEL - Dashboard workspace */}
|
||
<main className="flex-grow lg:w-3/4">
|
||
<AnimatePresence mode="wait">
|
||
|
||
{/* TAB 1: PROJELER */}
|
||
{activeTab === "projeler" && (
|
||
<motion.div
|
||
key="projeler-tab"
|
||
initial={{ opacity: 0, y: 15 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
exit={{ opacity: 0, y: -15 }}
|
||
transition={{ duration: 0.4, ease: expo }}
|
||
className="space-y-6"
|
||
>
|
||
<div className="border border-[#C8C2B8] p-4 bg-[#EDE8E0] flex flex-col sm:flex-row gap-4 items-stretch sm:items-center justify-between">
|
||
<div className="relative flex-grow max-w-md">
|
||
<input
|
||
type="text"
|
||
placeholder="Projelerde ara..."
|
||
value={searchQuery}
|
||
onChange={(e) => setSearchQuery(e.target.value)}
|
||
className="w-full font-mono text-[11px] bg-[#F4F0E8] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-4 py-3 placeholder-[#A0998E] text-[#0A0A0A] transition-colors"
|
||
/>
|
||
</div>
|
||
<button
|
||
onClick={handleStartAdd}
|
||
className="btn-brutal btn-brutal-yellow font-display font-black text-[11px] tracking-widest uppercase px-6 py-3 cursor-pointer shrink-0"
|
||
>
|
||
+ YENİ PROJE EKLE
|
||
</button>
|
||
</div>
|
||
|
||
<div className="flex flex-col gap-4">
|
||
{filteredProjects.map((p) => (
|
||
<div
|
||
key={p.num}
|
||
className="border border-[#C8C2B8] hover:border-[#0A0A0A] bg-[#F4F0E8] p-6 flex flex-col md:flex-row md:items-center justify-between gap-6 transition-all group relative overflow-hidden"
|
||
>
|
||
<div className="absolute top-0 bottom-0 left-0 w-1 bg-[#FFE600]" />
|
||
<div className="space-y-2 pl-2">
|
||
<div className="flex items-center gap-3">
|
||
<span className="font-mono text-[10px] tracking-wider text-[#A0998E] border border-[#C8C2B8] px-2 py-0.5 uppercase">
|
||
PROJE // 0{p.num}
|
||
</span>
|
||
<span className="font-mono text-[9px] text-[#FF4500] uppercase tracking-widest font-bold">
|
||
{p.tag}
|
||
</span>
|
||
</div>
|
||
<h3 className="font-display font-black text-2xl uppercase tracking-tight text-[#0A0A0A]">
|
||
{p.title}
|
||
</h3>
|
||
<div className="flex flex-wrap gap-1.5 pt-1">
|
||
{p.tech.map((t) => (
|
||
<span key={t} className="font-mono text-[9px] uppercase bg-[#EDE8E0] px-2 py-0.5 border border-[#C8C2B8]">
|
||
{t}
|
||
</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-2 shrink-0 md:self-center">
|
||
<button
|
||
onClick={() => handleStartEdit(p)}
|
||
className="border border-[#C8C2B8] hover:border-[#0A0A0A] hover:bg-[#0A0A0A] hover:text-[#FFE600] px-4 py-2.5 font-display font-black text-[10px] tracking-wider uppercase transition-all cursor-pointer bg-[#F4F0E8]"
|
||
>
|
||
DÜZENLE
|
||
</button>
|
||
<button
|
||
onClick={() => handleDeleteProject(p.id!, p.title)}
|
||
className="border border-[#FF4500] text-[#FF4500] hover:bg-[#FF4500] hover:text-white px-4 py-2.5 font-display font-black text-[10px] tracking-wider uppercase transition-all cursor-pointer bg-[#F4F0E8]"
|
||
>
|
||
SİL
|
||
</button>
|
||
</div>
|
||
</div>
|
||
))}
|
||
|
||
{filteredProjects.length === 0 && (
|
||
<div className="border border-dashed border-[#C8C2B8] py-16 text-center">
|
||
<p className="font-mono text-xs uppercase text-[#A0998E]">[ HİÇBİR PROJE BULUNAMADI ]</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
|
||
{/* TAB 2: BLOG YAZILARI YÖNETİMİ */}
|
||
{activeTab === "blog" && (
|
||
<motion.div
|
||
key="blog-tab"
|
||
initial={{ opacity: 0, y: 15 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
exit={{ opacity: 0, y: -15 }}
|
||
transition={{ duration: 0.4, ease: expo }}
|
||
className="space-y-6"
|
||
>
|
||
<div className="border border-[#C8C2B8] p-4 bg-[#EDE8E0] flex flex-col sm:flex-row gap-4 items-stretch sm:items-center justify-between">
|
||
<div className="relative flex-grow max-w-md">
|
||
<input
|
||
type="text"
|
||
placeholder="Başlık, yazar veya kategori ara..."
|
||
value={blogSearchQuery}
|
||
onChange={(e) => setBlogSearchQuery(e.target.value)}
|
||
className="w-full font-mono text-[11px] bg-[#F4F0E8] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-4 py-3 placeholder-[#A0998E] text-[#0A0A0A] transition-colors"
|
||
/>
|
||
</div>
|
||
<button
|
||
onClick={handleStartAddPost}
|
||
className="btn-brutal btn-brutal-yellow font-display font-black text-[11px] tracking-widest uppercase px-6 py-3 cursor-pointer shrink-0"
|
||
>
|
||
+ YENİ YAZI EKLE
|
||
</button>
|
||
</div>
|
||
|
||
<div className="flex flex-col gap-4">
|
||
{filteredPosts.map((post) => (
|
||
<div
|
||
key={post.slug}
|
||
className="border border-[#C8C2B8] hover:border-[#0A0A0A] bg-[#F4F0E8] p-6 flex flex-col md:flex-row md:items-center justify-between gap-6 transition-all group relative overflow-hidden"
|
||
>
|
||
<div className="absolute top-0 bottom-0 left-0 w-1 bg-[#FF4500]" />
|
||
<div className="space-y-2 pl-2">
|
||
<div className="flex items-center gap-3">
|
||
<span className="font-mono text-[9px] tracking-wider text-[#A0998E] border border-[#C8C2B8] px-2 py-0.5 uppercase">
|
||
SLUG: {post.slug}
|
||
</span>
|
||
<span className="font-mono text-[9px] text-[#00CC77] uppercase tracking-widest font-bold">
|
||
{post.tr.category}
|
||
</span>
|
||
</div>
|
||
<h3 className="font-display font-black text-xl uppercase tracking-tight text-[#0A0A0A]">
|
||
{post.tr.title}
|
||
</h3>
|
||
<p className="text-[11px] text-[#A0998E] font-mono uppercase">
|
||
Yazar: {post.author} ({post.authorRole}) • Tarih: {post.date}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-2 shrink-0 md:self-center">
|
||
<button
|
||
onClick={() => handleStartEditPost(post)}
|
||
className="border border-[#C8C2B8] hover:border-[#0A0A0A] hover:bg-[#0A0A0A] hover:text-[#FFE600] px-4 py-2.5 font-display font-black text-[10px] tracking-wider uppercase transition-all cursor-pointer bg-[#F4F0E8]"
|
||
>
|
||
DÜZENLE
|
||
</button>
|
||
<button
|
||
onClick={() => handleDeletePost(post.id!, post.tr.title)}
|
||
className="border border-[#FF4500] text-[#FF4500] hover:bg-[#FF4500] hover:text-white px-4 py-2.5 font-display font-black text-[10px] tracking-wider uppercase transition-all cursor-pointer bg-[#F4F0E8]"
|
||
>
|
||
SİL
|
||
</button>
|
||
</div>
|
||
</div>
|
||
))}
|
||
|
||
{filteredPosts.length === 0 && (
|
||
<div className="border border-dashed border-[#C8C2B8] py-16 text-center">
|
||
<p className="font-mono text-xs uppercase text-[#A0998E]">[ HİÇBİR BLOG YAZISI BULUNAMADI ]</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
|
||
{/* TAB 2.5: PARTNERLER YÖNETİMİ */}
|
||
{activeTab === "partnerler" && (
|
||
<motion.div
|
||
key="partners-tab"
|
||
initial={{ opacity: 0, y: 15 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
exit={{ opacity: 0, y: -15 }}
|
||
transition={{ duration: 0.4, ease: expo }}
|
||
className="space-y-6"
|
||
>
|
||
<div className="border border-[#C8C2B8] p-4 bg-[#EDE8E0] flex flex-col sm:flex-row gap-4 items-stretch sm:items-center justify-between">
|
||
<div className="relative flex-grow max-w-md">
|
||
<input
|
||
type="text"
|
||
placeholder="Partnerlerde ara..."
|
||
value={partnerSearchQuery}
|
||
onChange={(e) => setPartnerSearchQuery(e.target.value)}
|
||
className="w-full font-mono text-[11px] bg-[#F4F0E8] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-4 py-3 placeholder-[#A0998E] text-[#0A0A0A] transition-colors"
|
||
/>
|
||
</div>
|
||
<button
|
||
onClick={handleStartAddPartner}
|
||
className="btn-brutal btn-brutal-yellow font-display font-black text-[11px] tracking-widest uppercase px-6 py-3 cursor-pointer shrink-0"
|
||
>
|
||
+ YENİ PARTNER EKLE
|
||
</button>
|
||
</div>
|
||
|
||
<div className="flex flex-col gap-4">
|
||
{filteredPartners.map((p) => (
|
||
<div
|
||
key={p.id}
|
||
className="border border-[#C8C2B8] hover:border-[#0A0A0A] bg-[#F4F0E8] p-6 flex flex-col md:flex-row md:items-center justify-between gap-6 transition-all group relative overflow-hidden"
|
||
>
|
||
<div className="absolute top-0 bottom-0 left-0 w-1 bg-[#FFE600]" />
|
||
<div className="space-y-2 pl-2">
|
||
<div className="flex items-center gap-3">
|
||
<span className="font-mono text-[10px] tracking-wider text-[#A0998E] border border-[#C8C2B8] px-2 py-0.5 uppercase">
|
||
MONOGRAM: {p.mono}
|
||
</span>
|
||
<span className="font-mono text-[9px] text-[#FF4500] uppercase tracking-widest font-bold">
|
||
{p.tag}
|
||
</span>
|
||
<span className="font-mono text-[9px] text-[#A0998E] uppercase tracking-widest">
|
||
YIL: {p.year}
|
||
</span>
|
||
</div>
|
||
<h3 className="font-display font-black text-2xl uppercase tracking-tight text-[#0A0A0A]">
|
||
{p.name}
|
||
</h3>
|
||
<p className="text-[12px] text-[#6A6460] font-sans">
|
||
{p.desc}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-2 shrink-0 md:self-center">
|
||
<button
|
||
onClick={() => handleStartEditPartner(p)}
|
||
className="border border-[#C8C2B8] hover:border-[#0A0A0A] hover:bg-[#0A0A0A] hover:text-[#FFE600] px-4 py-2.5 font-display font-black text-[10px] tracking-wider uppercase transition-all cursor-pointer bg-[#F4F0E8]"
|
||
>
|
||
DÜZENLE
|
||
</button>
|
||
<button
|
||
onClick={() => handleDeletePartner(p.id!, p.name)}
|
||
className="border border-[#FF4500] text-[#FF4500] hover:bg-[#FF4500] hover:text-white px-4 py-2.5 font-display font-black text-[10px] tracking-wider uppercase transition-all cursor-pointer bg-[#F4F0E8]"
|
||
>
|
||
SİL
|
||
</button>
|
||
</div>
|
||
</div>
|
||
))}
|
||
|
||
{filteredPartners.length === 0 && (
|
||
<div className="border border-dashed border-[#C8C2B8] py-16 text-center">
|
||
<p className="font-mono text-xs uppercase text-[#A0998E]">[ HİÇBİR PARTNER BULUNAMADI ]</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
|
||
{/* TAB 3: SITE AYARLARI */}
|
||
{activeTab === "ayarlar" && (
|
||
<motion.div
|
||
key="ayarlar-tab"
|
||
initial={{ opacity: 0, y: 15 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
exit={{ opacity: 0, y: -15 }}
|
||
transition={{ duration: 0.4, ease: expo }}
|
||
className="border border-[#C8C2B8] bg-[#F4F0E8] p-8"
|
||
>
|
||
<div className="border-b border-[#C8C2B8] pb-6 mb-8">
|
||
<h3 className="font-display font-black text-2xl uppercase tracking-tight text-[#0A0A0A]">⚙️ SİTE GENEL YAPILANDIRMASI</h3>
|
||
<p className="text-[#6A6460] text-[13px] mt-1">Görsel kimlik, başlıklar ve iletişim kanallarının yönetimi.</p>
|
||
</div>
|
||
|
||
<form onSubmit={handleSaveSettings} className="space-y-6">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<div className="space-y-2">
|
||
<label className="font-mono text-[10px] tracking-wider uppercase text-[#A0998E] block">MARKA ADI (BRAND NAME)</label>
|
||
<input
|
||
type="text"
|
||
value={brandName}
|
||
onChange={(e) => setBrandName(e.target.value)}
|
||
className="w-full font-mono text-[12px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-4 py-3 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<label className="font-mono text-[10px] tracking-wider uppercase text-[#A0998E] block">MARKA ALT BAŞLIĞI (BRAND SUB)</label>
|
||
<input
|
||
type="text"
|
||
value={brandSub}
|
||
onChange={(e) => setBrandSub(e.target.value)}
|
||
className="w-full font-mono text-[12px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-4 py-3 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<label className="font-mono text-[10px] tracking-wider uppercase text-[#A0998E] block">KAHRAMAN BÖLÜMÜ YAZISI (HERO BADGE)</label>
|
||
<input
|
||
type="text"
|
||
value={heroBadge}
|
||
onChange={(e) => setHeroBadge(e.target.value)}
|
||
className="w-full font-mono text-[12px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-4 py-3 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<div className="space-y-2">
|
||
<label className="font-mono text-[10px] tracking-wider uppercase text-[#A0998E] block">ANA BAŞLIK SATIRI 1</label>
|
||
<input
|
||
type="text"
|
||
value={heroTitle1}
|
||
onChange={(e) => setHeroTitle1(e.target.value)}
|
||
className="w-full font-mono text-[12px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-4 py-3 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<label className="font-mono text-[10px] tracking-wider uppercase text-[#A0998E] block">ANA BAŞLIK SATIRI 2</label>
|
||
<input
|
||
type="text"
|
||
value={heroTitle2}
|
||
onChange={(e) => setHeroTitle2(e.target.value)}
|
||
className="w-full font-mono text-[12px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-4 py-3 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<label className="font-mono text-[10px] tracking-wider uppercase text-[#A0998E] block">HERO AÇIKLAMA METNİ</label>
|
||
<textarea
|
||
value={heroDesc}
|
||
rows={3}
|
||
onChange={(e) => setHeroDesc(e.target.value)}
|
||
className="w-full font-mono text-[12px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-4 py-3 text-[#0A0A0A] resize-y"
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 border-t border-[#C8C2B8] pt-6">
|
||
<div className="space-y-2">
|
||
<label className="font-mono text-[10px] tracking-wider uppercase text-[#A0998E] block">TELEMETRİ İLETİŞİM E-POSTASI</label>
|
||
<input
|
||
type="email"
|
||
value={telemetryEmail}
|
||
onChange={(e) => setTelemetryEmail(e.target.value)}
|
||
className="w-full font-mono text-[12px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-4 py-3 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<label className="font-mono text-[10px] tracking-wider uppercase text-[#A0998E] block">MÜHENDİSLİK OPERASYONEL MERKEZİ</label>
|
||
<input
|
||
type="text"
|
||
value={telemetryHub}
|
||
onChange={(e) => setTelemetryHub(e.target.value)}
|
||
className="w-full font-mono text-[12px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-4 py-3 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-4 border-t border-[#C8C2B8] flex justify-end">
|
||
<button
|
||
type="submit"
|
||
className="btn-brutal btn-brutal-yellow font-display font-black text-[11px] tracking-widest uppercase px-8 py-4 cursor-pointer"
|
||
>
|
||
AYARLARI KAYDET VE SİTEYİ GÜNCELLE
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</motion.div>
|
||
)}
|
||
</AnimatePresence>
|
||
</main>
|
||
</div>
|
||
|
||
{/* CRUD ADD/EDIT MODAL OVERLAY (FOR PROJECTS) */}
|
||
<AnimatePresence>
|
||
{(editingProject || isAddingNew) && (
|
||
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm px-4 py-8 overflow-y-auto">
|
||
<motion.div
|
||
className="bg-[#F4F0E8] border-2 border-[#0A0A0A] p-8 max-w-3xl w-full max-h-[85vh] overflow-y-auto relative shadow-[8px 8px_0px_0px_#0A0A0A]"
|
||
initial={{ scale: 0.95, opacity: 0 }}
|
||
animate={{ scale: 1, opacity: 1 }}
|
||
exit={{ scale: 0.95, opacity: 0 }}
|
||
transition={{ duration: 0.3, ease: expo }}
|
||
>
|
||
<button
|
||
onClick={() => {
|
||
setEditingProject(null);
|
||
setIsAddingNew(false);
|
||
}}
|
||
className="absolute top-6 right-6 font-mono text-xs text-[#A0998E] hover:text-[#0A0A0A] border border-[#C8C2B8] hover:border-[#0A0A0A] w-8 h-8 flex items-center justify-center cursor-pointer bg-[#F4F0E8] transition-colors"
|
||
>
|
||
[X]
|
||
</button>
|
||
|
||
<h3 className="font-display font-black text-2xl uppercase tracking-tight text-[#0A0A0A] mb-6 pb-3 border-b border-[#C8C2B8]">
|
||
{isAddingNew ? "📂 YENİ PROJE EKLEME FORMU" : `⚙️ PROJE DÜZENLEME // 0${editingProject?.num}`}
|
||
</h3>
|
||
|
||
<form onSubmit={handleSaveProject} className="space-y-4">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Proje Başlığı</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formTitle}
|
||
onChange={(e) => setFormTitle(e.target.value)}
|
||
placeholder="Örn: FinanceAI Paneli"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Kategori / Tag</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formTag}
|
||
onChange={(e) => setFormTag(e.target.value)}
|
||
placeholder="Örn: YZ · Finans"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Proje Özet Açıklaması</label>
|
||
<textarea
|
||
rows={2}
|
||
required
|
||
value={formDesc}
|
||
onChange={(e) => setFormDesc(e.target.value)}
|
||
placeholder="Projeyi kısaca açıklayınız..."
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A] resize-y"
|
||
/>
|
||
</div>
|
||
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Performans / Güvence Metriği (Spec)</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formSpec}
|
||
onChange={(e) => setFormSpec(e.target.value)}
|
||
placeholder="Örn: Gerçek zamanlı akış, 10ms altı yanıt süresi."
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Yıl</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formYear}
|
||
onChange={(e) => setFormYear(e.target.value)}
|
||
placeholder="Örn: 2024"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Müşteri</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formClient}
|
||
onChange={(e) => setFormClient(e.target.value)}
|
||
placeholder="Örn: Acme Fintech Ltd."
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Geliştirme Süresi</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formDuration}
|
||
onChange={(e) => setFormDuration(e.target.value)}
|
||
placeholder="Örn: 14 hafta"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Teknoloji Yığını (Virgülle Ayırın)</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formTechString}
|
||
onChange={(e) => setFormTechString(e.target.value)}
|
||
placeholder="Next.js, Python, TensorFlow, Redis, Kafka"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Kapak Görseli URL Adresi</label>
|
||
<div className="flex gap-2">
|
||
<input
|
||
type="text"
|
||
value={formImage}
|
||
onChange={(e) => setFormImage(e.target.value)}
|
||
placeholder="https://images.unsplash.com/..."
|
||
className="flex-grow font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
<label className="border border-[#0A0A0A] px-3 flex items-center justify-center font-display font-black text-[9px] uppercase tracking-wider transition-colors cursor-pointer bg-[#F4F0E8] hover:bg-[#EDE8E0] shrink-0">
|
||
{uploadingImage ? "YÜKLENİYOR..." : "YÜKLE"}
|
||
<input
|
||
type="file"
|
||
accept="image/*"
|
||
className="hidden"
|
||
disabled={uploadingImage}
|
||
onChange={async (e) => {
|
||
const file = e.target.files?.[0];
|
||
if (!file) return;
|
||
setUploadingImage(true);
|
||
try {
|
||
const url = await handleFileUpload(file);
|
||
setFormImage(url);
|
||
} catch (err) {
|
||
showToast("Resim yüklenemedi", "alert");
|
||
} finally {
|
||
setUploadingImage(false);
|
||
}
|
||
}}
|
||
/>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Website URL (Opsiyonel)</label>
|
||
<input
|
||
type="text"
|
||
value={formWebsite}
|
||
onChange={(e) => setFormWebsite(e.target.value)}
|
||
placeholder="https://example.com"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Öne Çıkan Proje (Ana Sayfa İçin)</label>
|
||
<div className="flex items-center h-[30px]">
|
||
<input
|
||
type="checkbox"
|
||
checked={formFeatured}
|
||
onChange={(e) => setFormFeatured(e.target.checked)}
|
||
className="w-4 h-4 cursor-pointer"
|
||
/>
|
||
<span className="ml-2 font-mono text-[10px] text-[#0A0A0A]">Bu bizim kendi projemiz (Ana sayfada göster)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Karşılaşılan Problem (Challenge)</label>
|
||
<textarea
|
||
rows={3}
|
||
required
|
||
value={formChallenge}
|
||
onChange={(e) => setFormChallenge(e.target.value)}
|
||
className="w-full font-mono text-[10px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A] resize-y"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Mühendislik Çözümümüz (Solution)</label>
|
||
<textarea
|
||
rows={3}
|
||
required
|
||
value={formSolution}
|
||
onChange={(e) => setFormSolution(e.target.value)}
|
||
className="w-full font-mono text-[10px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A] resize-y"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-2 pt-2 border-t border-[#C8C2B8]">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Elde Edilen Sonuçlar</label>
|
||
<div className="flex flex-col gap-2 mb-2 max-h-24 overflow-y-auto border border-[#C8C2B8] p-2 bg-[#EDE8E0]">
|
||
{formResults.map((item, idx) => (
|
||
<div key={idx} className="flex items-center justify-between gap-3 bg-[#F4F0E8] border border-[#C8C2B8] px-3 py-1">
|
||
<span className="font-display font-bold text-[9px] uppercase text-[#0A0A0A] truncate">{item}</span>
|
||
<button
|
||
type="button"
|
||
onClick={() => handleRemoveResultItem(idx)}
|
||
className="font-mono text-[8px] text-[#FF4500] cursor-pointer"
|
||
>
|
||
[ SİL ]
|
||
</button>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<div className="flex gap-2">
|
||
<input
|
||
type="text"
|
||
value={newResultItem}
|
||
onChange={(e) => setNewResultItem(e.target.value)}
|
||
className="flex-grow font-mono text-[9px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
<button
|
||
type="button"
|
||
onClick={handleAddResultItem}
|
||
className="border border-[#0A0A0A] px-4 font-display font-black text-[9px] uppercase tracking-wider transition-colors cursor-pointer bg-[#F4F0E8]"
|
||
>
|
||
[ EKLE ]
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-2 pt-2 border-t border-[#C8C2B8]">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Galeri Görselleri (Ekran Görüntüleri)</label>
|
||
<div className="flex flex-col gap-2 mb-2 max-h-24 overflow-y-auto border border-[#C8C2B8] p-2 bg-[#EDE8E0]">
|
||
{formGallery.map((item, idx) => (
|
||
<div key={idx} className="flex items-center justify-between gap-3 bg-[#F4F0E8] border border-[#C8C2B8] px-3 py-1">
|
||
<span className="font-display font-bold text-[9px] uppercase text-[#0A0A0A] truncate">{item}</span>
|
||
<button
|
||
type="button"
|
||
onClick={() => handleRemoveGalleryItem(idx)}
|
||
className="font-mono text-[8px] text-[#FF4500] cursor-pointer"
|
||
>
|
||
[ SİL ]
|
||
</button>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<div className="flex gap-2">
|
||
<input
|
||
type="text"
|
||
value={newGalleryItem}
|
||
onChange={(e) => setNewGalleryItem(e.target.value)}
|
||
placeholder="https://images.unsplash.com/..."
|
||
className="flex-grow font-mono text-[9px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
<label className="border border-[#0A0A0A] px-3 flex items-center justify-center font-display font-black text-[9px] uppercase tracking-wider transition-colors cursor-pointer bg-[#F4F0E8] hover:bg-[#EDE8E0] shrink-0">
|
||
{uploadingGallery ? "YÜKLENİYOR..." : "RESİM YÜKLE"}
|
||
<input
|
||
type="file"
|
||
accept="image/*"
|
||
className="hidden"
|
||
disabled={uploadingGallery}
|
||
onChange={async (e) => {
|
||
const file = e.target.files?.[0];
|
||
if (!file) return;
|
||
setUploadingGallery(true);
|
||
try {
|
||
const url = await handleFileUpload(file);
|
||
setFormGallery(prev => [...prev, url]);
|
||
} catch (err) {
|
||
showToast("Resim yüklenemedi", "alert");
|
||
} finally {
|
||
setUploadingGallery(false);
|
||
}
|
||
}}
|
||
/>
|
||
</label>
|
||
<button
|
||
type="button"
|
||
onClick={handleAddGalleryItem}
|
||
className="border border-[#0A0A0A] px-4 font-display font-black text-[9px] uppercase tracking-wider transition-colors cursor-pointer bg-[#F4F0E8] shrink-0"
|
||
>
|
||
[ EKLE ]
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-4 border-t border-[#C8C2B8] flex justify-end gap-2">
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
setEditingProject(null);
|
||
setIsAddingNew(false);
|
||
}}
|
||
className="border border-[#C8C2B8] hover:border-[#0A0A0A] px-6 py-3 font-display font-black text-[10px] tracking-wider uppercase transition-colors cursor-pointer bg-[#F4F0E8]"
|
||
>
|
||
İPTAL ET
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="btn-brutal btn-brutal-yellow font-display font-black text-[10px] tracking-widest uppercase px-6 py-3 cursor-pointer"
|
||
>
|
||
PROJEYİ KAYDET VE UYGULA
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</motion.div>
|
||
</div>
|
||
)}
|
||
</AnimatePresence>
|
||
|
||
{/* CRUD ADD/EDIT MODAL OVERLAY (FOR BLOG POSTS) */}
|
||
<AnimatePresence>
|
||
{(editingPost || isAddingNewPost) && (
|
||
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm px-4 py-8 overflow-y-auto">
|
||
<motion.div
|
||
className="bg-[#F4F0E8] border-2 border-[#0A0A0A] p-8 max-w-3xl w-full max-h-[85vh] overflow-y-auto relative shadow-[8px 8px_0px_0px_#0A0A0A]"
|
||
initial={{ scale: 0.95, opacity: 0 }}
|
||
animate={{ scale: 1, opacity: 1 }}
|
||
exit={{ scale: 0.95, opacity: 0 }}
|
||
transition={{ duration: 0.3, ease: expo }}
|
||
>
|
||
<button
|
||
onClick={() => {
|
||
setEditingPost(null);
|
||
setIsAddingNewPost(false);
|
||
}}
|
||
className="absolute top-6 right-6 font-mono text-xs text-[#A0998E] hover:text-[#0A0A0A] border border-[#C8C2B8] hover:border-[#0A0A0A] w-8 h-8 flex items-center justify-center cursor-pointer bg-[#F4F0E8] transition-colors"
|
||
>
|
||
[X]
|
||
</button>
|
||
|
||
<h3 className="font-display font-black text-2xl uppercase tracking-tight text-[#0A0A0A] mb-4 pb-3 border-b border-[#C8C2B8]">
|
||
{isAddingNewPost ? "📝 YENİ BLOG YAZISI EKLE" : `⚙️ BLOG YAZISI DÜZENLEME`}
|
||
</h3>
|
||
|
||
{/* Form Sub-Tabs Navigation */}
|
||
<div className="flex border border-[#C8C2B8] bg-[#EDE8E0] mb-6">
|
||
<button
|
||
type="button"
|
||
onClick={() => setBlogFormSubTab("genel")}
|
||
className={`flex-1 py-2.5 font-display font-black text-[10px] tracking-wider uppercase transition-colors cursor-pointer ${
|
||
blogFormSubTab === "genel" ? "bg-[#0A0A0A] text-[#F4F0E8]" : "hover:bg-[#F4F0E8] text-[#6A6460]"
|
||
}`}
|
||
>
|
||
Genel Bilgiler
|
||
</button>
|
||
<button
|
||
type="button"
|
||
onClick={() => setBlogFormSubTab("tr")}
|
||
className={`flex-1 py-2.5 font-display font-black text-[10px] tracking-wider uppercase transition-colors cursor-pointer ${
|
||
blogFormSubTab === "tr" ? "bg-[#0A0A0A] text-[#F4F0E8]" : "hover:bg-[#F4F0E8] text-[#6A6460]"
|
||
}`}
|
||
>
|
||
Türkçe İçerik (TR)
|
||
</button>
|
||
<button
|
||
type="button"
|
||
onClick={() => setBlogFormSubTab("en")}
|
||
className={`flex-1 py-2.5 font-display font-black text-[10px] tracking-wider uppercase transition-colors cursor-pointer ${
|
||
blogFormSubTab === "en" ? "bg-[#0A0A0A] text-[#F4F0E8]" : "hover:bg-[#F4F0E8] text-[#6A6460]"
|
||
}`}
|
||
>
|
||
English Content (EN)
|
||
</button>
|
||
</div>
|
||
|
||
<form onSubmit={handleSavePost} className="space-y-4">
|
||
|
||
{/* SUB TAB: GENEL BİLGİLER */}
|
||
{blogFormSubTab === "genel" && (
|
||
<div className="space-y-4 animate-fadeIn">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Yazı Slug Değeri (URL)</label>
|
||
<input
|
||
type="text"
|
||
value={formBlogSlug}
|
||
onChange={(e) => setFormBlogSlug(e.target.value)}
|
||
placeholder="Örn: real-time-anomaly-detection-ai"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Yayın Tarihi</label>
|
||
<input
|
||
type="date"
|
||
required
|
||
value={formBlogDate}
|
||
onChange={(e) => setFormBlogDate(e.target.value)}
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Yazar Adı Soyadı</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formBlogAuthor}
|
||
onChange={(e) => setFormBlogAuthor(e.target.value)}
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Yazar Rolü</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formBlogAuthorRole}
|
||
onChange={(e) => setFormBlogAuthorRole(e.target.value)}
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Kapak Görseli URL Adresi</label>
|
||
<input
|
||
type="text"
|
||
value={formBlogImage}
|
||
onChange={(e) => setFormBlogImage(e.target.value)}
|
||
placeholder="https://images.unsplash.com/..."
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* SUB TAB: TÜRKÇE İÇERİK */}
|
||
{blogFormSubTab === "tr" && (
|
||
<div className="space-y-4 animate-fadeIn">
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
<div className="md:col-span-2 space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Yazı Başlığı (TR)</label>
|
||
<input
|
||
type="text"
|
||
required={blogFormSubTab === "tr"}
|
||
value={formBlogTrTitle}
|
||
onChange={(e) => setFormBlogTrTitle(e.target.value)}
|
||
placeholder="Türkçe başlık giriniz..."
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Okuma Süresi (TR)</label>
|
||
<input
|
||
type="text"
|
||
value={formBlogTrReadingTime}
|
||
onChange={(e) => setFormBlogTrReadingTime(e.target.value)}
|
||
placeholder="Örn: 5 dk okuma"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Kategori (TR)</label>
|
||
<input
|
||
type="text"
|
||
value={formBlogTrCategory}
|
||
onChange={(e) => setFormBlogTrCategory(e.target.value)}
|
||
placeholder="Örn: YZ · Finans"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Etiketler (Virgülle Ayırın)</label>
|
||
<input
|
||
type="text"
|
||
value={formBlogTrTags}
|
||
onChange={(e) => setFormBlogTrTags(e.target.value)}
|
||
placeholder="AI, Fintech, Web3"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Özet Açıklama (TR)</label>
|
||
<textarea
|
||
rows={2}
|
||
value={formBlogTrExcerpt}
|
||
onChange={(e) => setFormBlogTrExcerpt(e.target.value)}
|
||
placeholder="Kısa özet giriniz..."
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A] resize-y"
|
||
/>
|
||
</div>
|
||
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Makale Metni Markdown (TR)</label>
|
||
<textarea
|
||
rows={6}
|
||
value={formBlogTrContent}
|
||
onChange={(e) => setFormBlogTrContent(e.target.value)}
|
||
placeholder="## Başlıklar ve paragraflar..."
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A] resize-y"
|
||
/>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* SUB TAB: ENGLISH CONTENT */}
|
||
{blogFormSubTab === "en" && (
|
||
<div className="space-y-4 animate-fadeIn">
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
<div className="md:col-span-2 space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Article Title (EN)</label>
|
||
<input
|
||
type="text"
|
||
value={formBlogEnTitle}
|
||
onChange={(e) => setFormBlogEnTitle(e.target.value)}
|
||
placeholder="Enter English title..."
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Reading Time (EN)</label>
|
||
<input
|
||
type="text"
|
||
value={formBlogEnReadingTime}
|
||
onChange={(e) => setFormBlogEnReadingTime(e.target.value)}
|
||
placeholder="e.g. 5 min read"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Category (EN)</label>
|
||
<input
|
||
type="text"
|
||
value={formBlogEnCategory}
|
||
onChange={(e) => setFormBlogEnCategory(e.target.value)}
|
||
placeholder="e.g. AI · Fintech"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Tags (Comma Separated)</label>
|
||
<input
|
||
type="text"
|
||
value={formBlogEnTags}
|
||
onChange={(e) => setFormBlogEnTags(e.target.value)}
|
||
placeholder="AI, Fintech, Web3"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Excerpt (EN)</label>
|
||
<textarea
|
||
rows={2}
|
||
value={formBlogEnExcerpt}
|
||
onChange={(e) => setFormBlogEnExcerpt(e.target.value)}
|
||
placeholder="Enter short description..."
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A] resize-y"
|
||
/>
|
||
</div>
|
||
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Article Body Markdown (EN)</label>
|
||
<textarea
|
||
rows={6}
|
||
value={formBlogEnContent}
|
||
onChange={(e) => setFormBlogEnContent(e.target.value)}
|
||
placeholder="## Headers and paragraphs..."
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A] resize-y"
|
||
/>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Sub-tab navigation helpers */}
|
||
<div className="flex gap-2 font-mono text-[9px] text-[#A0998E] py-2">
|
||
<span>* Lütfen formu onaylamadan önce Türkçe ve İngilizce tüm sekmeleri kontrol edin.</span>
|
||
</div>
|
||
|
||
{/* Actions */}
|
||
<div className="pt-4 border-t border-[#C8C2B8] flex justify-end gap-2">
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
setEditingPost(null);
|
||
setIsAddingNewPost(false);
|
||
}}
|
||
className="border border-[#C8C2B8] hover:border-[#0A0A0A] px-6 py-3 font-display font-black text-[10px] tracking-wider uppercase transition-colors cursor-pointer bg-[#F4F0E8]"
|
||
>
|
||
İPTAL ET
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="btn-brutal btn-brutal-yellow font-display font-black text-[10px] tracking-widest uppercase px-6 py-3 cursor-pointer"
|
||
>
|
||
YAZIYI PAYLAŞ VE YAYINLA
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</motion.div>
|
||
</div>
|
||
)}
|
||
</AnimatePresence>
|
||
|
||
{/* CRUD ADD/EDIT MODAL OVERLAY (FOR PARTNERS) */}
|
||
<AnimatePresence>
|
||
{(editingPartner || isAddingNewPartner) && (
|
||
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm px-4 py-8 overflow-y-auto">
|
||
<motion.div
|
||
className="bg-[#F4F0E8] border-2 border-[#0A0A0A] p-8 max-w-xl w-full max-h-[85vh] overflow-y-auto relative shadow-[8px 8px_0px_0px_#0A0A0A]"
|
||
initial={{ scale: 0.95, opacity: 0 }}
|
||
animate={{ scale: 1, opacity: 1 }}
|
||
exit={{ scale: 0.95, opacity: 0 }}
|
||
transition={{ duration: 0.3, ease: expo }}
|
||
>
|
||
<button
|
||
onClick={() => {
|
||
setEditingPartner(null);
|
||
setIsAddingNewPartner(false);
|
||
}}
|
||
className="absolute top-6 right-6 font-mono text-xs text-[#A0998E] hover:text-[#0A0A0A] border border-[#C8C2B8] hover:border-[#0A0A0A] w-8 h-8 flex items-center justify-center cursor-pointer bg-[#F4F0E8] transition-colors"
|
||
>
|
||
[X]
|
||
</button>
|
||
|
||
<h3 className="font-display font-black text-2xl uppercase tracking-tight text-[#0A0A0A] mb-6 pb-3 border-b border-[#C8C2B8]">
|
||
{isAddingNewPartner ? "🤝 YENİ PARTNER EKLE" : `⚙️ PARTNER DÜZENLEME`}
|
||
</h3>
|
||
|
||
<form onSubmit={handleSavePartner} className="space-y-4">
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Partner İsmi</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formPartnerName}
|
||
onChange={(e) => setFormPartnerName(e.target.value)}
|
||
placeholder="Örn: Ayris Labs"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Monogram (Görsel Monolog)</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formPartnerMono}
|
||
onChange={(e) => setFormPartnerMono(e.target.value)}
|
||
placeholder="Örn: AYL"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Kategori / Alan</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formPartnerTag}
|
||
onChange={(e) => setFormPartnerTag(e.target.value)}
|
||
placeholder="Örn: Web3"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Başlangıç Yılı</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formPartnerYear}
|
||
onChange={(e) => setFormPartnerYear(e.target.value)}
|
||
placeholder="Örn: 2026"
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-1">
|
||
<label className="font-mono text-[9px] uppercase text-[#A0998E] block">Kısa Açıklama / Biyografi</label>
|
||
<textarea
|
||
rows={4}
|
||
value={formPartnerDesc}
|
||
onChange={(e) => setFormPartnerDesc(e.target.value)}
|
||
placeholder="Partner hakkında kısa detay..."
|
||
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] focus:border-[#0A0A0A] outline-none px-3 py-2 text-[#0A0A0A] resize-none"
|
||
/>
|
||
</div>
|
||
|
||
<div className="pt-4 border-t border-[#C8C2B8] flex justify-end gap-2">
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
setEditingPartner(null);
|
||
setIsAddingNewPartner(false);
|
||
}}
|
||
className="border border-[#C8C2B8] hover:border-[#0A0A0A] px-6 py-3 font-display font-black text-[10px] tracking-wider uppercase transition-colors cursor-pointer bg-[#F4F0E8]"
|
||
>
|
||
İPTAL ET
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="btn-brutal btn-brutal-yellow font-display font-black text-[10px] tracking-widest uppercase px-6 py-3 cursor-pointer"
|
||
>
|
||
PARTNERİ KAYDET
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</motion.div>
|
||
</div>
|
||
)}
|
||
</AnimatePresence>
|
||
|
||
{/* TACTILE FLOATING NOTIFICATION TOAST */}
|
||
<AnimatePresence>
|
||
{toast && (
|
||
<motion.div
|
||
className="fixed bottom-8 right-8 z-[200] border-2 border-[#0A0A0A] bg-[#FFE600] text-[#0A0A0A] font-mono text-[10px] px-6 py-4 shadow-[4px 4px_0px_0px_#0A0A0A]"
|
||
initial={{ y: 50, opacity: 0, scale: 0.9 }}
|
||
animate={{ y: 0, opacity: 1, scale: 1 }}
|
||
exit={{ y: 50, opacity: 0, scale: 0.9 }}
|
||
transition={{ duration: 0.35, ease: expo }}
|
||
>
|
||
<div className="flex items-center gap-3">
|
||
<span className="animate-pulse">●</span>
|
||
<span>{toast.text}</span>
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
</AnimatePresence>
|
||
</div>
|
||
);
|
||
}
|