This commit is contained in:
Şahan Hasret
2025-11-21 17:46:30 +03:00
parent c0b7fb463e
commit 76c31274d5
46 changed files with 3675 additions and 1043 deletions

View File

@@ -0,0 +1,73 @@
import { NextRequest, NextResponse } from 'next/server';
import { compare } from 'bcryptjs';
import { prisma } from '@/lib/prisma';
import { encrypt } from '@/lib/auth';
export async function POST(request: NextRequest) {
try {
const { username, password } = await request.json();
if (!username || !password) {
return NextResponse.json(
{ error: 'Kullanıcı adı ve şifre gerekli' },
{ status: 400 }
);
}
// Kullanıcıyı bul
const user = await prisma.user.findUnique({
where: { username },
});
if (!user) {
return NextResponse.json(
{ error: 'Kullanıcı adı veya şifre hatalı' },
{ status: 401 }
);
}
// Şifreyi kontrol et
const isPasswordValid = await compare(password, user.password);
if (!isPasswordValid) {
return NextResponse.json(
{ error: 'Kullanıcı adı veya şifre hatalı' },
{ status: 401 }
);
}
// JWT token oluştur
const token = await encrypt({
userId: user.id.toString(),
username: user.username,
});
// Response oluştur
const response = NextResponse.json(
{
user: {
id: user.id,
username: user.username,
},
},
{ status: 200 }
);
// Cookie'ye token ekle
response.cookies.set('session', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 60 * 60 * 24, // 24 saat
path: '/',
});
return response;
} catch (error) {
console.error('Login error:', error);
return NextResponse.json(
{ error: 'Giriş yapılırken bir hata oluştu' },
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,13 @@
import { NextResponse } from 'next/server';
export async function POST() {
const response = NextResponse.json(
{ message: ıkış başarılı' },
{ status: 200 }
);
// Cookie'yi sil
response.cookies.delete('session');
return response;
}

View File

@@ -0,0 +1,18 @@
import { NextResponse } from 'next/server';
import { getSession } from '@/lib/auth';
export async function GET() {
const session = await getSession();
if (!session) {
return NextResponse.json({ authenticated: false }, { status: 401 });
}
return NextResponse.json({
authenticated: true,
user: {
id: session.userId,
username: session.username,
},
});
}

103
app/api/cameras/route.ts Normal file
View File

@@ -0,0 +1,103 @@
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { withAuth } from '@/lib/auth';
// GET - Tüm kameraları getir
export async function GET() {
try {
const cameras = await prisma.camera.findMany({
orderBy: { order: 'asc' },
});
return NextResponse.json(cameras);
} catch (error) {
console.error('Cameras fetch error:', error);
return NextResponse.json(
{ error: 'Kameralar alınırken hata oluştu' },
{ status: 500 }
);
}
}
// POST - Yeni kamera ekle (Auth gerekli)
export async function POST(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const camera = await prisma.camera.create({
data: {
name: data.name,
location: data.location,
videoUrl: data.videoUrl,
status: data.status,
viewers: data.viewers || 0,
order: data.order,
},
});
return NextResponse.json(camera, { status: 201 });
} catch (error) {
console.error('Camera create error:', error);
return NextResponse.json(
{ error: 'Kamera oluşturulurken hata oluştu' },
{ status: 500 }
);
}
});
}
// PUT - Kamera güncelle (Auth gerekli)
export async function PUT(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const camera = await prisma.camera.update({
where: { id: data.id },
data: {
name: data.name,
location: data.location,
videoUrl: data.videoUrl,
status: data.status,
viewers: data.viewers,
order: data.order,
},
});
return NextResponse.json(camera);
} catch (error) {
console.error('Camera update error:', error);
return NextResponse.json(
{ error: 'Kamera güncellenirken hata oluştu' },
{ status: 500 }
);
}
});
}
// DELETE - Kamera sil (Auth gerekli)
export async function DELETE(request: NextRequest) {
return withAuth(request, async () => {
try {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
if (!id) {
return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
}
await prisma.camera.delete({
where: { id: parseInt(id) },
});
return NextResponse.json({ message: 'Kamera silindi' });
} catch (error) {
console.error('Camera delete error:', error);
return NextResponse.json(
{ error: 'Kamera silinirken hata oluştu' },
{ status: 500 }
);
}
});
}

