2533 lines
124 KiB
TypeScript
2533 lines
124 KiB
TypeScript
'use client';
|
||
|
||
import { useEffect, useState } from 'react';
|
||
import { useRouter } from 'next/navigation';
|
||
import { dataStore, type SliderItem, type LiveStreamConfig, type Message, type SiteSettings, type FAQ, type Camera } from '@/lib/dataStore';
|
||
import type { NewsItem } from '@/data/news';
|
||
import type { MediaItem } from '@/data/media';
|
||
import type { Document } from '@/data/documents';
|
||
import type { MetroStation } from '@/data/metroStations';
|
||
|
||
export default function Dashboard() {
|
||
const router = useRouter();
|
||
const [activeSection, setActiveSection] = useState('overview');
|
||
const [menuOpen, setMenuOpen] = useState(false);
|
||
|
||
// Gerçek veriler için state'ler
|
||
const [sliderItems, setSliderItems] = useState<SliderItem[]>([]);
|
||
const [newsItems, setNewsItems] = useState<NewsItem[]>([]);
|
||
const [mediaItems, setMediaItems] = useState<MediaItem[]>([]);
|
||
const [documents, setDocuments] = useState<Document[]>([]);
|
||
const [metroStations, setMetroStations] = useState<MetroStation[]>([]);
|
||
const [liveStreamConfig, setLiveStreamConfig] = useState<LiveStreamConfig | null>(null);
|
||
const [messages, setMessages] = useState<Message[]>([]);
|
||
const [siteSettings, setSiteSettings] = useState<SiteSettings | null>(null);
|
||
const [faqs, setFaqs] = useState<FAQ[]>([]);
|
||
const [cameras, setCameras] = useState<Camera[]>([]);
|
||
|
||
useEffect(() => {
|
||
const token = localStorage.getItem('admin_token');
|
||
if (!token) {
|
||
router.push('/yonetim-paneli-a2m-secure');
|
||
return;
|
||
}
|
||
|
||
// Verileri yükle
|
||
loadData();
|
||
}, [router]);
|
||
|
||
const loadData = async () => {
|
||
try {
|
||
const [slider, news, media, docs, stations, liveStream, msgs, settings, faqList, cameraList] = await Promise.all([
|
||
dataStore.getSlider(),
|
||
dataStore.getNews(),
|
||
dataStore.getMedia(),
|
||
dataStore.getDocuments(),
|
||
dataStore.getMetroStations(),
|
||
dataStore.getLiveStream(),
|
||
dataStore.getMessages(),
|
||
dataStore.getSiteSettings(),
|
||
dataStore.getFAQs(),
|
||
dataStore.getCameras(),
|
||
]);
|
||
|
||
setSliderItems(slider);
|
||
setNewsItems(news);
|
||
setMediaItems(media);
|
||
setDocuments(docs);
|
||
setMetroStations(stations);
|
||
setLiveStreamConfig(liveStream);
|
||
setMessages(msgs);
|
||
setSiteSettings(settings);
|
||
setFaqs(faqList.sort((a, b) => a.order - b.order));
|
||
setCameras(cameraList.sort((a, b) => a.order - b.order));
|
||
} catch (error) {
|
||
console.error('Veri yükleme hatası:', error);
|
||
}
|
||
};
|
||
|
||
const handleLogout = async () => {
|
||
try {
|
||
await fetch('/api/auth/logout', { method: 'POST' });
|
||
localStorage.removeItem('admin_token');
|
||
router.push('/yonetim-paneli-a2m-secure');
|
||
} catch (error) {
|
||
console.error('Logout error:', error);
|
||
router.push('/yonetim-paneli-a2m-secure');
|
||
}
|
||
};
|
||
|
||
const stats = [
|
||
{ title: 'Aktif Slider', value: sliderItems.filter(s => s.active).length.toString(), icon: '🎬', color: 'from-[#004B87] to-[#00B4D8]', change: '+1' },
|
||
{ title: 'Toplam Haberler', value: newsItems.length.toString(), icon: '📰', color: 'from-[#00B4D8] to-[#0096C7]', change: '+2' },
|
||
{ title: 'Medya İçeriği', value: mediaItems.length.toString(), icon: '📸', color: 'from-[#0096C7] to-[#48CAE4]', change: '+3' },
|
||
{ title: 'Gelen Mesajlar', value: messages.filter(m => !m.read).length.toString(), icon: '✉️', color: 'from-[#48CAE4] to-[#90E0EF]', change: `+${messages.filter(m => !m.read).length}` },
|
||
];
|
||
|
||
const menuItems = [
|
||
{ id: 'overview', label: 'Genel Bakış', icon: '📊' },
|
||
{ id: 'slider', label: 'Slider Yönetimi', icon: '🎬' },
|
||
{ id: 'news', label: 'Haberler', icon: '📰' },
|
||
{ id: 'media', label: 'Medya', icon: '📸' },
|
||
{ id: 'documents', label: 'Belgeler', icon: '📄' },
|
||
{ id: 'metro-line', label: 'Metro Hattı', icon: '🚇' },
|
||
{ id: 'live-stream', label: 'Canlı Yayın', icon: '📺' },
|
||
{ id: 'cameras', label: 'Kameralar', icon: '📹' },
|
||
{ id: 'faqs', label: 'SSS Yönetimi', icon: '❓' },
|
||
{ id: 'messages', label: 'Gelen Mesajlar', icon: '✉️', badge: messages.filter(m => !m.read).length || undefined },
|
||
{ id: 'site-settings', label: 'Site Ayarları', icon: '⚙️' },
|
||
];
|
||
|
||
// Slider management functions
|
||
const toggleSlideActive = async (id: number) => {
|
||
const item = sliderItems.find(s => s.id === id);
|
||
if (item) {
|
||
await dataStore.updateSlider(id, { active: !item.active });
|
||
loadData();
|
||
}
|
||
};
|
||
|
||
const updateSlide = async (id: number, updates: Partial<SliderItem>) => {
|
||
await dataStore.updateSlider(id, updates);
|
||
loadData();
|
||
};
|
||
|
||
// News management functions
|
||
const handleDeleteNews = async (id: number) => {
|
||
if (confirm('Bu haberi silmek istediğinizden emin misiniz?')) {
|
||
await dataStore.deleteNews(id);
|
||
loadData();
|
||
}
|
||
};
|
||
|
||
const handleToggleFeatured = (id: number) => {
|
||
const news = newsItems.find(n => n.id === id);
|
||
if (news) {
|
||
dataStore.updateNews(id, { featured: !news.featured });
|
||
loadData();
|
||
}
|
||
};
|
||
|
||
// Media management functions
|
||
const handleDeleteMedia = async (id: number) => {
|
||
if (confirm('Bu medya içeriğini silmek istediğinizden emin misiniz?')) {
|
||
await dataStore.deleteMedia(id);
|
||
loadData();
|
||
}
|
||
};
|
||
|
||
// Document management functions
|
||
const handleDeleteDocument = async (id: number) => {
|
||
if (confirm('Bu belgeyi silmek istediğinizden emin misiniz?')) {
|
||
await dataStore.deleteDocument(id);
|
||
loadData();
|
||
}
|
||
};
|
||
|
||
// Metro station update function (used in modal)
|
||
// const handleUpdateStation = (id: number, updates: Partial<MetroStation>) => {
|
||
// dataStore.updateStation(id, updates);
|
||
// loadData();
|
||
// };
|
||
|
||
// Categories
|
||
const categories = [
|
||
{ id: 'all', name: 'Tümü', icon: '📋' },
|
||
{ id: 'construction', name: 'İnşaat', icon: '🏗️' },
|
||
{ id: 'announcement', name: 'Duyuru', icon: '📢' },
|
||
{ id: 'event', name: 'Etkinlik', icon: '🎉' },
|
||
];
|
||
|
||
const documentCategories = [
|
||
{ id: 'all', name: 'Tümü', icon: '📋' },
|
||
{ id: 'technical', name: 'Teknik', icon: '⚙️' },
|
||
{ id: 'administrative', name: 'İdari', icon: '📊' },
|
||
{ id: 'legal', name: 'Hukuki', icon: '⚖️' },
|
||
{ id: 'financial', name: 'Mali', icon: '💰' },
|
||
];
|
||
|
||
// Form states
|
||
const [selectedNewsCategory, setSelectedNewsCategory] = useState('all');
|
||
// const [editingNews, setEditingNews] = useState<NewsItem | null>(null);
|
||
// const [showNewsForm, setShowNewsForm] = useState(false);
|
||
const [selectedMediaType, setSelectedMediaType] = useState<'all' | 'video' | 'photo'>('all');
|
||
const [selectedDocCategory, setSelectedDocCategory] = useState('all');
|
||
|
||
// Filtered lists
|
||
const filteredNewsList = selectedNewsCategory === 'all'
|
||
? newsItems
|
||
: newsItems.filter(news => news.category === selectedNewsCategory);
|
||
|
||
const filteredMediaList = selectedMediaType === 'all'
|
||
? mediaItems
|
||
: mediaItems.filter(media => media.type === selectedMediaType);
|
||
|
||
const filteredDocsList = selectedDocCategory === 'all'
|
||
? documents
|
||
: documents.filter(doc => doc.category === selectedDocCategory);
|
||
|
||
// Station filtering and selection
|
||
const [selectedStationStatus, setSelectedStationStatus] = useState<'all' | 'completed' | 'in-progress' | 'planned'>('all');
|
||
const [selectedStationId, setSelectedStationId] = useState<number | null>(null);
|
||
|
||
// Edit modals
|
||
const [editingSlide, setEditingSlide] = useState<SliderItem | null>(null);
|
||
const [showSlideModal, setShowSlideModal] = useState(false);
|
||
const [editingNewsItem, setEditingNewsItem] = useState<NewsItem | null>(null);
|
||
const [showNewsModal, setShowNewsModal] = useState(false);
|
||
const [editingMediaItem, setEditingMediaItem] = useState<MediaItem | null>(null);
|
||
const [showMediaModal, setShowMediaModal] = useState(false);
|
||
const [editingDocument, setEditingDocument] = useState<Document | null>(null);
|
||
const [showDocumentModal, setShowDocumentModal] = useState(false);
|
||
const [editingStation, setEditingStation] = useState<MetroStation | null>(null);
|
||
const [showStationModal, setShowStationModal] = useState(false);
|
||
|
||
const filteredStationsList = selectedStationStatus === 'all'
|
||
? metroStations
|
||
: metroStations.filter(station => station.status === selectedStationStatus);
|
||
|
||
const handleStationClick = (stationId: number) => {
|
||
const newSelectedId = stationId === selectedStationId ? null : stationId;
|
||
setSelectedStationId(newSelectedId);
|
||
|
||
// Ana sayfadaki metro animasyonu için seçili istasyonu kaydet
|
||
if (newSelectedId !== null) {
|
||
localStorage.setItem('metro_selected_station', newSelectedId.toString());
|
||
} else {
|
||
localStorage.removeItem('metro_selected_station');
|
||
}
|
||
};
|
||
|
||
// Slider edit handlers
|
||
const handleEditSlide = (slide: SliderItem) => {
|
||
setEditingSlide(slide);
|
||
setShowSlideModal(true);
|
||
};
|
||
|
||
const handleSaveSlide = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (editingSlide) {
|
||
const isNew = !sliderItems.find(s => s.id === editingSlide.id);
|
||
if (isNew) {
|
||
await dataStore.addSlider(editingSlide);
|
||
} else {
|
||
await dataStore.updateSlider(editingSlide.id, editingSlide);
|
||
}
|
||
loadData();
|
||
setShowSlideModal(false);
|
||
setEditingSlide(null);
|
||
}
|
||
};
|
||
|
||
const handleAddSlide = () => {
|
||
const newSlide: SliderItem = {
|
||
id: Math.max(...sliderItems.map(s => s.id), 0) + 1,
|
||
title: '',
|
||
description: '',
|
||
buttonText: 'Detaylı Bilgi',
|
||
buttonLink: '#',
|
||
active: false
|
||
};
|
||
setEditingSlide(newSlide);
|
||
setShowSlideModal(true);
|
||
};
|
||
|
||
const handleDeleteSlide = (id: number) => {
|
||
if (confirm('Bu slider\'ı silmek istediğinizden emin misiniz?')) {
|
||
const updated = sliderItems.filter(s => s.id !== id);
|
||
setSliderItems(updated);
|
||
dataStore.setSlider(updated);
|
||
}
|
||
};
|
||
|
||
// News edit handlers
|
||
const handleAddNews = () => {
|
||
const newNews: NewsItem = {
|
||
id: Math.max(...newsItems.map(n => n.id), 0) + 1,
|
||
title: '',
|
||
summary: '',
|
||
content: '',
|
||
date: new Date().toLocaleDateString('tr-TR'),
|
||
image: '',
|
||
category: 'announcements',
|
||
author: 'Admin',
|
||
tags: [],
|
||
featured: false
|
||
};
|
||
setEditingNewsItem(newNews);
|
||
setShowNewsModal(true);
|
||
};
|
||
|
||
const handleEditNews = (news: NewsItem) => {
|
||
setEditingNewsItem(news);
|
||
setShowNewsModal(true);
|
||
};
|
||
|
||
const handleSaveNews = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (editingNewsItem) {
|
||
const isNew = !newsItems.find(n => n.id === editingNewsItem.id);
|
||
if (isNew) {
|
||
await dataStore.addNews(editingNewsItem);
|
||
} else {
|
||
await dataStore.updateNews(editingNewsItem.id, editingNewsItem);
|
||
}
|
||
loadData();
|
||
setShowNewsModal(false);
|
||
setEditingNewsItem(null);
|
||
}
|
||
};
|
||
|
||
// Media edit handlers
|
||
const handleAddMedia = () => {
|
||
const newMedia: MediaItem = {
|
||
id: Math.max(...mediaItems.map(m => m.id), 0) + 1,
|
||
title: '',
|
||
description: '',
|
||
type: 'photo',
|
||
thumbnail: '',
|
||
date: new Date().toLocaleDateString('tr-TR'),
|
||
category: 'construction'
|
||
};
|
||
setEditingMediaItem(newMedia);
|
||
setShowMediaModal(true);
|
||
};
|
||
|
||
const handleEditMedia = (media: MediaItem) => {
|
||
setEditingMediaItem(media);
|
||
setShowMediaModal(true);
|
||
};
|
||
|
||
const handleSaveMedia = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (editingMediaItem) {
|
||
const isNew = !mediaItems.find(m => m.id === editingMediaItem.id);
|
||
if (isNew) {
|
||
await dataStore.addMedia(editingMediaItem);
|
||
} else {
|
||
await dataStore.updateMedia(editingMediaItem.id, editingMediaItem);
|
||
}
|
||
loadData();
|
||
setShowMediaModal(false);
|
||
setEditingMediaItem(null);
|
||
}
|
||
};
|
||
|
||
// Document edit handlers
|
||
const handleAddDocument = () => {
|
||
const newDoc: Document = {
|
||
id: Math.max(...documents.map(d => d.id), 0) + 1,
|
||
title: '',
|
||
description: '',
|
||
type: 'PDF',
|
||
size: '',
|
||
date: new Date().toLocaleDateString('tr-TR'),
|
||
category: 'teknik',
|
||
downloadUrl: '#'
|
||
};
|
||
setEditingDocument(newDoc);
|
||
setShowDocumentModal(true);
|
||
};
|
||
|
||
const handleEditDocument = (doc: Document) => {
|
||
setEditingDocument(doc);
|
||
setShowDocumentModal(true);
|
||
};
|
||
|
||
const handleSaveDocument = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (editingDocument) {
|
||
const isNew = !documents.find(d => d.id === editingDocument.id);
|
||
if (isNew) {
|
||
await dataStore.addDocument(editingDocument);
|
||
} else {
|
||
await dataStore.updateDocument(editingDocument.id, editingDocument);
|
||
}
|
||
loadData();
|
||
setShowDocumentModal(false);
|
||
setEditingDocument(null);
|
||
}
|
||
};
|
||
|
||
// Station edit handlers
|
||
const handleEditStation = (station: MetroStation) => {
|
||
setEditingStation(station);
|
||
setShowStationModal(true);
|
||
};
|
||
|
||
const handleSaveStation = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (editingStation) {
|
||
await dataStore.updateStation(editingStation.id, editingStation);
|
||
loadData();
|
||
setShowStationModal(false);
|
||
setEditingStation(null);
|
||
}
|
||
};
|
||
|
||
// Helper function for file icons
|
||
const getFileIcon = (type: string) => {
|
||
switch (type) {
|
||
case 'pdf': return '📄';
|
||
case 'doc': return '📝';
|
||
case 'xls': return '📊';
|
||
case 'image': return '🖼️';
|
||
default: return '📁';
|
||
}
|
||
};
|
||
|
||
// Helper functions for station status
|
||
const getStatusColor = (status: string) => {
|
||
switch (status) {
|
||
case 'completed': return 'bg-green-500';
|
||
case 'in-progress': return 'bg-yellow-500';
|
||
case 'planned': return 'bg-blue-500';
|
||
default: return 'bg-gray-500';
|
||
}
|
||
};
|
||
|
||
const getStatusText = (status: string) => {
|
||
switch (status) {
|
||
case 'completed': return 'Tamamlandı';
|
||
case 'in-progress': return 'Devam Ediyor';
|
||
case 'planned': return 'Planlandı';
|
||
default: return status;
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen bg-gray-50">
|
||
{/* Sidebar */}
|
||
<aside className={`fixed top-0 left-0 h-full bg-[#003366] text-white w-64 transform transition-transform duration-300 z-50 ${menuOpen ? 'translate-x-0' : '-translate-x-full'} lg:translate-x-0`}>
|
||
<div className="p-6 border-b border-[#004B87]">
|
||
<h1 className="text-2xl font-bold">A2 Metro</h1>
|
||
<p className="text-sm text-[#90E0EF] mt-1">Yönetim Paneli</p>
|
||
</div>
|
||
|
||
<nav className="p-4 space-y-2">
|
||
{menuItems.map((item) => (
|
||
<button
|
||
key={item.id}
|
||
onClick={() => {
|
||
setActiveSection(item.id);
|
||
setMenuOpen(false);
|
||
}}
|
||
className={`w-full flex items-center space-x-3 px-4 py-3 rounded-lg transition-all ${
|
||
activeSection === item.id
|
||
? 'bg-[#004B87] text-white shadow-lg'
|
||
: 'text-[#90E0EF] hover:bg-[#004B87]/50'
|
||
}`}
|
||
>
|
||
<span className="text-2xl">{item.icon}</span>
|
||
<span className="font-medium flex-1 text-left">{item.label}</span>
|
||
{item.badge && item.badge > 0 && (
|
||
<span className="px-2 py-1 bg-red-500 text-white text-xs rounded-full font-bold">
|
||
{item.badge}
|
||
</span>
|
||
)}
|
||
</button>
|
||
))}
|
||
</nav>
|
||
|
||
<div className="absolute bottom-0 left-0 right-0 p-4 border-t border-[#004B87]">
|
||
<button
|
||
onClick={handleLogout}
|
||
className="w-full flex items-center space-x-3 px-4 py-3 rounded-lg text-[#90E0EF] hover:bg-red-500/20 hover:text-red-300 transition-all"
|
||
>
|
||
<span className="text-2xl">🚪</span>
|
||
<span className="font-medium">Çıkış Yap</span>
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
|
||
{/* Main Content */}
|
||
<div className="lg:ml-64">
|
||
{/* Header */}
|
||
<header className="bg-white shadow-sm sticky top-0 z-40">
|
||
<div className="flex items-center justify-between px-4 py-4 lg:px-8">
|
||
<button
|
||
onClick={() => setMenuOpen(!menuOpen)}
|
||
className="lg:hidden text-[#003366] p-2"
|
||
>
|
||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
||
</svg>
|
||
</button>
|
||
|
||
<h2 className="text-xl font-bold text-[#003366]">
|
||
{menuItems.find(item => item.id === activeSection)?.label || 'Dashboard'}
|
||
</h2>
|
||
|
||
<div className="flex items-center space-x-4">
|
||
<button className="relative p-2 text-gray-600 hover:text-[#004B87] transition-colors">
|
||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||
</svg>
|
||
<span className="absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full"></span>
|
||
</button>
|
||
|
||
<div className="flex items-center space-x-2">
|
||
<div className="w-10 h-10 bg-linear-to-br from-[#004B87] to-[#00B4D8] rounded-full flex items-center justify-center">
|
||
<span className="text-white font-bold">A</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
{/* Dashboard Content */}
|
||
<main className="p-4 lg:p-8">
|
||
{activeSection === 'overview' && (
|
||
<>
|
||
{/* Stats Grid */}
|
||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6 mb-8">
|
||
{stats.map((stat, index) => (
|
||
<div key={index} className="bg-white rounded-xl shadow-sm p-6 hover:shadow-lg transition-shadow">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<div className={`w-12 h-12 bg-linear-to-br ${stat.color} rounded-lg flex items-center justify-center text-2xl`}>
|
||
{stat.icon}
|
||
</div>
|
||
<span className="text-green-500 text-sm font-semibold">{stat.change}</span>
|
||
</div>
|
||
<h3 className="text-gray-600 text-sm font-medium mb-1">{stat.title}</h3>
|
||
<p className="text-3xl font-bold text-[#003366]">{stat.value}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Charts Section */}
|
||
<div className="grid grid-cols-1 xl:grid-cols-2 gap-6 mb-8">
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<h3 className="text-lg font-bold text-[#003366] mb-4">İnşaat İlerleme Durumu</h3>
|
||
<div className="space-y-4">
|
||
{[
|
||
{ name: 'Temeller', progress: 100, color: 'bg-green-500' },
|
||
{ name: 'Yapı İnşaatı', progress: 75, color: 'bg-[#00B4D8]' },
|
||
{ name: 'Elektrik', progress: 60, color: 'bg-[#0096C7]' },
|
||
{ name: 'Ray Döşeme', progress: 40, color: 'bg-yellow-500' },
|
||
{ name: 'İç Mekan', progress: 25, color: 'bg-orange-500' },
|
||
].map((item, index) => (
|
||
<div key={index}>
|
||
<div className="flex justify-between mb-2">
|
||
<span className="text-sm font-medium text-gray-700">{item.name}</span>
|
||
<span className="text-sm font-semibold text-[#003366]">{item.progress}%</span>
|
||
</div>
|
||
<div className="w-full bg-gray-200 rounded-full h-2">
|
||
<div className={`${item.color} h-2 rounded-full transition-all duration-500`} style={{ width: `${item.progress}%` }}></div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<h3 className="text-lg font-bold text-[#003366] mb-4">Son Aktiviteler</h3>
|
||
<div className="space-y-4">
|
||
{[
|
||
{ action: 'Yeni haber eklendi', time: '5 dakika önce', icon: '📰', color: 'bg-blue-100 text-blue-600' },
|
||
{ action: 'Medya içeriği güncellendi', time: '1 saat önce', icon: '📸', color: 'bg-purple-100 text-purple-600' },
|
||
{ action: 'Belge yüklendi', time: '2 saat önce', icon: '📄', color: 'bg-green-100 text-green-600' },
|
||
{ action: 'İstasyon durumu güncellendi', time: '3 saat önce', icon: '🚇', color: 'bg-yellow-100 text-yellow-600' },
|
||
].map((activity, index) => (
|
||
<div key={index} className="flex items-start space-x-3">
|
||
<div className={`w-10 h-10 ${activity.color} rounded-lg flex items-center justify-center shrink-0`}>
|
||
{activity.icon}
|
||
</div>
|
||
<div className="flex-1 min-w-0">
|
||
<p className="text-sm font-medium text-gray-900">{activity.action}</p>
|
||
<p className="text-xs text-gray-500">{activity.time}</p>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Quick Actions */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<h3 className="text-lg font-bold text-[#003366] mb-4">Hızlı İşlemler</h3>
|
||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||
{[
|
||
{ label: 'Haber Ekle', icon: '➕', color: 'from-[#004B87] to-[#00B4D8]' },
|
||
{ label: 'Medya Yükle', icon: '📤', color: 'from-[#00B4D8] to-[#0096C7]' },
|
||
{ label: 'Belge Yükle', icon: '📁', color: 'from-[#0096C7] to-[#48CAE4]' },
|
||
{ label: 'Rapor Oluştur', icon: '📊', color: 'from-[#48CAE4] to-[#90E0EF]' },
|
||
].map((action, index) => (
|
||
<button
|
||
key={index}
|
||
className={`bg-linear-to-br ${action.color} text-white p-6 rounded-xl hover:shadow-lg transition-all duration-300 hover:scale-105`}
|
||
>
|
||
<div className="text-4xl mb-2">{action.icon}</div>
|
||
<div className="font-semibold text-sm">{action.label}</div>
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</>
|
||
)}
|
||
|
||
{/* Slider Management Section */}
|
||
{activeSection === 'slider' && (
|
||
<div className="space-y-6">
|
||
{/* Header */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6 flex items-center justify-between">
|
||
<div>
|
||
<h3 className="text-2xl font-bold text-[#003366]">Slider Yönetimi</h3>
|
||
<p className="text-gray-600 mt-1">Ana sayfa hero slider içeriklerini yönetin</p>
|
||
</div>
|
||
<button
|
||
onClick={handleAddSlide}
|
||
className="bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white px-6 py-3 rounded-lg font-semibold hover:shadow-lg transition-all flex items-center space-x-2"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||
</svg>
|
||
<span>Yeni Slide Ekle</span>
|
||
</button>
|
||
</div>
|
||
|
||
{/* Slider Items Grid */}
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
{sliderItems.map((slide, index) => (
|
||
<div key={slide.id} className="bg-white rounded-xl shadow-sm p-6 hover:shadow-lg transition-all">
|
||
<div className="flex items-start justify-between mb-4">
|
||
<div className="flex items-center space-x-3">
|
||
<div className="w-12 h-12 bg-linear-to-br from-[#004B87] to-[#00B4D8] rounded-lg flex items-center justify-center text-white font-bold text-lg">
|
||
{index + 1}
|
||
</div>
|
||
<div>
|
||
<h4 className="font-bold text-[#003366] text-sm">Slide #{slide.id}</h4>
|
||
<button
|
||
onClick={() => toggleSlideActive(slide.id)}
|
||
className={`text-xs px-3 py-1 rounded-full cursor-pointer transition-colors ${
|
||
slide.active
|
||
? 'bg-green-100 text-green-600 hover:bg-green-200'
|
||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
{slide.active ? '✓ Aktif - Tıkla Pasif Yap' : '○ Pasif - Tıkla Aktif Yap'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div className="flex space-x-2">
|
||
<button
|
||
onClick={() => handleEditSlide(slide)}
|
||
className="p-2 text-[#00B4D8] hover:bg-blue-50 rounded-lg transition-colors"
|
||
title="Düzenle"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
|
||
</svg>
|
||
</button>
|
||
<button
|
||
onClick={() => handleDeleteSlide(slide.id)}
|
||
className="p-2 text-red-500 hover:bg-red-50 rounded-lg transition-colors"
|
||
title="Sil"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-3">
|
||
<div>
|
||
<label className="text-xs font-semibold text-gray-500 uppercase">Başlık</label>
|
||
<p className="text-sm font-semibold text-[#003366] mt-1">{slide.title}</p>
|
||
</div>
|
||
<div>
|
||
<label className="text-xs font-semibold text-gray-500 uppercase">Açıklama</label>
|
||
<p className="text-sm text-gray-600 mt-1 line-clamp-2">{slide.description}</p>
|
||
</div>
|
||
<div className="flex items-center justify-between pt-3 border-t">
|
||
<div>
|
||
<label className="text-xs font-semibold text-gray-500 uppercase">Buton Metni</label>
|
||
<p className="text-sm font-medium text-[#00B4D8]">{slide.buttonText}</p>
|
||
</div>
|
||
<div className="flex items-center space-x-2">
|
||
<button className="text-xs px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded transition-colors">
|
||
Sırayı Değiştir
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Info Box */}
|
||
<div className="bg-blue-50 border border-blue-200 rounded-xl p-6">
|
||
<div className="flex items-start space-x-3">
|
||
<div className="text-blue-500 text-2xl">ℹ️</div>
|
||
<div>
|
||
<h4 className="font-bold text-blue-900 mb-2">Slider Kullanım Bilgisi</h4>
|
||
<ul className="text-sm text-blue-700 space-y-1">
|
||
<li>• Slider otomatik olarak 5 saniyede bir değişir</li>
|
||
<li>• Maksimum 4 slide önerilir (mevcut: {sliderItems.length}/4)</li>
|
||
<li>• Başlık 80 karakter, açıklama 200 karakter ile sınırlıdır</li>
|
||
<li>• Değişiklikler anında ana sayfaya yansır</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* News Management Section */}
|
||
{activeSection === 'news' && (
|
||
<div className="space-y-6">
|
||
{/* Header */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6 flex items-center justify-between flex-wrap gap-4">
|
||
<div>
|
||
<h3 className="text-2xl font-bold text-[#003366]">Haberler Yönetimi</h3>
|
||
<p className="text-gray-600 mt-1">Haberler ve duyuruları yönetin, düzenleyin</p>
|
||
</div>
|
||
<button
|
||
onClick={handleAddNews}
|
||
className="bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white px-6 py-3 rounded-lg font-semibold hover:shadow-lg transition-all flex items-center space-x-2"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||
</svg>
|
||
<span>Yeni Haber Ekle</span>
|
||
</button>
|
||
</div>
|
||
|
||
{/* Category Filter */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex flex-wrap gap-3">
|
||
{categories.map((category) => (
|
||
<button
|
||
key={category.id}
|
||
onClick={() => setSelectedNewsCategory(category.id)}
|
||
className={`px-4 py-2 rounded-lg font-semibold text-sm transition-all ${
|
||
selectedNewsCategory === category.id
|
||
? 'bg-[#00B4D8] text-white shadow-md'
|
||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
{category.icon} {category.name}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* News Table */}
|
||
<div className="bg-white rounded-xl shadow-sm overflow-hidden">
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full">
|
||
<thead className="bg-gray-50 border-b border-gray-200">
|
||
<tr>
|
||
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||
Haber
|
||
</th>
|
||
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||
Kategori
|
||
</th>
|
||
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||
Tarih
|
||
</th>
|
||
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||
Yazar
|
||
</th>
|
||
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||
Durum
|
||
</th>
|
||
<th className="px-6 py-4 text-right text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||
İşlemler
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody className="divide-y divide-gray-200">
|
||
{filteredNewsList.map((news) => (
|
||
<tr key={news.id} className="hover:bg-gray-50 transition-colors">
|
||
<td className="px-6 py-4">
|
||
<div className="flex items-center space-x-3">
|
||
<img
|
||
src={news.image}
|
||
alt={news.title}
|
||
className="w-16 h-16 rounded-lg object-cover"
|
||
/>
|
||
<div className="max-w-md">
|
||
<p className="font-semibold text-[#003366] line-clamp-1">{news.title}</p>
|
||
<p className="text-sm text-gray-500 line-clamp-1">{news.summary}</p>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td className="px-6 py-4">
|
||
<span className="px-3 py-1 text-xs font-semibold rounded-full bg-blue-100 text-blue-700">
|
||
{categories.find(c => c.id === news.category)?.name}
|
||
</span>
|
||
</td>
|
||
<td className="px-6 py-4 text-sm text-gray-600">
|
||
{news.date}
|
||
</td>
|
||
<td className="px-6 py-4 text-sm text-gray-600">
|
||
{news.author}
|
||
</td>
|
||
<td className="px-6 py-4">
|
||
{news.featured ? (
|
||
<span className="px-3 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-700">
|
||
⭐ Ana Sayfa
|
||
</span>
|
||
) : (
|
||
<span className="px-3 py-1 text-xs font-semibold rounded-full bg-gray-100 text-gray-600">
|
||
Yayında
|
||
</span>
|
||
)}
|
||
</td>
|
||
<td className="px-6 py-4 text-right">
|
||
<div className="flex items-center justify-end space-x-2">
|
||
<button
|
||
onClick={() => handleToggleFeatured(news.id)}
|
||
className={`p-2 rounded-lg transition-colors ${
|
||
news.featured
|
||
? 'text-yellow-500 bg-yellow-50 hover:bg-yellow-100'
|
||
: 'text-gray-400 hover:bg-gray-100'
|
||
}`}
|
||
title={news.featured ? 'Ana Sayfadan Kaldır' : 'Ana Sayfaya Ekle'}
|
||
>
|
||
<svg className="w-5 h-5" fill={news.featured ? 'currentColor' : 'none'} viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" />
|
||
</svg>
|
||
</button>
|
||
<button
|
||
onClick={() => handleEditNews(news)}
|
||
className="p-2 text-[#00B4D8] hover:bg-blue-50 rounded-lg transition-colors"
|
||
title="Düzenle"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
|
||
</svg>
|
||
</button>
|
||
<button
|
||
onClick={() => handleDeleteNews(news.id)}
|
||
className="p-2 text-red-500 hover:bg-red-50 rounded-lg transition-colors"
|
||
title="Sil"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Stats */}
|
||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-sm text-gray-600">Toplam Haber</p>
|
||
<p className="text-2xl font-bold text-[#003366] mt-1">{newsItems.length}</p>
|
||
</div>
|
||
<div className="text-4xl">📰</div>
|
||
</div>
|
||
</div>
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-sm text-gray-600">Ana Sayfada</p>
|
||
<p className="text-2xl font-bold text-[#003366] mt-1">{newsItems.filter(n => n.featured).length}</p>
|
||
</div>
|
||
<div className="text-4xl">⭐</div>
|
||
</div>
|
||
</div>
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-sm text-gray-600">İnşaat Haberleri</p>
|
||
<p className="text-2xl font-bold text-[#003366] mt-1">{newsItems.filter(n => n.category === 'construction').length}</p>
|
||
</div>
|
||
<div className="text-4xl">🏗️</div>
|
||
</div>
|
||
</div>
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-sm text-gray-600">Duyurular</p>
|
||
<p className="text-2xl font-bold text-[#003366] mt-1">{newsItems.filter(n => n.category === 'announcements').length}</p>
|
||
</div>
|
||
<div className="text-4xl">📢</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Media Management Section */}
|
||
{activeSection === 'media' && (
|
||
<div className="space-y-6">
|
||
{/* Header */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6 flex items-center justify-between flex-wrap gap-4">
|
||
<div>
|
||
<h3 className="text-2xl font-bold text-[#003366]">Medya Galerisi Yönetimi</h3>
|
||
<p className="text-gray-600 mt-1">Video ve fotoğraf içeriklerini yönetin</p>
|
||
</div>
|
||
<button
|
||
onClick={handleAddMedia}
|
||
className="bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white px-6 py-3 rounded-lg font-semibold hover:shadow-lg transition-all flex items-center space-x-2"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||
</svg>
|
||
<span>Yeni Medya Ekle</span>
|
||
</button>
|
||
</div>
|
||
|
||
{/* Type Filter */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex flex-wrap gap-3">
|
||
<button
|
||
onClick={() => setSelectedMediaType('all')}
|
||
className={`px-4 py-2 rounded-lg font-semibold text-sm transition-all ${
|
||
selectedMediaType === 'all'
|
||
? 'bg-[#00B4D8] text-white shadow-md'
|
||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
📁 Tümü
|
||
</button>
|
||
<button
|
||
onClick={() => setSelectedMediaType('video')}
|
||
className={`px-4 py-2 rounded-lg font-semibold text-sm transition-all ${
|
||
selectedMediaType === 'video'
|
||
? 'bg-[#00B4D8] text-white shadow-md'
|
||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
🎥 Videolar
|
||
</button>
|
||
<button
|
||
onClick={() => setSelectedMediaType('photo')}
|
||
className={`px-4 py-2 rounded-lg font-semibold text-sm transition-all ${
|
||
selectedMediaType === 'photo'
|
||
? 'bg-[#00B4D8] text-white shadow-md'
|
||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
📸 Fotoğraflar
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Media Grid */}
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||
{filteredMediaList.map((media) => (
|
||
<div key={media.id} className="bg-white rounded-xl shadow-sm overflow-hidden hover:shadow-lg transition-all">
|
||
<div className="relative h-48">
|
||
<img src={media.thumbnail} alt={media.title} className="w-full h-full object-cover" />
|
||
<div className="absolute top-3 left-3">
|
||
<span className={`px-3 py-1 rounded-full text-xs font-semibold ${
|
||
media.type === 'video' ? 'bg-red-500 text-white' : 'bg-blue-500 text-white'
|
||
}`}>
|
||
{media.type === 'video' ? '🎥 Video' : '📸 Foto'}
|
||
</span>
|
||
</div>
|
||
{media.duration && (
|
||
<div className="absolute bottom-3 right-3 bg-black/70 text-white px-2 py-1 rounded text-xs font-semibold">
|
||
{media.duration}
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div className="p-4">
|
||
<h4 className="font-bold text-[#003366] mb-2 line-clamp-2">{media.title}</h4>
|
||
<p className="text-sm text-gray-600 mb-3 line-clamp-2">{media.description}</p>
|
||
<div className="flex items-center justify-between">
|
||
<span className="text-xs text-gray-500">{media.date}</span>
|
||
<div className="flex space-x-2">
|
||
<button
|
||
onClick={() => handleEditMedia(media)}
|
||
className="p-2 text-[#00B4D8] hover:bg-blue-50 rounded-lg transition-colors"
|
||
title="Düzenle"
|
||
>
|
||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
|
||
</svg>
|
||
</button>
|
||
<button
|
||
onClick={() => handleDeleteMedia(media.id)}
|
||
className="p-2 text-red-500 hover:bg-red-50 rounded-lg transition-colors"
|
||
title="Sil"
|
||
>
|
||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Stats */}
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-sm text-gray-600">Toplam Medya</p>
|
||
<p className="text-2xl font-bold text-[#003366] mt-1">{mediaItems.length}</p>
|
||
</div>
|
||
<div className="text-4xl">📁</div>
|
||
</div>
|
||
</div>
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-sm text-gray-600">Videolar</p>
|
||
<p className="text-2xl font-bold text-[#003366] mt-1">{mediaItems.filter(m => m.type === 'video').length}</p>
|
||
</div>
|
||
<div className="text-4xl">🎥</div>
|
||
</div>
|
||
</div>
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-sm text-gray-600">Fotoğraflar</p>
|
||
<p className="text-2xl font-bold text-[#003366] mt-1">{mediaItems.filter(m => m.type === 'photo').length}</p>
|
||
</div>
|
||
<div className="text-4xl">📸</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Documents Management Section */}
|
||
{activeSection === 'documents' && (
|
||
<div className="space-y-6">
|
||
{/* Header */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6 flex items-center justify-between flex-wrap gap-4">
|
||
<div>
|
||
<h3 className="text-2xl font-bold text-[#003366]">Belgeler Yönetimi</h3>
|
||
<p className="text-gray-600 mt-1">Proje belgelerini ve dökümanları yönetin</p>
|
||
</div>
|
||
<button
|
||
onClick={handleAddDocument}
|
||
className="bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white px-6 py-3 rounded-lg font-semibold hover:shadow-lg transition-all flex items-center space-x-2"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||
</svg>
|
||
<span>Yeni Belge Ekle</span>
|
||
</button>
|
||
</div>
|
||
|
||
{/* Category Filter */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex flex-wrap gap-3">
|
||
{documentCategories.map((category) => (
|
||
<button
|
||
key={category.id}
|
||
onClick={() => setSelectedDocCategory(category.id)}
|
||
className={`px-4 py-2 rounded-lg font-semibold text-sm transition-all ${
|
||
selectedDocCategory === category.id
|
||
? 'bg-[#00B4D8] text-white shadow-md'
|
||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
{category.icon} {category.name}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Documents Table */}
|
||
<div className="bg-white rounded-xl shadow-sm overflow-hidden">
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full">
|
||
<thead className="bg-gray-50 border-b border-gray-200">
|
||
<tr>
|
||
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||
Belge
|
||
</th>
|
||
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||
Kategori
|
||
</th>
|
||
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||
Tarih
|
||
</th>
|
||
<th className="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||
Boyut
|
||
</th>
|
||
<th className="px-6 py-4 text-right text-xs font-semibold text-gray-600 uppercase tracking-wider">
|
||
İşlemler
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody className="divide-y divide-gray-200">
|
||
{filteredDocsList.map((doc) => (
|
||
<tr key={doc.id} className="hover:bg-gray-50 transition-colors">
|
||
<td className="px-6 py-4">
|
||
<div className="flex items-center space-x-3">
|
||
<div className="text-3xl">{getFileIcon(doc.type)}</div>
|
||
<div>
|
||
<p className="font-semibold text-[#003366]">{doc.title}</p>
|
||
<p className="text-sm text-gray-500 line-clamp-1">{doc.description}</p>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td className="px-6 py-4">
|
||
<span className="px-3 py-1 text-xs font-semibold rounded-full bg-purple-100 text-purple-700">
|
||
{documentCategories.find(c => c.id === doc.category)?.name}
|
||
</span>
|
||
</td>
|
||
<td className="px-6 py-4 text-sm text-gray-600">
|
||
{doc.date}
|
||
</td>
|
||
<td className="px-6 py-4 text-sm text-gray-600">
|
||
{doc.size}
|
||
</td>
|
||
<td className="px-6 py-4 text-right">
|
||
<div className="flex items-center justify-end space-x-2">
|
||
<button className="p-2 text-green-600 hover:bg-green-50 rounded-lg transition-colors" title="İndir">
|
||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||
</svg>
|
||
</button>
|
||
<button
|
||
onClick={() => handleEditDocument(doc)}
|
||
className="p-2 text-[#00B4D8] hover:bg-blue-50 rounded-lg transition-colors"
|
||
title="Düzenle"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
|
||
</svg>
|
||
</button>
|
||
<button
|
||
onClick={() => handleDeleteDocument(doc.id)}
|
||
className="p-2 text-red-500 hover:bg-red-50 rounded-lg transition-colors"
|
||
title="Sil"
|
||
>
|
||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Stats */}
|
||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex flex-col items-center text-center">
|
||
<div className="text-3xl mb-2">📋</div>
|
||
<p className="text-sm text-gray-600">Toplam</p>
|
||
<p className="text-xl font-bold text-[#003366]">{documents.length}</p>
|
||
</div>
|
||
</div>
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex flex-col items-center text-center">
|
||
<div className="text-3xl mb-2">📄</div>
|
||
<p className="text-sm text-gray-600">İhale</p>
|
||
<p className="text-xl font-bold text-[#003366]">{documents.filter(d => d.category === 'ihale').length}</p>
|
||
</div>
|
||
</div>
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex flex-col items-center text-center">
|
||
<div className="text-3xl mb-2">📐</div>
|
||
<p className="text-sm text-gray-600">Teknik</p>
|
||
<p className="text-xl font-bold text-[#003366]">{documents.filter(d => d.category === 'teknik').length}</p>
|
||
</div>
|
||
</div>
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex flex-col items-center text-center">
|
||
<div className="text-3xl mb-2">🌱</div>
|
||
<p className="text-sm text-gray-600">Çevresel</p>
|
||
<p className="text-xl font-bold text-[#003366]">{documents.filter(d => d.category === 'cevresel').length}</p>
|
||
</div>
|
||
</div>
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex flex-col items-center text-center">
|
||
<div className="text-3xl mb-2">📊</div>
|
||
<p className="text-sm text-gray-600">Raporlar</p>
|
||
<p className="text-xl font-bold text-[#003366]">{documents.filter(d => d.category === 'raporlar').length}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Metro Line Management Section */}
|
||
{activeSection === 'metro-line' && (
|
||
<div className="space-y-6">
|
||
{/* Header */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6 flex items-center justify-between flex-wrap gap-4">
|
||
<div>
|
||
<h3 className="text-2xl font-bold text-[#003366]">Metro Hattı İstasyonları</h3>
|
||
<p className="text-gray-600 mt-1">Hangi istasyonun inşaatta olduğunu seçin</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Basit İstasyon Listesi */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="space-y-3">
|
||
{metroStations.map((station, index) => {
|
||
const selectedIndex = selectedStationId
|
||
? metroStations.findIndex(s => s.id === selectedStationId)
|
||
: -1;
|
||
|
||
let status = 'planned';
|
||
if (selectedIndex !== -1) {
|
||
if (index < selectedIndex) status = 'completed';
|
||
else if (index === selectedIndex) status = 'inprogress';
|
||
}
|
||
|
||
return (
|
||
<div
|
||
key={station.id}
|
||
onClick={() => handleStationClick(station.id)}
|
||
className={`
|
||
flex items-center gap-4 p-4 rounded-xl cursor-pointer transition-all
|
||
${status === 'completed' ? 'bg-green-50 border-2 border-green-200' : ''}
|
||
${status === 'inprogress' ? 'bg-blue-50 border-2 border-blue-300 shadow-lg' : ''}
|
||
${status === 'planned' ? 'bg-gray-50 border-2 border-gray-200' : ''}
|
||
${selectedStationId === station.id ? 'ring-4 ring-blue-400' : ''}
|
||
hover:shadow-md
|
||
`}
|
||
>
|
||
{/* İkon */}
|
||
<div className="shrink-0">
|
||
{status === 'completed' && (
|
||
<div className="w-12 h-12 rounded-full bg-green-500 flex items-center justify-center text-white text-xl font-bold shadow-lg">
|
||
✓
|
||
</div>
|
||
)}
|
||
{status === 'inprogress' && (
|
||
<div className="w-12 h-12 rounded-full bg-blue-500 flex items-center justify-center text-white text-xl font-bold shadow-lg animate-pulse">
|
||
🚧
|
||
</div>
|
||
)}
|
||
{status === 'planned' && (
|
||
<div className="w-12 h-12 rounded-full bg-gray-300 flex items-center justify-center text-gray-600 text-xl font-bold shadow">
|
||
○
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Durak Bilgileri */}
|
||
<div className="flex-1 min-w-0">
|
||
<h3 className={`
|
||
text-lg font-bold mb-1 truncate
|
||
${status === 'completed' ? 'text-green-700' : ''}
|
||
${status === 'inprogress' ? 'text-blue-700' : ''}
|
||
${status === 'planned' ? 'text-gray-600' : ''}
|
||
`}>
|
||
{station.name}
|
||
</h3>
|
||
{station.district && (
|
||
<p className="text-sm text-gray-600">
|
||
📍 {station.district}
|
||
</p>
|
||
)}
|
||
</div>
|
||
|
||
{/* Durum Badge */}
|
||
<div className="shrink-0">
|
||
{status === 'completed' && (
|
||
<span className="px-3 py-1 bg-green-100 text-green-700 rounded-full text-sm font-semibold">
|
||
✓ Yapıldı
|
||
</span>
|
||
)}
|
||
{status === 'inprogress' && (
|
||
<span className="px-3 py-1 bg-blue-100 text-blue-700 rounded-full text-sm font-semibold animate-pulse">
|
||
🚧 Yapılıyor
|
||
</span>
|
||
)}
|
||
{status === 'planned' && (
|
||
<span className="px-3 py-1 bg-gray-100 text-gray-600 rounded-full text-sm font-semibold">
|
||
📋 Planlı
|
||
</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{/* Açıklama */}
|
||
<div className="mt-6 pt-6 border-t border-gray-200">
|
||
<div className="bg-blue-50 border-l-4 border-blue-500 p-4 rounded">
|
||
<p className="text-sm text-blue-800">
|
||
<strong>💡 Nasıl Çalışır:</strong> Şu an inşaatta olan istasyonu tıklayın.
|
||
Seçtiğiniz istasyondan öncekiler "Yapıldı", seçtiğiniz "Yapılıyor", sonrakiler "Planlı" olarak gösterilir.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Live Stream Management Section */}
|
||
{activeSection === 'live-stream' && liveStreamConfig && (
|
||
<div className="space-y-6">
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="mb-6">
|
||
<h3 className="text-2xl font-bold text-[#003366]">Canlı Yayın Yönetimi</h3>
|
||
<p className="text-gray-600 mt-1">YouTube canlı yayın URL'sini ve ayarlarını yönetin</p>
|
||
</div>
|
||
|
||
<form onSubmit={async (e) => {
|
||
e.preventDefault();
|
||
await dataStore.setLiveStream(liveStreamConfig);
|
||
alert('Canlı yayın ayarları kaydedildi!');
|
||
loadData();
|
||
}} className="space-y-6">
|
||
<div>
|
||
<label className="flex items-center space-x-2 mb-4">
|
||
<input
|
||
type="checkbox"
|
||
checked={liveStreamConfig.active}
|
||
onChange={(e) => setLiveStreamConfig({ ...liveStreamConfig, active: e.target.checked })}
|
||
className="w-5 h-5 text-[#00B4D8] border-gray-300 rounded focus:ring-[#00B4D8]"
|
||
/>
|
||
<span className="text-sm font-semibold text-gray-700">Canlı Yayın Aktif</span>
|
||
</label>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">
|
||
Yayın Başlığı
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={liveStreamConfig.title || ''}
|
||
onChange={(e) => setLiveStreamConfig({ ...liveStreamConfig, title: e.target.value })}
|
||
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="Canlı Yayın"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">
|
||
YouTube Embed URL *
|
||
</label>
|
||
<input
|
||
type="url"
|
||
value={liveStreamConfig.url}
|
||
onChange={(e) => setLiveStreamConfig({ ...liveStreamConfig, url: e.target.value })}
|
||
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="https://www.youtube.com/embed/VIDEO_ID"
|
||
required
|
||
/>
|
||
<p className="mt-2 text-sm text-gray-500">
|
||
YouTube video URL'sini embed formatında girin. Örnek: https://www.youtube.com/embed/VIDEO_ID
|
||
</p>
|
||
</div>
|
||
|
||
{/* Preview */}
|
||
{liveStreamConfig.url && (
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Önizleme</label>
|
||
<div className="relative w-full bg-gray-100 rounded-lg overflow-hidden" style={{paddingBottom: '56.25%'}}>
|
||
<iframe
|
||
className="absolute top-0 left-0 w-full h-full"
|
||
src={liveStreamConfig.url}
|
||
title="Önizleme"
|
||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||
allowFullScreen
|
||
></iframe>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="flex justify-end pt-4 border-t">
|
||
<button
|
||
type="submit"
|
||
className="px-6 py-3 bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white rounded-lg font-semibold hover:shadow-lg transition-all"
|
||
>
|
||
Değişiklikleri Kaydet
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Messages Section */}
|
||
{activeSection === 'messages' && (
|
||
<div className="space-y-6">
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between mb-6">
|
||
<div>
|
||
<h3 className="text-2xl font-bold text-[#003366]">Gelen Mesajlar</h3>
|
||
<p className="text-gray-600 mt-1">Kullanıcılardan gelen dilek, öneri ve şikayetler</p>
|
||
</div>
|
||
<div className="flex items-center space-x-2">
|
||
<span className="px-4 py-2 bg-red-100 text-red-700 rounded-lg font-semibold text-sm">
|
||
{messages.filter(m => !m.read).length} Okunmamış
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{messages.length === 0 ? (
|
||
<div className="text-center py-12">
|
||
<svg className="w-16 h-16 mx-auto text-gray-400 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" />
|
||
</svg>
|
||
<p className="text-gray-500">Henüz mesaj bulunmuyor</p>
|
||
</div>
|
||
) : (
|
||
<div className="space-y-4">
|
||
{messages.map((message) => (
|
||
<div
|
||
key={message.id}
|
||
className={`border rounded-lg p-4 transition-all ${
|
||
message.read ? 'bg-gray-50 border-gray-200' : 'bg-blue-50 border-blue-200'
|
||
}`}
|
||
>
|
||
<div className="flex items-start justify-between mb-3">
|
||
<div className="flex-1">
|
||
<div className="flex items-center space-x-3 mb-2">
|
||
<h4 className="font-bold text-[#003366]">{message.name}</h4>
|
||
<span className={`px-3 py-1 text-xs font-semibold rounded-full ${
|
||
message.type === 'sikayet' ? 'bg-red-100 text-red-700' :
|
||
message.type === 'oneri' ? 'bg-green-100 text-green-700' :
|
||
'bg-blue-100 text-blue-700'
|
||
}`}>
|
||
{message.type === 'sikayet' ? 'Şikayet' : message.type === 'oneri' ? 'Öneri' : 'Bilgi Talebi'}
|
||
</span>
|
||
{!message.read && (
|
||
<span className="px-2 py-1 bg-red-500 text-white text-xs rounded-full">Yeni</span>
|
||
)}
|
||
</div>
|
||
<p className="text-sm text-gray-600 mb-2">
|
||
📧 {message.email} • 📱 {message.phone}
|
||
</p>
|
||
<p className="text-sm font-semibold text-gray-700 mb-2">
|
||
Konu: {message.subject}
|
||
</p>
|
||
<p className="text-sm text-gray-700 bg-white p-3 rounded border">
|
||
{message.message}
|
||
</p>
|
||
<p className="text-xs text-gray-500 mt-2">
|
||
{new Date(message.date).toLocaleString('tr-TR')}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex space-x-2">
|
||
{!message.read && (
|
||
<button
|
||
onClick={() => {
|
||
dataStore.markMessageAsRead(message.id);
|
||
loadData();
|
||
}}
|
||
className="px-4 py-2 bg-[#00B4D8] text-white rounded-lg text-sm font-semibold hover:bg-[#004B87] transition-colors"
|
||
>
|
||
Okundu İşaretle
|
||
</button>
|
||
)}
|
||
<button
|
||
onClick={async () => {
|
||
if (confirm('Bu mesajı silmek istediğinizden emin misiniz?')) {
|
||
await dataStore.deleteMessage(message.id);
|
||
loadData();
|
||
}
|
||
}}
|
||
className="px-4 py-2 bg-red-500 text-white rounded-lg text-sm font-semibold hover:bg-red-600 transition-colors"
|
||
>
|
||
Sil
|
||
</button>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* FAQs Section */}
|
||
{activeSection === 'faqs' && (
|
||
<div className="space-y-6">
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between mb-6">
|
||
<h3 className="text-2xl font-bold text-[#003366]">SSS Yönetimi</h3>
|
||
<button
|
||
onClick={async () => {
|
||
await dataStore.addFAQ({
|
||
question: 'Yeni Soru',
|
||
answer: 'Cevap buraya yazılacak...',
|
||
order: faqs.length + 1
|
||
});
|
||
loadData();
|
||
}}
|
||
className="px-4 py-2 bg-[#00B4D8] text-white rounded-lg font-semibold hover:bg-[#0096C7] transition-colors flex items-center space-x-2"
|
||
>
|
||
<span>➕</span>
|
||
<span>Yeni SSS Ekle</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div className="space-y-4">
|
||
{faqs.map((faq) => (
|
||
<div key={faq.id} className="border border-gray-200 rounded-lg p-4">
|
||
<div className="space-y-3">
|
||
<div className="flex items-start justify-between gap-4">
|
||
<div className="flex-1">
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Soru</label>
|
||
<input
|
||
type="text"
|
||
value={faq.question}
|
||
onChange={async (e) => {
|
||
await dataStore.updateFAQ(faq.id, { question: e.target.value });
|
||
loadData();
|
||
}}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
/>
|
||
</div>
|
||
<div className="flex items-center space-x-2">
|
||
<input
|
||
type="number"
|
||
value={faq.order}
|
||
onChange={async (e) => {
|
||
await dataStore.updateFAQ(faq.id, { order: parseInt(e.target.value) });
|
||
loadData();
|
||
}}
|
||
className="w-16 px-2 py-2 border border-gray-300 rounded-lg text-center"
|
||
title="Sıra"
|
||
/>
|
||
<button
|
||
onClick={async () => {
|
||
if (confirm('Bu SSS silinsin mi?')) {
|
||
await dataStore.deleteFAQ(faq.id);
|
||
loadData();
|
||
}
|
||
}}
|
||
className="px-3 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors"
|
||
>
|
||
🗑️
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Cevap</label>
|
||
<textarea
|
||
value={faq.answer}
|
||
onChange={async (e) => {
|
||
await dataStore.updateFAQ(faq.id, { answer: e.target.value });
|
||
loadData();
|
||
}}
|
||
rows={3}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{faqs.length === 0 && (
|
||
<div className="text-center py-12 text-gray-500">
|
||
<p className="text-lg mb-2">Henüz SSS eklenmemiş</p>
|
||
<p className="text-sm">Yukarıdaki butonu kullanarak ilk SSS'nizi ekleyin</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Cameras Section */}
|
||
{activeSection === 'cameras' && (
|
||
<div className="space-y-6">
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between mb-6">
|
||
<h3 className="text-2xl font-bold text-[#003366]">Kamera Yönetimi</h3>
|
||
<button
|
||
onClick={async () => {
|
||
await dataStore.addCamera({
|
||
name: 'Yeni Kamera',
|
||
location: 'Konum',
|
||
videoUrl: 'https://www.youtube.com/embed/VIDEO_ID',
|
||
status: 'online',
|
||
viewers: 0,
|
||
order: cameras.length + 1
|
||
});
|
||
loadData();
|
||
}}
|
||
className="px-4 py-2 bg-[#00B4D8] text-white rounded-lg font-semibold hover:bg-[#0096C7] transition-colors flex items-center space-x-2"
|
||
>
|
||
<span>➕</span>
|
||
<span>Yeni Kamera Ekle</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
{cameras.map((camera) => (
|
||
<div key={camera.id} className="border border-gray-200 rounded-lg p-4">
|
||
<div className="space-y-3">
|
||
<div className="flex items-start justify-between gap-2">
|
||
<div className="flex-1">
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Kamera Adı</label>
|
||
<input
|
||
type="text"
|
||
value={camera.name}
|
||
onChange={async (e) => {
|
||
await dataStore.updateCamera(camera.id, { name: e.target.value });
|
||
loadData();
|
||
}}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
/>
|
||
</div>
|
||
<button
|
||
onClick={async () => {
|
||
if (confirm('Bu kamera silinsin mi?')) {
|
||
await dataStore.deleteCamera(camera.id);
|
||
loadData();
|
||
}
|
||
}}
|
||
className="px-3 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors"
|
||
>
|
||
🗑️
|
||
</button>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Konum</label>
|
||
<input
|
||
type="text"
|
||
value={camera.location}
|
||
onChange={async (e) => {
|
||
await dataStore.updateCamera(camera.id, { location: e.target.value });
|
||
loadData();
|
||
}}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Video URL (YouTube Embed)</label>
|
||
<input
|
||
type="text"
|
||
value={camera.videoUrl}
|
||
onChange={async (e) => {
|
||
await dataStore.updateCamera(camera.id, { videoUrl: e.target.value });
|
||
loadData();
|
||
}}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="https://www.youtube.com/embed/VIDEO_ID"
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-3 gap-2">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Durum</label>
|
||
<select
|
||
value={camera.status}
|
||
onChange={async (e) => {
|
||
await dataStore.updateCamera(camera.id, { status: e.target.value as 'online' | 'offline' });
|
||
loadData();
|
||
}}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
>
|
||
<option value="online">Online</option>
|
||
<option value="offline">Offline</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">İzleyici</label>
|
||
<input
|
||
type="number"
|
||
value={camera.viewers || 0}
|
||
onChange={async (e) => {
|
||
await dataStore.updateCamera(camera.id, { viewers: parseInt(e.target.value) || 0 });
|
||
loadData();
|
||
}}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Sıra</label>
|
||
<input
|
||
type="number"
|
||
value={camera.order}
|
||
onChange={async (e) => {
|
||
await dataStore.updateCamera(camera.id, { order: parseInt(e.target.value) });
|
||
loadData();
|
||
}}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{cameras.length === 0 && (
|
||
<div className="text-center py-12 text-gray-500">
|
||
<p className="text-lg mb-2">Henüz kamera eklenmemiş</p>
|
||
<p className="text-sm">Yukarıdaki butonu kullanarak ilk kameranızı ekleyin</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Site Settings Section */}
|
||
{activeSection === 'site-settings' && siteSettings && (
|
||
<div className="space-y-6">
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="mb-6">
|
||
<h3 className="text-2xl font-bold text-[#003366]">Site Ayarları</h3>
|
||
<p className="text-gray-600 mt-1">İletişim bilgileri ve sosyal medya bağlantılarını yönetin</p>
|
||
</div>
|
||
|
||
<form onSubmit={(e) => {
|
||
e.preventDefault();
|
||
dataStore.updateSiteSettings(siteSettings);
|
||
alert('Site ayarları kaydedildi!');
|
||
}} className="space-y-8">
|
||
|
||
{/* Company Info */}
|
||
<div className="border-b pb-6">
|
||
<h4 className="text-lg font-bold text-[#003366] mb-4">Şirket Bilgileri</h4>
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Kısa Ad</label>
|
||
<input
|
||
type="text"
|
||
value={siteSettings.company?.name || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
company: { ...siteSettings.company, name: e.target.value }
|
||
})}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Açıklama</label>
|
||
<input
|
||
type="text"
|
||
value={siteSettings.company?.description || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
company: { ...siteSettings.company, description: e.target.value }
|
||
})}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Logo URL</label>
|
||
<input
|
||
type="text"
|
||
value={siteSettings.company?.logo || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
company: { ...siteSettings.company, logo: e.target.value }
|
||
})}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Contact Info */}
|
||
<div className="border-b pb-6">
|
||
<h4 className="text-lg font-bold text-[#003366] mb-4">İletişim Bilgileri</h4>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Telefon</label>
|
||
<input
|
||
type="text"
|
||
value={siteSettings.contact?.phone || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
contact: { ...siteSettings.contact, phone: e.target.value }
|
||
})}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">E-posta</label>
|
||
<input
|
||
type="email"
|
||
value={siteSettings.contact?.email || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
contact: { ...siteSettings.contact, email: e.target.value }
|
||
})}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">KEP Adresi</label>
|
||
<input
|
||
type="text"
|
||
value={siteSettings.contact?.kep || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
contact: { ...siteSettings.contact, kep: e.target.value }
|
||
})}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
/>
|
||
</div>
|
||
<div className="md:col-span-2">
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Adres</label>
|
||
<textarea
|
||
value={siteSettings.contact?.address || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
contact: { ...siteSettings.contact, address: e.target.value }
|
||
})}
|
||
rows={3}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent resize-none text-gray-900 text-gray-900"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Social Media */}
|
||
<div>
|
||
<h4 className="text-lg font-bold text-[#003366] mb-4">Sosyal Medya</h4>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Facebook</label>
|
||
<input
|
||
type="url"
|
||
value={siteSettings.social?.facebook || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
social: { ...siteSettings.social, facebook: e.target.value }
|
||
})}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
placeholder="https://facebook.com/..."
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Twitter</label>
|
||
<input
|
||
type="url"
|
||
value={siteSettings.social?.twitter || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
social: { ...siteSettings.social, twitter: e.target.value }
|
||
})}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
placeholder="https://twitter.com/..."
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Instagram</label>
|
||
<input
|
||
type="url"
|
||
value={siteSettings.social?.instagram || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
social: { ...siteSettings.social, instagram: e.target.value }
|
||
})}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
placeholder="https://instagram.com/..."
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">YouTube</label>
|
||
<input
|
||
type="url"
|
||
value={siteSettings.social?.youtube || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
social: { ...siteSettings.social, youtube: e.target.value }
|
||
})}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
placeholder="https://youtube.com/..."
|
||
/>
|
||
</div>
|
||
<div className="md:col-span-2">
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">LinkedIn (Opsiyonel)</label>
|
||
<input
|
||
type="url"
|
||
value={siteSettings.social.linkedin || ''}
|
||
onChange={(e) => setSiteSettings({
|
||
...siteSettings,
|
||
social: { ...siteSettings.social, linkedin: e.target.value }
|
||
})}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900 text-gray-900"
|
||
placeholder="https://linkedin.com/..."
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex justify-end pt-4 border-t">
|
||
<button
|
||
type="submit"
|
||
className="px-6 py-3 bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white rounded-lg font-semibold hover:shadow-lg transition-all"
|
||
>
|
||
Değişiklikleri Kaydet
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{activeSection !== 'overview' && activeSection !== 'slider' && activeSection !== 'news' && activeSection !== 'media' && activeSection !== 'documents' && activeSection !== 'metro-line' && activeSection !== 'live-stream' && activeSection !== 'cameras' && activeSection !== 'faqs' && activeSection !== 'messages' && activeSection !== 'site-settings' && (
|
||
<div className="bg-white rounded-xl shadow-sm p-8 text-center">
|
||
<div className="text-6xl mb-4">
|
||
{menuItems.find(item => item.id === activeSection)?.icon}
|
||
</div>
|
||
<h3 className="text-2xl font-bold text-[#003366] mb-2">
|
||
{menuItems.find(item => item.id === activeSection)?.label}
|
||
</h3>
|
||
<p className="text-gray-600">Bu bölüm yakında geliştirilecek...</p>
|
||
</div>
|
||
)}
|
||
</main>
|
||
</div>
|
||
|
||
{/* Mobile Overlay */}
|
||
{menuOpen && (
|
||
<div
|
||
className="fixed inset-0 bg-black/50 z-40 lg:hidden"
|
||
onClick={() => setMenuOpen(false)}
|
||
></div>
|
||
)}
|
||
|
||
{/* Slider Edit Modal */}
|
||
{showSlideModal && editingSlide && (
|
||
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
|
||
<div className="bg-white rounded-xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
||
<div className="p-6 border-b border-gray-200">
|
||
<div className="flex items-center justify-between">
|
||
<h3 className="text-2xl font-bold text-[#003366]">
|
||
{editingSlide.id > Math.max(...sliderItems.map(s => s.id)) ? 'Yeni Slider Ekle' : 'Slider Düzenle'}
|
||
</h3>
|
||
<button
|
||
onClick={() => {
|
||
setShowSlideModal(false);
|
||
setEditingSlide(null);
|
||
}}
|
||
className="text-gray-400 hover:text-gray-600"
|
||
>
|
||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<form onSubmit={handleSaveSlide} className="p-6 space-y-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Başlık *</label>
|
||
<input
|
||
type="text"
|
||
value={editingSlide.title}
|
||
onChange={(e) => setEditingSlide({ ...editingSlide, title: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Açıklama *</label>
|
||
<textarea
|
||
value={editingSlide.description}
|
||
onChange={(e) => setEditingSlide({ ...editingSlide, description: e.target.value })}
|
||
rows={3}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Buton Metni</label>
|
||
<input
|
||
type="text"
|
||
value={editingSlide.buttonText}
|
||
onChange={(e) => setEditingSlide({ ...editingSlide, buttonText: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Buton Linki</label>
|
||
<input
|
||
type="text"
|
||
value={editingSlide.buttonLink}
|
||
onChange={(e) => setEditingSlide({ ...editingSlide, buttonLink: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="#"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center space-x-2">
|
||
<input
|
||
type="checkbox"
|
||
id="slideActive"
|
||
checked={editingSlide.active}
|
||
onChange={(e) => setEditingSlide({ ...editingSlide, active: e.target.checked })}
|
||
className="w-4 h-4 text-[#00B4D8] border-gray-300 rounded focus:ring-[#00B4D8]"
|
||
/>
|
||
<label htmlFor="slideActive" className="text-sm font-medium text-gray-700">
|
||
Slider'ı aktif yap (Ana sayfada göster)
|
||
</label>
|
||
</div>
|
||
|
||
<div className="flex justify-end space-x-3 pt-4 border-t">
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
setShowSlideModal(false);
|
||
setEditingSlide(null);
|
||
}}
|
||
className="px-6 py-2 border border-gray-300 rounded-lg font-semibold text-gray-700 hover:bg-gray-50 transition-all"
|
||
>
|
||
İptal
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="px-6 py-2 bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white rounded-lg font-semibold hover:shadow-lg transition-all"
|
||
>
|
||
Kaydet
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* News Edit Modal */}
|
||
{showNewsModal && editingNewsItem && (
|
||
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
|
||
<div className="bg-white rounded-xl shadow-2xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
|
||
<div className="p-6 border-b border-gray-200">
|
||
<div className="flex items-center justify-between">
|
||
<h3 className="text-2xl font-bold text-[#003366]">
|
||
{!newsItems.find(n => n.id === editingNewsItem.id) ? 'Yeni Haber Ekle' : 'Haber Düzenle'}
|
||
</h3>
|
||
<button
|
||
onClick={() => {
|
||
setShowNewsModal(false);
|
||
setEditingNewsItem(null);
|
||
}}
|
||
className="text-gray-400 hover:text-gray-600"
|
||
>
|
||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<form onSubmit={handleSaveNews} className="p-6 space-y-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Başlık *</label>
|
||
<input
|
||
type="text"
|
||
value={editingNewsItem.title}
|
||
onChange={(e) => setEditingNewsItem({ ...editingNewsItem, title: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Özet *</label>
|
||
<textarea
|
||
value={editingNewsItem.summary}
|
||
onChange={(e) => setEditingNewsItem({ ...editingNewsItem, summary: e.target.value })}
|
||
rows={2}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">İçerik *</label>
|
||
<textarea
|
||
value={editingNewsItem.content}
|
||
onChange={(e) => setEditingNewsItem({ ...editingNewsItem, content: e.target.value })}
|
||
rows={6}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Görsel URL *</label>
|
||
<input
|
||
type="url"
|
||
value={editingNewsItem.image}
|
||
onChange={(e) => setEditingNewsItem({ ...editingNewsItem, image: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="https://example.com/image.jpg"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Kategori *</label>
|
||
<select
|
||
value={editingNewsItem.category}
|
||
onChange={(e) => setEditingNewsItem({ ...editingNewsItem, category: e.target.value as 'construction' | 'announcements' | 'events' })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
>
|
||
<option value="construction">İnşaat</option>
|
||
<option value="announcements">Duyuru</option>
|
||
<option value="events">Etkinlik</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Yazar</label>
|
||
<input
|
||
type="text"
|
||
value={editingNewsItem.author || ''}
|
||
onChange={(e) => setEditingNewsItem({ ...editingNewsItem, author: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="Yazar adı"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center space-x-2">
|
||
<input
|
||
type="checkbox"
|
||
id="newsFeatured"
|
||
checked={editingNewsItem.featured || false}
|
||
onChange={(e) => setEditingNewsItem({ ...editingNewsItem, featured: e.target.checked })}
|
||
className="w-4 h-4 text-[#00B4D8] border-gray-300 rounded focus:ring-[#00B4D8]"
|
||
/>
|
||
<label htmlFor="newsFeatured" className="text-sm font-medium text-gray-700">
|
||
Öne çıkan haber (Ana sayfada göster)
|
||
</label>
|
||
</div>
|
||
|
||
<div className="flex justify-end space-x-3 pt-4 border-t">
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
setShowNewsModal(false);
|
||
setEditingNewsItem(null);
|
||
}}
|
||
className="px-6 py-2 border border-gray-300 rounded-lg font-semibold text-gray-700 hover:bg-gray-50 transition-all"
|
||
>
|
||
İptal
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="px-6 py-2 bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white rounded-lg font-semibold hover:shadow-lg transition-all"
|
||
>
|
||
Kaydet
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Media Edit Modal */}
|
||
{showMediaModal && editingMediaItem && (
|
||
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
|
||
<div className="bg-white rounded-xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
||
<div className="p-6 border-b border-gray-200">
|
||
<div className="flex items-center justify-between">
|
||
<h3 className="text-2xl font-bold text-[#003366]">
|
||
{!mediaItems.find(m => m.id === editingMediaItem.id) ? 'Yeni Medya Ekle' : 'Medya Düzenle'}
|
||
</h3>
|
||
<button
|
||
onClick={() => {
|
||
setShowMediaModal(false);
|
||
setEditingMediaItem(null);
|
||
}}
|
||
className="text-gray-400 hover:text-gray-600"
|
||
>
|
||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<form onSubmit={handleSaveMedia} className="p-6 space-y-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Başlık *</label>
|
||
<input
|
||
type="text"
|
||
value={editingMediaItem.title}
|
||
onChange={(e) => setEditingMediaItem({ ...editingMediaItem, title: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Açıklama *</label>
|
||
<textarea
|
||
value={editingMediaItem.description}
|
||
onChange={(e) => setEditingMediaItem({ ...editingMediaItem, description: e.target.value })}
|
||
rows={3}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Tip *</label>
|
||
<select
|
||
value={editingMediaItem.type}
|
||
onChange={(e) => setEditingMediaItem({ ...editingMediaItem, type: e.target.value as 'video' | 'photo' })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
>
|
||
<option value="photo">Fotoğraf</option>
|
||
<option value="video">Video</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Kategori</label>
|
||
<select
|
||
value={editingMediaItem.category}
|
||
onChange={(e) => setEditingMediaItem({ ...editingMediaItem, category: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
>
|
||
<option value="construction">İnşaat</option>
|
||
<option value="event">Etkinlik</option>
|
||
<option value="announcement">Duyuru</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">{editingMediaItem.type === 'video' ? 'Video URL *' : 'Thumbnail URL *'}</label>
|
||
<input
|
||
type="url"
|
||
value={editingMediaItem.type === 'video' ? editingMediaItem.videoUrl || '' : editingMediaItem.thumbnail}
|
||
onChange={(e) => setEditingMediaItem(editingMediaItem.type === 'video' ? { ...editingMediaItem, videoUrl: e.target.value } : { ...editingMediaItem, thumbnail: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder={editingMediaItem.type === 'video' ? "https://www.youtube.com/embed/VIDEO_ID" : "https://example.com/image.jpg"}
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Thumbnail URL</label>
|
||
<input
|
||
type="url"
|
||
value={editingMediaItem.thumbnail}
|
||
onChange={(e) => setEditingMediaItem({ ...editingMediaItem, thumbnail: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="https://example.com/thumbnail.jpg"
|
||
/>
|
||
</div>
|
||
|
||
<div className="flex justify-end space-x-3 pt-4 border-t">
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
setShowMediaModal(false);
|
||
setEditingMediaItem(null);
|
||
}}
|
||
className="px-6 py-2 border border-gray-300 rounded-lg font-semibold text-gray-700 hover:bg-gray-50 transition-all"
|
||
>
|
||
İptal
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="px-6 py-2 bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white rounded-lg font-semibold hover:shadow-lg transition-all"
|
||
>
|
||
Kaydet
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Document Edit Modal */}
|
||
{showDocumentModal && editingDocument && (
|
||
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
|
||
<div className="bg-white rounded-xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
||
<div className="p-6 border-b border-gray-200">
|
||
<div className="flex items-center justify-between">
|
||
<h3 className="text-2xl font-bold text-[#003366]">
|
||
{!documents.find(d => d.id === editingDocument.id) ? 'Yeni Belge Ekle' : 'Belge Düzenle'}
|
||
</h3>
|
||
<button
|
||
onClick={() => {
|
||
setShowDocumentModal(false);
|
||
setEditingDocument(null);
|
||
}}
|
||
className="text-gray-400 hover:text-gray-600"
|
||
>
|
||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<form onSubmit={handleSaveDocument} className="p-6 space-y-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Başlık *</label>
|
||
<input
|
||
type="text"
|
||
value={editingDocument.title}
|
||
onChange={(e) => setEditingDocument({ ...editingDocument, title: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Açıklama *</label>
|
||
<textarea
|
||
value={editingDocument.description}
|
||
onChange={(e) => setEditingDocument({ ...editingDocument, description: e.target.value })}
|
||
rows={3}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Dosya Tipi *</label>
|
||
<select
|
||
value={editingDocument.type}
|
||
onChange={(e) => setEditingDocument({ ...editingDocument, type: e.target.value as 'PDF' | 'DWG' | 'XLSX' | 'DOCX' })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
>
|
||
<option value="PDF">PDF</option>
|
||
<option value="DOCX">Word</option>
|
||
<option value="XLSX">Excel</option>
|
||
<option value="DWG">DWG</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Dosya Boyutu</label>
|
||
<input
|
||
type="text"
|
||
value={editingDocument.size}
|
||
onChange={(e) => setEditingDocument({ ...editingDocument, size: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="2.5 MB"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Kategori *</label>
|
||
<select
|
||
value={editingDocument.category}
|
||
onChange={(e) => setEditingDocument({ ...editingDocument, category: e.target.value as 'ihale' | 'teknik' | 'cevresel' | 'raporlar' | 'guvenlik' })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
>
|
||
<option value="ihale">İhale</option>
|
||
<option value="teknik">Teknik</option>
|
||
<option value="cevresel">Çevresel</option>
|
||
<option value="raporlar">Raporlar</option>
|
||
<option value="guvenlik">Güvenlik</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">İndirme Linki</label>
|
||
<input
|
||
type="text"
|
||
value={editingDocument.downloadUrl}
|
||
onChange={(e) => setEditingDocument({ ...editingDocument, downloadUrl: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="#"
|
||
/>
|
||
</div>
|
||
|
||
<div className="flex justify-end space-x-3 pt-4 border-t">
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
setShowDocumentModal(false);
|
||
setEditingDocument(null);
|
||
}}
|
||
className="px-6 py-2 border border-gray-300 rounded-lg font-semibold text-gray-700 hover:bg-gray-50 transition-all"
|
||
>
|
||
İptal
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="px-6 py-2 bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white rounded-lg font-semibold hover:shadow-lg transition-all"
|
||
>
|
||
Kaydet
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Station Edit Modal */}
|
||
{showStationModal && editingStation && (
|
||
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
|
||
<div className="bg-white rounded-xl shadow-2xl max-w-3xl w-full max-h-[90vh] overflow-y-auto">
|
||
<div className="p-6 border-b border-gray-200">
|
||
<div className="flex items-center justify-between">
|
||
<h3 className="text-2xl font-bold text-[#003366]">İstasyon Düzenle</h3>
|
||
<button
|
||
onClick={() => {
|
||
setShowStationModal(false);
|
||
setEditingStation(null);
|
||
}}
|
||
className="text-gray-400 hover:text-gray-600"
|
||
>
|
||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<form onSubmit={handleSaveStation} className="p-6 space-y-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">İstasyon Adı</label>
|
||
<input
|
||
type="text"
|
||
value={editingStation.name}
|
||
onChange={(e) => setEditingStation({ ...editingStation, name: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent bg-gray-50 text-gray-900"
|
||
readOnly
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Durum *</label>
|
||
<select
|
||
value={editingStation.status}
|
||
onChange={(e) => setEditingStation({ ...editingStation, status: e.target.value as MetroStation['status'] })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
>
|
||
<option value="completed">Tamamlandı</option>
|
||
<option value="in-progress">Devam Ediyor</option>
|
||
<option value="planned">Planlandı</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">İlerleme (%) *</label>
|
||
<input
|
||
type="number"
|
||
min="0"
|
||
max="100"
|
||
value={editingStation.progress}
|
||
onChange={(e) => setEditingStation({ ...editingStation, progress: parseInt(e.target.value) })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
required
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Başlangıç Tarihi</label>
|
||
<input
|
||
type="text"
|
||
value={editingStation.startDate}
|
||
onChange={(e) => setEditingStation({ ...editingStation, startDate: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="1 Ocak 2024"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Tahmini Bitiş</label>
|
||
<input
|
||
type="text"
|
||
value={editingStation.expectedCompletion}
|
||
onChange={(e) => setEditingStation({ ...editingStation, expectedCompletion: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="31 Aralık 2025"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Günlük Kapasite</label>
|
||
<input
|
||
type="text"
|
||
value={editingStation.dailyCapacity}
|
||
onChange={(e) => setEditingStation({ ...editingStation, dailyCapacity: e.target.value })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="50,000"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-gray-700 mb-2">Özellikler (virgülle ayırın)</label>
|
||
<input
|
||
type="text"
|
||
value={editingStation.features.join(', ')}
|
||
onChange={(e) => setEditingStation({ ...editingStation, features: e.target.value.split(',').map(f => f.trim()) })}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent text-gray-900"
|
||
placeholder="Otopark, Engelsiz Erişim, Modern Tasarım"
|
||
/>
|
||
</div>
|
||
|
||
<div className="flex justify-end space-x-3 pt-4 border-t">
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
setShowStationModal(false);
|
||
setEditingStation(null);
|
||
}}
|
||
className="px-6 py-2 border border-gray-300 rounded-lg font-semibold text-gray-700 hover:bg-gray-50 transition-all"
|
||
>
|
||
İptal
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="px-6 py-2 bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white rounded-lg font-semibold hover:shadow-lg transition-all"
|
||
>
|
||
Kaydet
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|