This commit is contained in:
Şahan Hasret
2025-11-20 16:50:28 +03:00
parent 08c426f97b
commit c0b7fb463e
15 changed files with 1395 additions and 646 deletions

View File

@@ -1,145 +1,40 @@
'use client';
import { useState } from 'react';
import { useState, useEffect } from 'react';
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { dataStore } from '@/lib/dataStore';
import type { Document } from '@/data/documents';
export default function Documents() {
const [documents, setDocuments] = useState<Document[]>([]);
const [selectedCategory, setSelectedCategory] = useState('all');
const categories = [
{ id: 'all', name: 'Tümü', icon: '📁' },
{ id: 'technical', name: 'Teknik Dokümanlar', icon: '📐' },
{ id: 'reports', name: 'Raporlar', icon: '📊' },
{ id: 'permissions', name: 'İzinler', icon: '✅' },
{ id: 'presentations', name: 'Sunumlar', icon: '📽️' },
];
useEffect(() => {
setDocuments(dataStore.getDocuments());
}, []);
const documents = [
{
id: 1,
category: 'technical',
title: 'A2 Metro Hattı Teknik Şartnamesi',
description: 'Proje kapsamındaki tüm teknik detaylar',
date: '15 Ekim 2025',
size: '12.5 MB',
type: 'PDF',
icon: '📐',
},
{
id: 2,
category: 'technical',
title: 'İstasyon Mimari Projesi',
description: 'İstasyon binalarının mimari tasarımı',
date: '12 Ekim 2025',
size: '25.8 MB',
type: 'PDF',
icon: '🏗️',
},
{
id: 3,
category: 'technical',
title: 'Güvenlik Planı ve Prosedürleri',
description: 'İş güvenliği planı ve acil durum prosedürleri',
date: '10 Ekim 2025',
size: '6.8 MB',
type: 'PDF',
icon: '🛡️',
},
{
id: 4,
category: 'technical',
title: 'Malzeme Spesifikasyonları',
description: 'İnşaatta kullanılacak malzeme detayları',
date: '8 Ekim 2025',
size: '9.2 MB',
type: 'PDF',
icon: '🔧',
},
{
id: 5,
category: 'reports',
title: 'ÇED Raporu',
description: 'Çevresel Etki Değerlendirme',
date: '5 Ekim 2025',
size: '8.3 MB',
type: 'PDF',
icon: '🌍',
},
{
id: 6,
category: 'reports',
title: 'Eylül 2025 İlerleme Raporu',
description: 'Aylık proje ilerleme durumu',
date: '1 Ekim 2025',
size: '3.2 MB',
type: 'PDF',
icon: '📈',
},
{
id: 7,
category: 'reports',
title: 'Ağustos 2025 İlerleme Raporu',
description: 'Aylık proje ilerleme durumu',
date: '1 Eylül 2025',
size: '3.1 MB',
type: 'PDF',
icon: '📈',
},
{
id: 8,
category: 'permissions',
title: 'İnşaat Ruhsatı',
description: 'Belediye onaylı inşaat ruhsatı belgesi',
date: '5 Eylül 2025',
size: '1.5 MB',
type: 'PDF',
icon: '✅',
},
{
id: 9,
category: 'permissions',
title: 'Kamulaştırma İzin Belgeleri',
description: 'Proje alanı kamulaştırma işlem belgeleri',
date: '28 Ağustos 2025',
size: '4.7 MB',
type: 'PDF',
icon: '📋',
},
{
id: 10,
category: 'permissions',
title: 'Çalışma İzin Belgeleri',
description: 'İlgili kurumlardan alınan çalışma izinleri',
date: '15 Ağustos 2025',
size: '2.8 MB',
type: 'PDF',
icon: '📄',
},
{
id: 11,
category: 'presentations',
title: 'A2 Metro Hattı Tanıtım Sunumu',
description: 'Genel tanıtım ve proje özeti sunumu',
date: '20 Eylül 2025',
size: '15.6 MB',
type: 'PPTX',
icon: '📽️',
},
{
id: 12,
category: 'presentations',
title: 'Teknik Altyapı Sunumu',
description: 'Tünel ve istasyon altyapı detayları',
date: '15 Eylül 2025',
size: '22.4 MB',
type: 'PPTX',
icon: '🎯',
},
const categories = [
{ id: 'all', name: 'Tümü', icon: '📋' },
{ id: 'ihale', name: 'İhale Belgeleri', icon: '📄' },
{ id: 'teknik', name: 'Teknik Dökümanlar', icon: '📐' },
{ id: 'cevresel', name: 'Çevresel Etki', icon: '🌱' },
{ id: 'raporlar', name: 'İlerleme Raporları', icon: '📊' },
{ id: 'guvenlik', name: 'Güvenlik', icon: '🛡️' },
];
const filteredDocs = selectedCategory === 'all' ? documents : documents.filter(d => d.category === selectedCategory);
const getFileIcon = (type: string) => {
switch (type) {
case 'PDF': return '📕';
case 'DWG': return '📐';
case 'XLSX': return '📊';
case 'DOCX': return '📝';
default: return '📄';
}
};
return (
<div className="min-h-screen bg-[#003366]">
<Header />
@@ -169,7 +64,7 @@ export default function Documents() {
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredDocs.map((doc) => (
<div key={doc.id} className="bg-white rounded-2xl p-6">
<div className="text-4xl mb-4">{doc.icon}</div>
<div className="text-4xl mb-4">{getFileIcon(doc.type)}</div>
<h3 className="text-xl font-bold text-[#004B87] mb-2">{doc.title}</h3>
<p className="text-gray-600 text-sm mb-4">{doc.description}</p>
<div className="text-xs text-gray-500 mb-4">

View File

@@ -1,50 +1,41 @@
'use client';
import { useState } from 'react';
import { useState, useEffect } from 'react';
import Image from 'next/image';
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { dataStore, type Camera } from '@/lib/dataStore';
export default function LiveStream() {
const [selectedCamera, setSelectedCamera] = useState(1);
const [cameras, setCameras] = useState<Camera[]>([]);
const cameras = [
{
id: 1,
name: 'Dikimevi İstasyonu - Ana Giriş',
location: 'Dikimevi',
videoUrl: 'https://www.youtube.com/embed/b9q88QDEcKg?autoplay=1',
status: 'online',
viewers: 1243
},
{
id: 2,
name: 'Tuzluçayır İstasyonu - İnşaat Sahası',
location: 'Tuzluçayır',
videoUrl: 'https://www.youtube.com/embed/b9q88QDEcKg?autoplay=1',
status: 'online',
viewers: 856
},
{
id: 3,
name: 'A2 Metro Hattı - Tünel Kazı Çalışması',
location: 'Mamak',
videoUrl: 'https://www.youtube.com/embed/b9q88QDEcKg?autoplay=1',
status: 'online',
viewers: 2134
},
{
id: 4,
name: 'İstasyon Binası İç Mekan',
location: 'Dikimevi',
videoUrl: 'https://www.youtube.com/embed/b9q88QDEcKg?autoplay=1',
status: 'online',
viewers: 534
useEffect(() => {
const loadedCameras = dataStore.getCameras()
.filter(cam => cam.status === 'online')
.sort((a, b) => a.order - b.order);
setCameras(loadedCameras);
if (loadedCameras.length > 0) {
setSelectedCamera(loadedCameras[0].id);
}
];
}, []);
const selectedCam = cameras.find(cam => cam.id === selectedCamera) || cameras[0];
if (!selectedCam) {
return (
<div className="min-h-screen bg-[#003366]">
<Header />
<main className="pt-32 pb-16">
<div className="max-w-7xl mx-auto px-4 text-center">
<p className="text-white text-xl">Şu anda aktif kamera bulunmamaktadır.</p>
</div>
</main>
<Footer />
</div>
);
}
return (
<div className="min-h-screen bg-[#003366]">
<Header />
@@ -92,13 +83,15 @@ export default function LiveStream() {
{selectedCam.name}
</h2>
<div className="flex items-center space-x-4 text-sm text-gray-600">
<div className="flex items-center space-x-1">
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
<path fillRule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clipRule="evenodd" />
</svg>
<span>{selectedCam.viewers.toLocaleString('tr-TR')} izleyici</span>
</div>
{selectedCam.viewers && (
<div className="flex items-center space-x-1">
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
<path fillRule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clipRule="evenodd" />
</svg>
<span>{selectedCam.viewers.toLocaleString('tr-TR')} izleyici</span>
</div>
)}
<div className="flex items-center space-x-1">
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clipRule="evenodd" />

View File

@@ -1,15 +1,21 @@
'use client';
import { useState } from 'react';
import { useState, useEffect } from 'react';
import Image from 'next/image';
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { newsData, categories } from '@/data/news';
import { dataStore } from '@/lib/dataStore';
import { categories, type NewsItem } from '@/data/news';
export default function News() {
const [newsData, setNewsData] = useState<NewsItem[]>([]);
const [selectedCategory, setSelectedCategory] = useState('all');
const [selectedNews, setSelectedNews] = useState<number | null>(null);
useEffect(() => {
setNewsData(dataStore.getNews());
}, []);
const filteredNews = selectedCategory === 'all'
? newsData
: newsData.filter(item => item.category === selectedCategory);

View File

@@ -1,9 +1,19 @@
'use client';
import { useState, useEffect } from 'react';
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { dataStore, type SiteSettings } from '@/lib/dataStore';
export default function Contact() {
const [settings, setSettings] = useState<SiteSettings | null>(null);
useEffect(() => {
setSettings(dataStore.getSiteSettings());
}, []);
if (!settings) return null;
return (
<div className="min-h-screen bg-[#003366]">
<Header />
@@ -35,8 +45,7 @@ export default function Contact() {
<div className="flex-1">
<h3 className="text-lg font-bold text-[#004B87] mb-2">ADRES</h3>
<p className="text-gray-700 leading-relaxed">
Emniyet Mah. Hipodrom Caddesi No: 5<br />
Yenimahalle / Ankara
{settings.contact.address}
</p>
</div>
</div>
@@ -53,10 +62,10 @@ export default function Contact() {
<div className="flex-1">
<h3 className="text-lg font-bold text-[#004B87] mb-2">KEP ADRESİ</h3>
<a
href="mailto:ankarabuyuksehirbelediyesi@hs01.kep.tr"
href={`mailto:${settings.contact.kep}`}
className="text-gray-700 hover:text-[#00B4D8] transition-colors break-all"
>
ankarabuyuksehirbelediyesi@hs01.kep.tr
{settings.contact.kep}
</a>
</div>
</div>
@@ -73,10 +82,10 @@ export default function Contact() {
<div className="flex-1">
<h3 className="text-lg font-bold text-[#004B87] mb-2">TELEFON</h3>
<a
href="tel:+903125071000"
href={`tel:${settings.contact.phone.replace(/\s/g, '')}`}
className="text-xl font-semibold text-gray-700 hover:text-[#00B4D8] transition-colors"
>
+90 (312) 507 10 00
{settings.contact.phone}
</a>
</div>
</div>

View File

@@ -1,86 +1,20 @@
'use client';
import { useState } from 'react';
import { useState, useEffect } from 'react';
import Image from 'next/image';
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { dataStore } from '@/lib/dataStore';
import type { MediaItem } from '@/data/media';
export default function MediaGallery() {
const [mediaItems, setMediaItems] = useState<MediaItem[]>([]);
const [selectedTab, setSelectedTab] = useState<'all' | 'video' | 'photo'>('all');
const [selectedMedia, setSelectedMedia] = useState<number | null>(null);
const mediaItems = [
{
id: 1,
type: 'video',
title: 'A2 Metro Hattı Genel Tanıtım',
thumbnail: 'https://images.pexels.com/photos/17152223/pexels-photo-17152223.jpeg',
videoUrl: 'https://www.youtube.com/embed/b9q88QDEcKg',
date: '15 Ekim 2025',
duration: '5:32',
description: 'A2 Metro Hattı projesinin genel tanıtımı ve istasyonların detayları'
},
{
id: 2,
type: 'photo',
title: 'Dikimevi İstasyonu İnşaat Çalışmaları',
thumbnail: 'https://images.pexels.com/photos/17302615/pexels-photo-17302615.jpeg',
date: '12 Ekim 2025',
description: 'Dikimevi metro istasyonunda devam eden kazı ve inşaat çalışmaları'
},
{
id: 3,
type: 'photo',
title: 'Tuzluçayır İstasyonu Temel Atma',
thumbnail: 'https://images.pexels.com/photos/33950678/pexels-photo-33950678.jpeg',
date: '10 Ekim 2025',
description: 'Tuzluçayır istasyonunun temel atma töreni anları'
},
{
id: 4,
type: 'video',
title: 'Metro İnşaatı İlerleme Raporu',
thumbnail: 'https://images.pexels.com/photos/253647/pexels-photo-253647.jpeg',
videoUrl: 'https://www.youtube.com/embed/b9q88QDEcKg',
date: '8 Ekim 2025',
duration: '8:15',
description: 'Ekim ayı metro inşaatı ilerleme raporu ve gelecek hedefler'
},
{
id: 5,
type: 'photo',
title: 'Modern İstasyon Tasarımları',
thumbnail: 'https://images.pexels.com/photos/17152223/pexels-photo-17152223.jpeg',
date: '5 Ekim 2025',
description: 'Yeni nesil metro istasyonlarının modern iç mekan tasarımları'
},
{
id: 6,
type: 'video',
title: 'Çevre Dostu Metro Projesi',
thumbnail: 'https://images.pexels.com/photos/17302615/pexels-photo-17302615.jpeg',
videoUrl: 'https://www.youtube.com/embed/b9q88QDEcKg',
date: '1 Ekim 2025',
duration: '6:45',
description: 'Metro projesinde kullanılan çevre dostu teknolojiler ve sürdürülebilir yaklaşımlar'
},
{
id: 7,
type: 'photo',
title: 'İşçi Güvenliği Eğitimi',
thumbnail: 'https://images.pexels.com/photos/33950678/pexels-photo-33950678.jpeg',
date: '28 Eylül 2025',
description: 'İnşaat sahalarında iş güvenliği eğitimleri'
},
{
id: 8,
type: 'photo',
title: 'Ray Döşeme Çalışmaları',
thumbnail: 'https://images.pexels.com/photos/253647/pexels-photo-253647.jpeg',
date: '25 Eylül 2025',
description: 'Metro hattında ray döşeme işlemlerinin başlaması'
},
];
useEffect(() => {
setMediaItems(dataStore.getMedia());
}, []);
const filteredMedia = selectedTab === 'all'
? mediaItems

View File

@@ -1,67 +0,0 @@
'use client';
import { useState, useEffect } from 'react';
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import HeroSlider from "@/components/HeroSlider";
import QuickMenuCards from "@/components/QuickMenuCards";
import LiveStreamSection from "@/components/LiveStreamSection";
import NewsSection from "@/components/NewsSection";
import MetroLine from "@/components/MetroLine";
export default function Home() {
const [showLiveStream, setShowLiveStream] = useState(false);
const [showNews, setShowNews] = useState(false);
const [showDocuments, setShowDocuments] = useState(false);
const [showMediaGallery, setShowMediaGallery] = useState(false);
const [showComplaintForm, setShowComplaintForm] = useState(false);
const [showContact, setShowContact] = useState(false);
// Modal açıldığında yukarı kaydır
useEffect(() => {
if (showLiveStream || showNews || showDocuments || showMediaGallery || showComplaintForm || showContact) {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
}, [showLiveStream, showNews, showDocuments, showMediaGallery, showComplaintForm, showContact]);
return (
<div className="min-h-screen bg-[#003366]">
<Header />
{/* Hero Slider Section */}
<HeroSlider />
{/* Quick Menu Cards */}
<QuickMenuCards
onLiveStreamClick={() => setShowLiveStream(!showLiveStream)}
onNewsClick={() => setShowNews(!showNews)}
onDocumentsClick={() => setShowDocuments(!showDocuments)}
onMediaClick={() => setShowMediaGallery(!showMediaGallery)}
onComplaintClick={() => setShowComplaintForm(!showComplaintForm)}
onContactClick={() => setShowContact(!showContact)}
/>
{/* Live Stream Section */}
<LiveStreamSection
show={showLiveStream}
onClose={() => setShowLiveStream(false)}
/>
{/* News Section */}
<NewsSection
show={showNews}
onClose={() => setShowNews(false)}
showLiveStream={showLiveStream}
/>
{/* Metro Line Section - Ana içerik */}
<main className={`${showLiveStream || showNews || showDocuments || showMediaGallery || showComplaintForm || showContact ? 'pt-8' : 'pt-8 md:pt-64'} pb-16`}>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<MetroLine />
</div>
</main>
<Footer />
</div>
);
}

View File

@@ -25,11 +25,13 @@ export default function Home() {
// Gerçek veriler için state
const [heroSlides, setHeroSlides] = useState(dataStore.getSlider());
const [newsData, setNewsData] = useState(dataStore.getNews());
const [liveStreamConfig, setLiveStreamConfig] = useState(dataStore.getLiveStream());
// Verileri yükle
useEffect(() => {
setHeroSlides(dataStore.getSlider());
setNewsData(dataStore.getNews());
setLiveStreamConfig(dataStore.getLiveStream());
}, []);
// Modal açıldığında yukarı kaydır - KALDIRILDI (kullanıcı deneyimi için)
@@ -290,13 +292,13 @@ export default function Home() {
</div>
{/* Canlı Yayın Video Bölümü */}
{showLiveStream && (
{showLiveStream && liveStreamConfig.active && (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-8 md:pt-32 pb-8">
<div className="bg-white rounded-2xl shadow-2xl p-6 lg:p-8">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center space-x-3">
<div className="w-3 h-3 bg-red-500 rounded-full animate-pulse"></div>
<h2 className="text-2xl font-bold text-[#004B87]">Canlı Yayın</h2>
<h2 className="text-2xl font-bold text-[#004B87]">{liveStreamConfig.title || 'Canlı Yayın'}</h2>
</div>
<button
onClick={() => setShowLiveStream(false)}
@@ -312,8 +314,8 @@ export default function Home() {
<div className="relative w-full" style={{paddingBottom: '56.25%'}}>
<iframe
className="absolute top-0 left-0 w-full h-full rounded-lg"
src="https://www.youtube.com/embed/b9q88QDEcKg?autoplay=1"
title="Canlı Yayın"
src={liveStreamConfig.url}
title={liveStreamConfig.title || 'Canlı Yayın'}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>

View File

@@ -1,65 +1,18 @@
'use client';
import { useState } from 'react';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { dataStore, type FAQ } from '@/lib/dataStore';
export default function FAQ() {
export default function FAQPage() {
const [openFAQ, setOpenFAQ] = useState<number | null>(null);
const [faqs, setFaqs] = useState<FAQ[]>([]);
const faqs = [
{
id: 1,
question: 'A2 Metro Hattı projesi ne zaman tamamlanacak?',
answer: 'A2 Metro Hattı projesinin 2026 yılı sonunda tamamlanması planlanmaktadır. Proje, Dikimevi-Natoyolu güzergâhında 6.5 kilometre uzunluğunda ve 5 istasyonlu bir metro hattı inşasını kapsamaktadır.'
},
{
id: 2,
question: 'Metro hattı hangi istasyonları kapsayacak?',
answer: 'A2 Metro Hattı aşağıdaki istasyonları içerecektir: Dikimevi İstasyonu, Tuzluçayır İstasyonu, Natoyolu İstasyonu ve diğer ara istasyonlar. Toplam 5 istasyon bulunacaktır.'
},
{
id: 3,
question: 'İnşaat çalışmaları sırasında trafik nasıl etkilenecek?',
answer: 'İnşaat çalışmaları sırasında geçici trafik düzenlemeleri uygulanacaktır. Alternatif güzergâhlar belirlenecek ve yönlendirme levhaları yerleştirilecektir. Vatandaşlarımızın anlayışına sığınıyoruz.'
},
{
id: 4,
question: 'Proje maliyeti ne kadar?',
answer: 'A2 Metro Hattı projesinin toplam maliyeti yaklaşık 2.5 milyar TL olarak belirlenmiştir. Bu maliyet, tünel kazısı, istasyon inşaatı, ray döşeme ve elektrik-elektronik sistemleri kapsamaktadır.'
},
{
id: 5,
question: 'Çevreye etkisi ne olacak?',
answer: 'Proje, çevre dostu teknolojiler kullanılarak gerçekleştirilmektedir. Çevresel Etki Değerlendirme (ÇED) raporu hazırlanmış ve gerekli izinler alınmıştır. İnşaat sırasında toz kontrolü ve gürültü önleme tedbirleri uygulanacaktır.'
},
{
id: 6,
question: 'İstasyonlar hangi özelliklere sahip olacak?',
answer: 'İstasyonlar modern mimari tasarımla, enerji verimliliği, erişilebilirlik ve güvenlik ön planda tutularak inşa edilecektir. Güneş enerjisi panelleri, LED aydınlatma ve akıllı havalandırma sistemleri kullanılacaktır.'
},
{
id: 7,
question: 'İnşaatta hangi teknolojiler kullanılacak?',
answer: 'Proje kapsamında TBM (Tunnel Boring Machine) tünel açma makinesi kullanılacaktır. Bu teknoloji sayesinde kazı çalışmaları daha hızlı ve güvenli şekilde gerçekleştirilecektir.'
},
{
id: 8,
question: 'Proje ilerleme durumu nasıl takip edilebilir?',
answer: 'Bu web sitesi üzerinden canlı yayın, haberler ve medya galerisi bölümlerinden proje ilerleme durumunu takip edebilirsiniz. Ayrıca aylık ilerleme raporları yayınlanmaktadır.'
},
{
id: 9,
question: 'İş güvenliği nasıl sağlanacak?',
answer: 'İnşaat alanında uluslararası standartlarda iş güvenliği tedbirleri uygulanmaktadır. Düzenli güvenlik eğitimleri verilir ve acil durum prosedürleri hazırlanmıştır.'
},
{
id: 10,
question: 'Vatandaşlar proje hakkında nasıl bilgi alabilir?',
answer: 'Bu web sitesi, sosyal medya hesapları ve tanıtım etkinlikleri aracılığıyla proje hakkında detaylı bilgi alınabilir. Ayrıca iletişim bölümünden sorularınızı iletebilirsiniz.'
}
];
useEffect(() => {
setFaqs(dataStore.getFAQs().sort((a, b) => a.order - b.order));
}, []);
const toggleFAQ = (id: number) => {
setOpenFAQ(openFAQ === id ? null : id);

View File

@@ -2,7 +2,7 @@
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { dataStore, type SliderItem } from '@/lib/dataStore';
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';
@@ -19,6 +19,11 @@ export default function Dashboard() {
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');
@@ -37,6 +42,11 @@ export default function Dashboard() {
setMediaItems(dataStore.getMedia());
setDocuments(dataStore.getDocuments());
setMetroStations(dataStore.getMetroStations());
setLiveStreamConfig(dataStore.getLiveStream());
setMessages(dataStore.getMessages());
setSiteSettings(dataStore.getSiteSettings());
setFaqs(dataStore.getFAQs().sort((a, b) => a.order - b.order));
setCameras(dataStore.getCameras().sort((a, b) => a.order - b.order));
};
const handleLogout = () => {
@@ -48,7 +58,7 @@ export default function Dashboard() {
{ 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: 'Metro İstasyonları', value: metroStations.length.toString(), icon: '🚇', color: 'from-[#48CAE4] to-[#90E0EF]', change: '+2' },
{ 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 = [
@@ -58,7 +68,11 @@ export default function Dashboard() {
{ id: 'media', label: 'Medya', icon: '📸' },
{ id: 'documents', label: 'Belgeler', icon: '📄' },
{ id: 'metro-line', label: 'Metro Hattı', icon: '🚇' },
{ id: 'settings', label: 'Ayarlar', 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
@@ -110,11 +124,11 @@ export default function Dashboard() {
}
};
// Metro station update function
const handleUpdateStation = (id: number, updates: Partial<MetroStation>) => {
dataStore.updateStation(id, updates);
loadData();
};
// Metro station update function (used in modal)
// const handleUpdateStation = (id: number, updates: Partial<MetroStation>) => {
// dataStore.updateStation(id, updates);
// loadData();
// };
// Categories
const categories = [
@@ -134,8 +148,8 @@ export default function Dashboard() {
// Form states
const [selectedNewsCategory, setSelectedNewsCategory] = useState('all');
const [editingNews, setEditingNews] = useState<NewsItem | null>(null);
const [showNewsForm, setShowNewsForm] = useState(false);
// 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');
@@ -215,7 +229,6 @@ export default function Dashboard() {
description: '',
buttonText: 'Detaylı Bilgi',
buttonLink: '#',
image: '',
active: false
};
setEditingSlide(newSlide);
@@ -239,7 +252,9 @@ export default function Dashboard() {
content: '',
date: new Date().toLocaleDateString('tr-TR'),
image: '',
category: 'announcement',
category: 'announcements',
author: 'Admin',
tags: [],
featured: false
};
setEditingNewsItem(newNews);
@@ -273,7 +288,6 @@ export default function Dashboard() {
title: '',
description: '',
type: 'photo',
url: '',
thumbnail: '',
date: new Date().toLocaleDateString('tr-TR'),
category: 'construction'
@@ -308,10 +322,10 @@ export default function Dashboard() {
id: Math.max(...documents.map(d => d.id), 0) + 1,
title: '',
description: '',
type: 'pdf',
type: 'PDF',
size: '',
date: new Date().toLocaleDateString('tr-TR'),
category: 'technical',
category: 'teknik',
downloadUrl: '#'
};
setEditingDocument(newDoc);
@@ -408,7 +422,12 @@ export default function Dashboard() {
}`}
>
<span className="text-2xl">{item.icon}</span>
<span className="font-medium">{item.label}</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>
@@ -1289,145 +1308,617 @@ export default function Dashboard() {
</div>
)}
</div>
{/* Features */}
<div className="mt-4 pt-4 border-t">
<p className="text-xs font-semibold text-gray-600 mb-2">ÖZELLİKLER:</p>
<div className="flex flex-wrap gap-2">
{station.features.map((feature, i) => (
<span key={i} className="px-2 py-1 bg-gray-100 text-gray-700 text-xs rounded">
{feature}
</span>
))}
</div>
</div>
</div>
</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 İstasyon</p>
<p className="text-2xl font-bold text-[#003366] mt-1">{metroStations.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">Tamamlanan</p>
<p className="text-2xl font-bold text-green-600 mt-1">{metroStations.filter(s => s.status === 'completed').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">Devam Eden</p>
<p className="text-2xl font-bold text-blue-600 mt-1">{metroStations.filter(s => s.status === 'in-progress').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">Planlanan</p>
<p className="text-2xl font-bold text-yellow-600 mt-1">{metroStations.filter(s => s.status === 'planned').length}</p>
</div>
<div className="text-4xl">📅</div>
</div>
</div>
</div>
</div>
)}
{/* Settings Section */}
{activeSection === 'settings' && (
{/* Live Stream Management Section */}
{activeSection === 'live-stream' && liveStreamConfig && (
<div className="space-y-6">
<div className="bg-white rounded-xl shadow-sm p-6">
<h3 className="text-2xl font-bold text-[#003366] mb-6">Sistem Ayarları</h3>
<div className="space-y-6">
{/* Site Settings */}
<div className="border-b pb-6">
<h4 className="text-lg font-semibold text-[#004B87] mb-4 flex items-center space-x-2">
<span>🌐</span>
<span>Site Ayarları</span>
</h4>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Site Başlığı</label>
<input type="text" defaultValue="A2 Metro Hattı Projesi" className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Site ıklaması</label>
<textarea rows={3} defaultValue="Ankara Büyükşehir Belediyesi A2 Metro Hattı İnşaat Projesi" className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent"></textarea>
</div>
</div>
<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&apos;sini ve ayarlarını yönetin</p>
</div>
<form onSubmit={(e) => {
e.preventDefault();
dataStore.setLiveStream(liveStreamConfig);
alert('Canlı yayın ayarları kaydedildi!');
}} 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>
{/* Admin Settings */}
<div className="border-b pb-6">
<h4 className="text-lg font-semibold text-[#004B87] mb-4 flex items-center space-x-2">
<span>👤</span>
<span>Admin Ayarları</span>
</h4>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Kullanıcı Adı</label>
<input type="text" defaultValue="admin" className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Yeni Şifre</label>
<input type="password" placeholder="Boş bırakın değiştirmek istemiyorsanız" className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#00B4D8] focus:border-transparent" />
</div>
</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"
placeholder="Canlı Yayın"
/>
</div>
{/* Notification Settings */}
<div className="border-b pb-6">
<h4 className="text-lg font-semibold text-[#004B87] mb-4 flex items-center space-x-2">
<span>🔔</span>
<span>Bildirim Ayarları</span>
</h4>
<div className="space-y-3">
<label className="flex items-center space-x-3">
<input type="checkbox" defaultChecked className="w-5 h-5 text-[#00B4D8] rounded focus:ring-[#00B4D8]" />
<span className="text-sm text-gray-700">Yeni haber eklendiğinde bildir</span>
</label>
<label className="flex items-center space-x-3">
<input type="checkbox" defaultChecked className="w-5 h-5 text-[#00B4D8] rounded focus:ring-[#00B4D8]" />
<span className="text-sm text-gray-700">Belge yüklendiğinde bildir</span>
</label>
<label className="flex items-center space-x-3">
<input type="checkbox" className="w-5 h-5 text-[#00B4D8] rounded focus:ring-[#00B4D8]" />
<span className="text-sm text-gray-700">Günlük rapor gönder</span>
</label>
</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"
placeholder="https://www.youtube.com/embed/VIDEO_ID"
required
/>
<p className="mt-2 text-sm text-gray-500">
YouTube video URL&apos;sini embed formatında girin. Örnek: https://www.youtube.com/embed/VIDEO_ID
</p>
</div>
{/* Save Button */}
<div className="flex justify-end space-x-3">
<button className="px-6 py-3 border border-gray-300 rounded-lg font-semibold text-gray-700 hover:bg-gray-50 transition-all">
İptal
</button>
<button className="px-6 py-3 bg-linear-to-r from-[#004B87] to-[#00B4D8] text-white rounded-lg font-semibold hover:shadow-lg transition-all">
{/* 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>
</div>
</form>
</div>
</div>
)}
{activeSection !== 'overview' && activeSection !== 'slider' && activeSection !== 'news' && activeSection !== 'media' && activeSection !== 'documents' && activeSection !== 'metro-line' && activeSection !== 'settings' && (
{/* 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={() => {
if (confirm('Bu mesajı silmek istediğinizden emin misiniz?')) {
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={() => {
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={(e) => {
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"
/>
</div>
<div className="flex items-center space-x-2">
<input
type="number"
value={faq.order}
onChange={(e) => {
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={() => {
if (confirm('Bu SSS silinsin mi?')) {
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={(e) => {
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"
/>
</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&apos;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={() => {
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={(e) => {
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"
/>
</div>
<button
onClick={() => {
if (confirm('Bu kamera silinsin mi?')) {
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={(e) => {
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"
/>
</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={(e) => {
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"
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={(e) => {
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"
>
<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={(e) => {
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"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Sıra</label>
<input
type="number"
value={camera.order}
onChange={(e) => {
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"
/>
</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.companyInfo.name}
onChange={(e) => setSiteSettings({
...siteSettings,
companyInfo: { ...siteSettings.companyInfo, 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"
/>
</div>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">Tam Ünvan</label>
<input
type="text"
value={siteSettings.companyInfo.fullName}
onChange={(e) => setSiteSettings({
...siteSettings,
companyInfo: { ...siteSettings.companyInfo, fullName: 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"
/>
</div>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">Kuruluş Yılı</label>
<input
type="text"
value={siteSettings.companyInfo.foundedYear}
onChange={(e) => setSiteSettings({
...siteSettings,
companyInfo: { ...siteSettings.companyInfo, foundedYear: 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"
/>
</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"
/>
</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"
/>
</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"
/>
</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"
/>
</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"
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"
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"
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"
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"
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}
@@ -1495,18 +1986,6 @@ export default function Dashboard() {
/>
</div>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">Görsel URL *</label>
<input
type="url"
value={editingSlide.image}
onChange={(e) => setEditingSlide({ ...editingSlide, 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"
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">Buton Metni</label>
@@ -1539,7 +2018,7 @@ export default function Dashboard() {
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)
Slider&apos;ı aktif yap (Ana sayfada göster)
</label>
</div>
@@ -1640,13 +2119,13 @@ export default function Dashboard() {
<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 })}
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"
required
>
<option value="construction">İnşaat</option>
<option value="announcement">Duyuru</option>
<option value="event">Etkinlik</option>
<option value="announcements">Duyuru</option>
<option value="events">Etkinlik</option>
</select>
</div>
@@ -1773,13 +2252,13 @@ export default function Dashboard() {
</div>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-2">Medya URL *</label>
<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.url}
onChange={(e) => setEditingMediaItem({ ...editingMediaItem, url: e.target.value })}
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"
placeholder="https://example.com/video.mp4"
placeholder={editingMediaItem.type === 'video' ? "https://www.youtube.com/embed/VIDEO_ID" : "https://example.com/image.jpg"}
required
/>
</div>
@@ -1869,14 +2348,14 @@ export default function Dashboard() {
<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 })}
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"
required
>
<option value="pdf">PDF</option>
<option value="doc">Word</option>
<option value="xls">Excel</option>
<option value="image">Görsel</option>
<option value="PDF">PDF</option>
<option value="DOCX">Word</option>
<option value="XLSX">Excel</option>
<option value="DWG">DWG</option>
</select>
</div>
@@ -1896,14 +2375,15 @@ export default function Dashboard() {
<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 })}
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"
required
>
<option value="technical">Teknik</option>
<option value="administrative">İdari</option>
<option value="legal">Hukuki</option>
<option value="financial">Mali</option>
<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>