116
app/api/documents/route.ts Normal file
View File

@@ -0,0 +1,116 @@
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { withAuth } from '@/lib/auth';
// GET - Tüm dokümanları getir
export async function GET() {
try {
const documents = await prisma.document.findMany({
orderBy: { createdAt: 'desc' },
});
return NextResponse.json(documents);
} catch (error) {
console.error('Documents fetch error:', error);
return NextResponse.json(
{ error: 'Dökümanlar alınırken hata oluştu' },
{ status: 500 }
);
}
}
// POST - Yeni döküman ekle (Auth gerekli)
export async function POST(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const document = await prisma.document.create({
data: {
title: data.title,
type: data.type,
category: data.category,
downloadUrl: data.fileUrl || data.downloadUrl,
size: data.fileSize || data.size || '0 MB',
description: data.description,
date: new Date().toISOString().split('T')[0],
},
});
return NextResponse.json(document, { status: 201 });
} catch (error) {
console.error('Document create error:', error);
return NextResponse.json(
{ error: 'Döküman oluşturulurken hata oluştu' },
{ status: 500 }
);
}
});
}
// PUT - Döküman güncelle (Auth gerekli)
export async function PUT(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const document = await prisma.document.update({
where: { id: data.id },
data: {
title: data.title,
type: data.type,
category: data.category,
downloadUrl: data.fileUrl || data.downloadUrl,
size: data.fileSize || data.size,
description: data.description,
},
});
return NextResponse.json(document);
} catch (error) {
console.error('Document update error:', error);
return NextResponse.json(
{ error: 'Döküman güncellenirken hata oluştu' },
{ status: 500 }
);
}
});
}
// DELETE - Döküman sil (Auth gerekli)
export async function DELETE(request: NextRequest) {
return withAuth(request, async () => {
try {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
console.log('DELETE document - ID:', id);
if (!id) {
return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
}
await prisma.document.delete({
where: { id: parseInt(id) },
});
return NextResponse.json({ message: 'Döküman silindi' });
} catch (error: unknown) {
console.error('Document delete error:', error);
// Prisma P2025: Record not found
if (error && typeof error === 'object' && 'code' in error && error.code === 'P2025') {
return NextResponse.json(
{ error: 'Döküman bulunamadı (zaten silinmiş olabilir)' },
{ status: 404 }
);
}
return NextResponse.json(
{ error: 'Döküman silinirken hata oluştu' },
{ status: 500 }
);
}
});
}

97
app/api/faqs/route.ts Normal file
View File

@@ -0,0 +1,97 @@
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { withAuth } from '@/lib/auth';
// GET - Tüm SSS'leri getir
export async function GET() {
try {
const faqs = await prisma.fAQ.findMany({
orderBy: { order: 'asc' },
});
return NextResponse.json(faqs);
} catch (error) {
console.error('FAQs fetch error:', error);
return NextResponse.json(
{ error: 'SSS alınırken hata oluştu' },
{ status: 500 }
);
}
}
// POST - Yeni SSS ekle (Auth gerekli)
export async function POST(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const faq = await prisma.fAQ.create({
data: {
question: data.question,
answer: data.answer,
order: data.order,
},
});
return NextResponse.json(faq, { status: 201 });
} catch (error) {
console.error('FAQ create error:', error);
return NextResponse.json(
{ error: 'SSS oluşturulurken hata oluştu' },
{ status: 500 }
);
}
});
}
// PUT - SSS güncelle (Auth gerekli)
export async function PUT(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const faq = await prisma.fAQ.update({
where: { id: data.id },
data: {
question: data.question,
answer: data.answer,
order: data.order,
},
});
return NextResponse.json(faq);
} catch (error) {
console.error('FAQ update error:', error);
return NextResponse.json(
{ error: 'SSS güncellenirken hata oluştu' },
{ status: 500 }
);
}
});
}
// DELETE - SSS sil (Auth gerekli)
export async function DELETE(request: NextRequest) {
return withAuth(request, async () => {
try {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
if (!id) {
return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
}
await prisma.fAQ.delete({
where: { id: parseInt(id) },
});
return NextResponse.json({ message: 'SSS silindi' });
} catch (error) {
console.error('FAQ delete error:', error);
return NextResponse.json(
{ error: 'SSS silinirken hata oluştu' },
{ status: 500 }
);
}
});
}

