diff --git a/prisma/dev.db b/prisma/dev.db index d79b091..eeb0ec3 100644 Binary files a/prisma/dev.db and b/prisma/dev.db differ diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f842a5f..f983f60 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -19,12 +19,12 @@ model Branch { name String address String? phone String? - instructors Instructor[] // Implicit many-to-many + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt classes DanceClass[] lessons Lesson[] students Student[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + instructors Instructor[] @relation("BranchToInstructor") } model Instructor { @@ -32,41 +32,41 @@ model Instructor { name String bio String? phone String? - branches Branch[] // Implicit many-to-many - classes DanceClass[] - lessons Lesson[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + classes DanceClass[] + lessons Lesson[] + branches Branch[] @relation("BranchToInstructor") } model DanceClass { - id String @id @default(uuid()) + id String @id @default(uuid()) name String description String? branchId String - branch Branch @relation(fields: [branchId], references: [id]) instructorId String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt instructor Instructor? @relation(fields: [instructorId], references: [id]) - lessons Lesson[] + branch Branch @relation(fields: [branchId], references: [id]) fees Fee[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + lessons Lesson[] } model Lesson { - id String @id @default(uuid()) - name String? // Optional name e.g. "Special Workshop" + id String @id @default(uuid()) + name String? startTime DateTime endTime DateTime - type String // GROUP, PRIVATE + type String branchId String - branch Branch @relation(fields: [branchId], references: [id]) instructorId String - instructor Instructor @relation(fields: [instructorId], references: [id]) classId String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt class DanceClass? @relation(fields: [classId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + instructor Instructor @relation(fields: [instructorId], references: [id]) + branch Branch @relation(fields: [branchId], references: [id]) } model Fee { @@ -74,11 +74,11 @@ model Fee { name String amount Float currency String @default("TRY") - type String // MONTHLY, PER_LESSON, PACKAGE + type String classId String? - class DanceClass? @relation(fields: [classId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + class DanceClass? @relation(fields: [classId], references: [id]) } model Student { @@ -88,7 +88,7 @@ model Student { phone String birthDate DateTime branchId String? - branch Branch? @relation(fields: [branchId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + branch Branch? @relation(fields: [branchId], references: [id]) } diff --git a/src/app/admin/register/RegisterForm.tsx b/src/app/admin/register/RegisterForm.tsx index a6823dd..3e184ec 100644 --- a/src/app/admin/register/RegisterForm.tsx +++ b/src/app/admin/register/RegisterForm.tsx @@ -1,6 +1,7 @@ 'use client'; import { useState } from 'react'; -import styles from './register.module.css'; +import { useRouter } from 'next/navigation'; +import styles from '../branches/branches.module.css'; export function RegisterForm({ branches }: { branches: any[] }) { const [formData, setFormData] = useState({ @@ -10,12 +11,10 @@ export function RegisterForm({ branches }: { branches: any[] }) { birthDate: '', branchId: '' }); - const [status, setStatus] = useState<'idle' | 'success' | 'error'>('idle'); + const router = useRouter(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - setStatus('idle'); - const res = await fetch('/api/register', { method: 'POST', body: JSON.stringify(formData), @@ -23,10 +22,8 @@ export function RegisterForm({ branches }: { branches: any[] }) { }); if (res.ok) { - setStatus('success'); setFormData({ name: '', email: '', phone: '', birthDate: '', branchId: '' }); - } else { - setStatus('error'); + router.refresh(); } }; @@ -35,41 +32,38 @@ export function RegisterForm({ branches }: { branches: any[] }) { }; return ( -
- {status === 'success' &&
Kayıt başarıyla oluşturuldu!
} - {status === 'error' &&
Bir hata oluştu. Lütfen tekrar deneyin.
} - -
- + +
+
-
- +
+
-
- +
+
-
- +
+
-
- - + {branches.map(b => ( ))}
- + ); } diff --git a/src/app/admin/register/StudentList.tsx b/src/app/admin/register/StudentList.tsx new file mode 100644 index 0000000..83be69e --- /dev/null +++ b/src/app/admin/register/StudentList.tsx @@ -0,0 +1,134 @@ +'use client'; +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import styles from '../branches/branches.module.css'; + +export function StudentList({ students, branches }: { students: any[], branches: any[] }) { + const router = useRouter(); + const [showSensitive, setShowSensitive] = useState(false); + const [editingId, setEditingId] = useState(null); + const [editForm, setEditForm] = useState(null); + + const handleDelete = async (id: string) => { + if (!confirm('Kaydı silmek istediğinize emin misiniz?')) return; + const res = await fetch(`/api/students/${id}`, { method: 'DELETE' }); + if (res.ok) { + router.refresh(); + } else { + alert('Silme işlemi başarısız oldu.'); + } + }; + + const startEdit = (student: any) => { + setEditingId(student.id); + setEditForm({ + name: student.name, + email: student.email || '', + phone: student.phone, + branchId: student.branchId || '', + birthDate: student.birthDate ? new Date(student.birthDate).toISOString().split('T')[0] : '' + }); + }; + + const handleUpdate = async (e: React.FormEvent) => { + e.preventDefault(); + const res = await fetch(`/api/students/${editingId}`, { + method: 'PATCH', + body: JSON.stringify(editForm), + headers: { 'Content-Type': 'application/json' } + }); + + if (res.ok) { + setEditingId(null); + router.refresh(); + } else { + alert('Güncelleme başarısız oldu.'); + } + }; + + const maskInfo = (text: string) => { + if (!text) return 'Yok'; + if (showSensitive) return text; + return text.replace(/.(?=.{4})/g, '*'); + }; + + return ( +
+
+ +
+ {students.map(s => ( +
+ {editingId === s.id ? ( +
+
+ + setEditForm({ ...editForm, name: e.target.value })} + className={styles.input} + required + /> +
+
+ + setEditForm({ ...editForm, phone: e.target.value })} + className={styles.input} + required + /> +
+
+ + setEditForm({ ...editForm, email: e.target.value })} + className={styles.input} + /> +
+
+ + +
+
+ + +
+
+ ) : ( + <> +
+

{s.name}

+

+ Tel: {maskInfo(s.phone)} | + Email: {maskInfo(s.email)} +

+ {s.branch?.name || 'Şube seçilmemiş'} +
+
+ + +
+ + )} +
+ ))} +
+ ); +} diff --git a/src/app/admin/register/page.tsx b/src/app/admin/register/page.tsx index 252bd9e..d6455f3 100644 --- a/src/app/admin/register/page.tsx +++ b/src/app/admin/register/page.tsx @@ -1,18 +1,27 @@ import { prisma } from '@/infrastructure/db/prisma'; import { RegisterForm } from './RegisterForm'; -import styles from './register.module.css'; +import { StudentList } from './StudentList'; +import styles from '../branches/branches.module.css'; -export default async function RegisterPage() { - const branches = await prisma.branch.findMany({ - select: { id: true, name: true } - }); +export default async function Page() { + const [branches, students] = await Promise.all([ + prisma.branch.findMany({ select: { id: true, name: true } }), + prisma.student.findMany({ + include: { branch: true }, + orderBy: { createdAt: 'desc' } + }) + ]); return (
-

Yeni Öğrenci Kaydı

+

Öğrenci Kayıt İşlemleri

+
+

Kayıtlı Öğrenciler

+
+
); } diff --git a/src/app/admin/register/register.module.css b/src/app/admin/register/register.module.css deleted file mode 100644 index 514f462..0000000 --- a/src/app/admin/register/register.module.css +++ /dev/null @@ -1,90 +0,0 @@ -.container { - padding: 1rem; -} - -.header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 2rem; -} - -.title { - font-size: 2rem; - font-weight: 600; - color: #333; -} - -.card { - background: white; - padding: 2rem; - border-radius: 8px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); - max-width: 800px; -} - -.formGroup { - margin-bottom: 1.5rem; - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.label { - font-weight: 500; - color: #555; - font-size: 0.95rem; -} - -.input, -.select { - padding: 0.75rem; - border: 1px solid #ddd; - border-radius: 6px; - font-size: 1rem; - color: #333; - background-color: #fff; - transition: border-color 0.2s; -} - -.input:focus, -.select:focus { - outline: none; - border-color: #333; -} - -.button { - padding: 0.75rem 2rem; - background: #333; - color: white; - border: none; - border-radius: 6px; - font-size: 1rem; - font-weight: 500; - cursor: pointer; - transition: background-color 0.2s; - margin-top: 1rem; - align-self: flex-start; -} - -.button:hover { - background: #000; -} - -.success { - background-color: #e8f5e9; - color: #2e7d32; - padding: 1rem; - border-radius: 6px; - margin-bottom: 1.5rem; - border: 1px solid #c8e6c9; -} - -.error { - background-color: #ffebee; - color: #c62828; - padding: 1rem; - border-radius: 6px; - margin-bottom: 1.5rem; - border: 1px solid #ffcdd2; -} \ No newline at end of file diff --git a/src/app/api/branches/[id]/route.ts b/src/app/api/branches/[id]/route.ts index 775e848..5a68415 100644 --- a/src/app/api/branches/[id]/route.ts +++ b/src/app/api/branches/[id]/route.ts @@ -3,11 +3,12 @@ import { prisma } from '@/infrastructure/db/prisma'; export async function DELETE( request: Request, - { params }: { params: { id: string } } + { params }: { params: Promise<{ id: string }> } ) { try { + const { id } = await params; await prisma.branch.delete({ - where: { id: params.id }, + where: { id }, }); return NextResponse.json({ success: true }); } catch (error) { @@ -17,12 +18,13 @@ export async function DELETE( export async function PUT( request: Request, - { params }: { params: { id: string } } + { params }: { params: Promise<{ id: string }> } ) { try { + const { id } = await params; const json = await request.json(); const branch = await prisma.branch.update({ - where: { id: params.id }, + where: { id }, data: json, }); return NextResponse.json(branch); diff --git a/src/app/api/classes/[id]/route.ts b/src/app/api/classes/[id]/route.ts index 40d26e4..f28462c 100644 --- a/src/app/api/classes/[id]/route.ts +++ b/src/app/api/classes/[id]/route.ts @@ -3,11 +3,12 @@ import { prisma } from '@/infrastructure/db/prisma'; export async function DELETE( request: Request, - { params }: { params: { id: string } } + { params }: { params: Promise<{ id: string }> } ) { try { + const { id } = await params; await prisma.danceClass.delete({ - where: { id: params.id }, + where: { id }, }); return NextResponse.json({ success: true }); } catch (error) { diff --git a/src/app/api/instructors/[id]/route.ts b/src/app/api/instructors/[id]/route.ts index c688545..3d0cf41 100644 --- a/src/app/api/instructors/[id]/route.ts +++ b/src/app/api/instructors/[id]/route.ts @@ -3,11 +3,12 @@ import { prisma } from '@/infrastructure/db/prisma'; export async function DELETE( request: Request, - { params }: { params: { id: string } } + { params }: { params: Promise<{ id: string }> } ) { try { + const { id } = await params; await prisma.instructor.delete({ - where: { id: params.id }, + where: { id }, }); return NextResponse.json({ success: true }); } catch (error) { diff --git a/src/app/api/students/[id]/route.ts b/src/app/api/students/[id]/route.ts new file mode 100644 index 0000000..7d0c975 --- /dev/null +++ b/src/app/api/students/[id]/route.ts @@ -0,0 +1,42 @@ +import { NextResponse } from 'next/server'; +import { prisma } from '@/infrastructure/db/prisma'; + +export async function PATCH( + request: Request, + { params }: { params: Promise<{ id: string }> } +) { + try { + const { id } = await params; + const json = await request.json(); + const student = await prisma.student.update({ + where: { id }, + data: { + name: json.name, + email: json.email || null, + phone: json.phone, + birthDate: json.birthDate ? new Date(json.birthDate) : undefined, + branchId: json.branchId || null + }, + }); + return NextResponse.json({ success: true, student }); + } catch (error: any) { + console.error('Update error:', error); + return NextResponse.json({ error: 'Failed to update student', details: error.message }, { status: 500 }); + } +} + +export async function DELETE( + request: Request, + { params }: { params: Promise<{ id: string }> } +) { + try { + const { id } = await params; + await prisma.student.delete({ + where: { id }, + }); + return NextResponse.json({ success: true }); + } catch (error) { + console.error('Delete error:', error); + return NextResponse.json({ error: 'Failed to delete student' }, { status: 500 }); + } +} diff --git a/src/infrastructure/db/prisma.ts b/src/infrastructure/db/prisma.ts index f759962..bf21eff 100644 --- a/src/infrastructure/db/prisma.ts +++ b/src/infrastructure/db/prisma.ts @@ -2,8 +2,13 @@ import { PrismaClient } from '@prisma/client'; const globalForPrisma = global as unknown as { prisma: PrismaClient }; -export const prisma = - globalForPrisma.prisma || - new PrismaClient(); +const prismaInstance = globalForPrisma.prisma || new PrismaClient(); -if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; +if (process.env.NODE_ENV !== 'production') { + globalForPrisma.prisma = prismaInstance; + // Log models to verify generation + const models = Object.keys(prismaInstance).filter(k => !k.startsWith('_') && !k.startsWith('$')); + console.log('Prisma models available:', models); +} + +export const prisma = prismaInstance;