View File

@@ -0,0 +1,57 @@
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { withAuth } from '@/lib/auth';
// GET - Aktif canlı yayın bilgisini getir
export async function GET() {
try {
const liveStream = await prisma.liveStream.findFirst({
where: { active: true },
});
return NextResponse.json(liveStream || { active: false, url: '', title: '' });
} catch (error) {
console.error('Live stream fetch error:', error);
return NextResponse.json(
{ error: 'Canlı yayın bilgisi alınırken hata oluştu' },
{ status: 500 }
);
}
}
// POST - Canlı yayın ayarlarını güncelle (Auth gerekli)
export async function POST(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
// Önce tüm canlı yayınları pasif yap
await prisma.liveStream.updateMany({
data: { active: false },
});
// Yeni ayarları oluştur veya güncelle
const liveStream = await prisma.liveStream.upsert({
where: { id: data.id || 0 },
update: {
url: data.url,
active: data.active,
title: data.title,
},
create: {
url: data.url,
active: data.active,
title: data.title,
},
});
return NextResponse.json(liveStream);
} catch (error) {
console.error('Live stream update error:', error);
return NextResponse.json(
{ error: 'Canlı yayın güncellenirken hata oluştu' },
{ status: 500 }
);
}
});
}

102
app/api/media/route.ts Normal file
View File

@@ -0,0 +1,102 @@
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { withAuth } from '@/lib/auth';
// GET - Tüm medya öğelerini getir
export async function GET() {
try {
const media = await prisma.media.findMany({
orderBy: { createdAt: 'desc' },
});
return NextResponse.json(media);
} catch (error) {
console.error('Media fetch error:', error);
return NextResponse.json(
{ error: 'Medya öğeleri alınırken hata oluştu' },
{ status: 500 }
);
}
}
// POST - Yeni medya öğesi ekle (Auth gerekli)
export async function POST(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const media = await prisma.media.create({
data: {
title: data.title,
type: data.type,
thumbnail: data.thumbnail,
videoUrl: data.videoUrl,
description: data.description,
date: new Date().toISOString().split('T')[0],
},
});
return NextResponse.json(media, { status: 201 });
} catch (error) {
console.error('Media create error:', error);
return NextResponse.json(
{ error: 'Medya öğesi oluşturulurken hata oluştu' },
{ status: 500 }
);
}
});
}
// PUT - Medya öğesi güncelle (Auth gerekli)
export async function PUT(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const media = await prisma.media.update({
where: { id: data.id },
data: {
title: data.title,
type: data.type,
thumbnail: data.thumbnail,
videoUrl: data.videoUrl,
description: data.description,
},
});
return NextResponse.json(media);
} catch (error) {
console.error('Media update error:', error);
return NextResponse.json(
{ error: 'Medya öğesi güncellenirken hata oluştu' },
{ status: 500 }
);
}
});
}
// DELETE - Medya öğesi sil (Auth gerekli)
export async function DELETE(request: NextRequest) {
return withAuth(request, async () => {
try {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
if (!id) {
return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
}
await prisma.media.delete({
where: { id: parseInt(id) },
});
return NextResponse.json({ message: 'Medya öğesi silindi' });
} catch (error) {
console.error('Media delete error:', error);
return NextResponse.json(
{ error: 'Medya öğesi silinirken hata oluştu' },
{ status: 500 }
);
}
});
}

98
app/api/messages/route.ts Normal file
View File

@@ -0,0 +1,98 @@
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { withAuth } from '@/lib/auth';
// GET - Tüm mesajları getir (Auth gerekli)
export async function GET(request: NextRequest) {
return withAuth(request, async () => {
try {
const messages = await prisma.message.findMany({
orderBy: { createdAt: 'desc' },
});
return NextResponse.json(messages);
} catch (error) {
console.error('Messages fetch error:', error);
return NextResponse.json(
{ error: 'Mesajlar alınırken hata oluştu' },
{ status: 500 }
);
}
});
}
// POST - Yeni mesaj gönder (Public - iletişim formu)
export async function POST(request: NextRequest) {
try {
const data = await request.json();
const message = await prisma.message.create({
data: {
name: data.name,
email: data.email,
phone: data.phone || '',
subject: data.subject,
type: data.type || 'contact',
message: data.message,
read: false,
date: new Date().toISOString().split('T')[0],
},
});
return NextResponse.json(message, { status: 201 });
} catch (error) {
console.error('Message create error:', error);
return NextResponse.json(
{ error: 'Mesaj gönderilirken hata oluştu' },
{ status: 500 }
);
}
}
// PUT - Mesajı okundu olarak işaretle (Auth gerekli)
export async function PUT(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const message = await prisma.message.update({
where: { id: data.id },
data: { read: data.read },
});
return NextResponse.json(message);
} catch (error) {
console.error('Message update error:', error);
return NextResponse.json(
{ error: 'Mesaj güncellenirken hata oluştu' },
{ status: 500 }
);
}
});
}
// DELETE - Mesaj sil (Auth gerekli)
export async function DELETE(request: NextRequest) {
return withAuth(request, async () => {
try {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
if (!id) {
return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
}
await prisma.message.delete({
where: { id: parseInt(id) },
});
return NextResponse.json({ message: 'Mesaj silindi' });
} catch (error) {
console.error('Message delete error:', error);
return NextResponse.json(
{ error: 'Mesaj silinirken hata oluştu' },
{ status: 500 }
);
}
});
}

107
app/api/news/route.ts Normal file
View File

@@ -0,0 +1,107 @@
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { withAuth } from '@/lib/auth';
// GET - Tüm haberleri getir
export async function GET() {
try {
const news = await prisma.news.findMany({
orderBy: { createdAt: 'desc' },
});
return NextResponse.json(news);
} catch (error) {
console.error('News fetch error:', error);
return NextResponse.json(
{ error: 'Haberler alınırken hata oluştu' },
{ status: 500 }
);
}
}
// POST - Yeni haber ekle (Auth gerekli)
export async function POST(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const news = await prisma.news.create({
data: {
title: data.title,
summary: data.summary || data.content.substring(0, 150) + '...',
content: data.content,
category: data.category,
image: data.image,
author: data.author,
tags: data.tags || '',
date: new Date().toISOString().split('T')[0],
featured: data.featured || false,
},
});
return NextResponse.json(news, { status: 201 });
} catch (error) {
console.error('News create error:', error);
return NextResponse.json(
{ error: 'Haber oluşturulurken hata oluştu' },
{ status: 500 }
);
}
});
}
// PUT - Haber güncelle (Auth gerekli)
export async function PUT(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const news = await prisma.news.update({
where: { id: data.id },
data: {
title: data.title,
summary: data.summary,
content: data.content,
category: data.category,
image: data.image,
author: data.author,
tags: data.tags,
},
});
return NextResponse.json(news);
} catch (error) {
console.error('News update error:', error);
return NextResponse.json(
{ error: 'Haber güncellenirken hata oluştu' },
{ status: 500 }
);
}
});
}
// DELETE - Haber sil (Auth gerekli)
export async function DELETE(request: NextRequest) {
return withAuth(request, async () => {
try {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
if (!id) {
return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
}
await prisma.news.delete({
where: { id: parseInt(id) },
});
return NextResponse.json({ message: 'Haber silindi' });
} catch (error) {
console.error('News delete error:', error);
return NextResponse.json(
{ error: 'Haber silinirken hata oluştu' },
{ status: 500 }
);
}
});
}

52
app/api/settings/route.ts Normal file
View File

@@ -0,0 +1,52 @@
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { withAuth } from '@/lib/auth';
// GET - Site ayarlarını getir
export async function GET() {
try {
const settings = await prisma.siteSettings.findFirst({
where: { key: 'main' },
});
if (!settings) {
return NextResponse.json({ error: 'Ayarlar bulunamadı' }, { status: 404 });
}
return NextResponse.json(JSON.parse(settings.value));
} catch (error) {
console.error('Settings fetch error:', error);
return NextResponse.json(
{ error: 'Ayarlar alınırken hata oluştu' },
{ status: 500 }
);
}
}
// POST - Site ayarlarını güncelle (Auth gerekli)
export async function POST(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const settings = await prisma.siteSettings.upsert({
where: { key: 'main' },
update: {
value: JSON.stringify(data),
},
create: {
key: 'main',
value: JSON.stringify(data),
},
});
return NextResponse.json(JSON.parse(settings.value));
} catch (error) {
console.error('Settings update error:', error);
return NextResponse.json(
{ error: 'Ayarlar güncellenirken hata oluştu' },
{ status: 500 }
);
}
});
}

101
app/api/slider/route.ts Normal file
View File

@@ -0,0 +1,101 @@
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { withAuth } from '@/lib/auth';
// GET - Tüm slider item'ları getir
export async function GET() {
try {
const items = await prisma.sliderItem.findMany({
orderBy: { id: 'asc' },
});
return NextResponse.json(items);
} catch (error) {
console.error('Slider items fetch error:', error);
return NextResponse.json(
{ error: 'Slider items alınırken hata oluştu' },
{ status: 500 }
);
}
}
// POST - Yeni slider item ekle (Auth gerekli)
export async function POST(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const item = await prisma.sliderItem.create({
data: {
title: data.title,
description: data.description,
buttonText: data.buttonText,
buttonLink: data.buttonLink,
active: data.active ?? true,
},
});
return NextResponse.json(item, { status: 201 });
} catch (error) {
console.error('Slider item create error:', error);
return NextResponse.json(
{ error: 'Slider item oluşturulurken hata oluştu' },
{ status: 500 }
);
}
});
}
// PUT - Slider item güncelle (Auth gerekli)
export async function PUT(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const item = await prisma.sliderItem.update({
where: { id: data.id },
data: {
title: data.title,
description: data.description,
buttonText: data.buttonText,
buttonLink: data.buttonLink,
active: data.active,
},
});
return NextResponse.json(item);
} catch (error) {
console.error('Slider item update error:', error);
return NextResponse.json(
{ error: 'Slider item güncellenirken hata oluştu' },
{ status: 500 }
);
}
});
}
// DELETE - Slider item sil (Auth gerekli)
export async function DELETE(request: NextRequest) {
return withAuth(request, async () => {
try {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
if (!id) {
return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
}
await prisma.sliderItem.delete({
where: { id: parseInt(id) },
});
return NextResponse.json({ message: 'Slider item silindi' });
} catch (error) {
console.error('Slider item delete error:', error);
return NextResponse.json(
{ error: 'Slider item silinirken hata oluştu' },
{ status: 500 }
);
}
});
}

101
app/api/stations/route.ts Normal file
View File

@@ -0,0 +1,101 @@
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { withAuth } from '@/lib/auth';
// GET - Tüm metro istasyonlarını getir
export async function GET() {
try {
const stations = await prisma.metroStation.findMany({
orderBy: { order: 'asc' },
});
return NextResponse.json(stations);
} catch (error) {
console.error('Stations fetch error:', error);
return NextResponse.json(
{ error: 'İstasyonlar alınırken hata oluştu' },
{ status: 500 }
);
}
}
// POST - Yeni istasyon ekle (Auth gerekli)
export async function POST(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const station = await prisma.metroStation.create({
data: {
name: data.name,
status: data.status,
progress: data.progress,
order: data.order,
description: data.description || data.estimatedCompletion,
},
});
return NextResponse.json(station, { status: 201 });
} catch (error) {
console.error('Station create error:', error);
return NextResponse.json(
{ error: 'İstasyon oluşturulurken hata oluştu' },
{ status: 500 }
);
}
});
}
// PUT - İstasyon güncelle (Auth gerekli)
export async function PUT(request: NextRequest) {
return withAuth(request, async () => {
try {
const data = await request.json();
const station = await prisma.metroStation.update({
where: { id: data.id },
data: {
name: data.name,
status: data.status,
progress: data.progress,
order: data.order,
description: data.description || data.estimatedCompletion,
},
});
return NextResponse.json(station);
} catch (error) {
console.error('Station update error:', error);
return NextResponse.json(
{ error: 'İstasyon güncellenirken hata oluştu' },
{ status: 500 }
);
}
});
}
// DELETE - İstasyon sil (Auth gerekli)
export async function DELETE(request: NextRequest) {
return withAuth(request, async () => {
try {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
if (!id) {
return NextResponse.json({ error: 'ID gerekli' }, { status: 400 });
}
await prisma.metroStation.delete({
where: { id: parseInt(id) },
});
return NextResponse.json({ message: 'İstasyon silindi' });
} catch (error) {
console.error('Station delete error:', error);
return NextResponse.json(
{ error: 'İstasyon silinirken hata oluştu' },
{ status: 500 }
);
}
});
}

View File

@@ -11,7 +11,7 @@ export default function Documents() {
const [selectedCategory, setSelectedCategory] = useState('all');
useEffect(() => {
setDocuments(dataStore.getDocuments());
dataStore.getDocuments().then(setDocuments);
}, []);
const categories = [

View File

@@ -11,13 +11,15 @@ export default function LiveStream() {
const [cameras, setCameras] = useState<Camera[]>([]);
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);
}
dataStore.getCameras().then(loadedCameras => {
const onlineCameras = loadedCameras
.filter(cam => cam.status === 'online')
.sort((a, b) => a.order - b.order);
setCameras(onlineCameras);
if (onlineCameras.length > 0) {
setSelectedCamera(onlineCameras[0].id);
}
});
}, []);
const selectedCam = cameras.find(cam => cam.id === selectedCamera) || cameras[0];

View File

@@ -13,7 +13,7 @@ export default function News() {
const [selectedNews, setSelectedNews] = useState<number | null>(null);
useEffect(() => {
setNewsData(dataStore.getNews());
dataStore.getNews().then(setNewsData);
}, []);
const filteredNews = selectedCategory === 'all'

View File

@@ -7,11 +7,51 @@ import { dataStore, type SiteSettings } from '@/lib/dataStore';
export default function Contact() {
const [settings, setSettings] = useState<SiteSettings | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const [formData, setFormData] = useState({
name: '',
email: '',
phone: '',
subject: '',
message: ''
});
useEffect(() => {
setSettings(dataStore.getSiteSettings());
dataStore.getSiteSettings().then(setSettings);
}, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
try {
await dataStore.addMessage({
name: formData.name,
email: formData.email,
phone: formData.phone,
subject: formData.subject,
message: formData.message,
type: 'contact'
});
alert('Mesajınız başarıyla gönderildi! En kısa sürede size dönüş yapacağız.');
// Formu temizle
setFormData({
name: '',
email: '',
phone: '',
subject: '',
message: ''
});
} catch (error) {
console.error('Mesaj gönderilirken hata:', error);
alert('Mesaj gönderilirken bir hata oluştu. Lütfen tekrar deneyin.');
} finally {
setIsSubmitting(false);
}
};
if (!settings) return null;
return (
@@ -152,10 +192,7 @@ export default function Contact() {
</p>
</div>
<form className="max-w-2xl mx-auto space-y-6" onSubmit={(e) => {
e.preventDefault();
alert('Mesajınız gönderildi! (Demo)');
}}>
<form className="max-w-2xl mx-auto space-y-6" onSubmit={handleSubmit}>
{/* İki Sütunlu Alan */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Ad Soyad */}
@@ -167,6 +204,8 @@ export default function Contact() {
type="text"
id="contact-name"
required
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: 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 outline-none transition-all text-gray-900 placeholder:text-gray-500"
placeholder="Adınız ve Soyadınız"
/>
@@ -181,6 +220,8 @@ export default function Contact() {
type="email"
id="contact-email"
required
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: 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 outline-none transition-all text-gray-900 placeholder:text-gray-500"
placeholder="ornek@email.com"
/>
@@ -194,6 +235,8 @@ export default function Contact() {
<input
type="tel"
id="contact-phone"
value={formData.phone}
onChange={(e) => setFormData({ ...formData, phone: 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 outline-none transition-all text-gray-900 placeholder:text-gray-500"
placeholder="0(5__) ___ __ __"
/>
@@ -208,6 +251,8 @@ export default function Contact() {
type="text"
id="contact-subject"
required
value={formData.subject}
onChange={(e) => setFormData({ ...formData, subject: 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 outline-none transition-all text-gray-900 placeholder:text-gray-500"
placeholder="Mesaj konusu"
/>
@@ -223,6 +268,8 @@ export default function Contact() {
id="contact-message"
required
rows={6}
value={formData.message}
onChange={(e) => setFormData({ ...formData, message: 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 outline-none transition-all resize-none text-gray-900 placeholder:text-gray-500"
placeholder="Lütfen mesajınızı yazınız..."
></textarea>
@@ -246,12 +293,25 @@ export default function Contact() {
<div className="text-center pt-4">
<button
type="submit"
className="px-8 py-3 bg-[#00B4D8] text-white rounded-lg hover:bg-[#004B87] transition-colors font-semibold shadow-lg hover:shadow-xl flex items-center justify-center space-x-2 mx-auto"
disabled={isSubmitting}
className="px-8 py-3 bg-[#00B4D8] text-white rounded-lg hover:bg-[#004B87] transition-colors font-semibold shadow-lg hover:shadow-xl flex items-center justify-center space-x-2 mx-auto disabled:opacity-50 disabled:cursor-not-allowed"
>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
</svg>
<span>Gönder</span>
{isSubmitting ? (
<>
<svg className="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span>Gönderiliyor...</span>
</>
) : (
<>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
</svg>
<span>Gönder</span>
</>
)}
</button>
</div>
</form>

View File

@@ -13,7 +13,7 @@ export default function MediaGallery() {
const [selectedMedia, setSelectedMedia] = useState<number | null>(null);
useEffect(() => {
setMediaItems(dataStore.getMedia());
dataStore.getMedia().then(setMediaItems);
}, []);
const filteredMedia = selectedTab === 'all'

View File

@@ -10,7 +10,8 @@ import DocumentsSection from "@/components/DocumentsSection";
import MediaGallery from "@/components/MediaGallery";
import ComplaintForm from "@/components/ComplaintForm";
import ContactSection from "@/components/ContactSection";
import { dataStore } from '@/lib/dataStore';
import { dataStore, type SliderItem, type LiveStreamConfig } from '@/lib/dataStore';
import type { NewsItem } from '@/data/news';
export default function Home() {
const [showLiveStream, setShowLiveStream] = useState(false);
@@ -23,15 +24,15 @@ export default function Home() {
const [currentSlide, setCurrentSlide] = useState(0);
// Gerçek veriler için state
const [heroSlides, setHeroSlides] = useState(dataStore.getSlider());
const [newsData, setNewsData] = useState(dataStore.getNews());
const [liveStreamConfig, setLiveStreamConfig] = useState(dataStore.getLiveStream());
const [heroSlides, setHeroSlides] = useState<SliderItem[]>([]);
const [newsData, setNewsData] = useState<NewsItem[]>([]);
const [liveStreamConfig, setLiveStreamConfig] = useState<LiveStreamConfig>({ url: '', active: false });
// Verileri yükle
useEffect(() => {
setHeroSlides(dataStore.getSlider());
setNewsData(dataStore.getNews());
setLiveStreamConfig(dataStore.getLiveStream());
dataStore.getSlider().then(setHeroSlides);
dataStore.getNews().then(setNewsData);
dataStore.getLiveStream().then(setLiveStreamConfig);
}, []);
// Modal açıldığında yukarı kaydır - KALDIRILDI (kullanıcı deneyimi için)

View File

@@ -11,7 +11,9 @@ export default function FAQPage() {
const [faqs, setFaqs] = useState<FAQ[]>([]);
useEffect(() => {
setFaqs(dataStore.getFAQs().sort((a, b) => a.order - b.order));
dataStore.getFAQs().then(faqs => {
setFaqs(faqs.sort((a, b) => a.order - b.order));
});
}, []);
const toggleFAQ = (id: number) => {

File diff suppressed because it is too large Load Diff

View File

@@ -14,12 +14,28 @@ export default function AdminLogin() {
setLoading(true);
setError('');
// Basit auth - production'da backend ile yapılmalı
if (credentials.username === 'admin' && credentials.password === 'A2Metro2025!') {
localStorage.setItem('admin_token', 'authenticated');
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(credentials),
});
const data = await response.json();
if (!response.ok) {
setError(data.error || 'Giriş başarısız');
setLoading(false);
return;
}
// Başarılı giriş
router.push('/yonetim-paneli-a2m-secure/dashboard');
} else {
setError('Kullanıcı adı veya şifre hatalı');
} catch (error) {
console.error('Login error:', error);
setError('Bir hata oluştu. Lütfen tekrar deneyin.');
setLoading(false);
}
};