From dbca26f3adf9c84566c3be643b30c067b260cbdc Mon Sep 17 00:00:00 2001 From: kertenkerem Date: Thu, 8 Jan 2026 02:03:26 +0300 Subject: [PATCH] feat: add in-place editing functionality for classes and instructors with dedicated API routes. --- src/app/admin/branches/BranchList.tsx | 90 ++++++++++++++- src/app/admin/classes/ClassList.tsx | 110 +++++++++++++++++-- src/app/admin/classes/page.tsx | 10 +- src/app/admin/instructors/InstructorList.tsx | 77 ++++++++++++- src/app/api/classes/[id]/route.ts | 22 ++++ src/app/api/instructors/[id]/route.ts | 21 ++++ 6 files changed, 308 insertions(+), 22 deletions(-) diff --git a/src/app/admin/branches/BranchList.tsx b/src/app/admin/branches/BranchList.tsx index 8295334..78bc1c0 100644 --- a/src/app/admin/branches/BranchList.tsx +++ b/src/app/admin/branches/BranchList.tsx @@ -1,9 +1,12 @@ 'use client'; +import { useState } from 'react'; import { useRouter } from 'next/navigation'; import styles from './branches.module.css'; export function BranchList({ branches }: { branches: any[] }) { const router = useRouter(); + const [editingId, setEditingId] = useState(null); + const [editForm, setEditForm] = useState(null); const handleDelete = async (id: string) => { if (!confirm('Silmek istediğinize emin misiniz?')) return; @@ -11,16 +14,91 @@ export function BranchList({ branches }: { branches: any[] }) { router.refresh(); }; + const startEdit = (branch: any) => { + setEditingId(branch.id); + setEditForm({ + name: branch.name, + phone: branch.phone || '', + address: branch.address || '', + hallCount: branch.hallCount?.toString() || '1' + }); + }; + + const handleUpdate = async (e: React.FormEvent) => { + e.preventDefault(); + const res = await fetch(`/api/branches/${editingId}`, { + method: 'PUT', + 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.'); + } + }; + return (
{branches.map(b => (
-
-

{b.name}

-

{b.phone ? `Tel: ${b.phone}` : 'Telefon yok'} | {b.address || 'Adres yok'}

- Salon Sayısı: {b.hallCount || 1} -
- + {editingId === b.id ? ( +
+
+ + setEditForm({ ...editForm, name: e.target.value })} + className={styles.input} + required + /> +
+
+ + setEditForm({ ...editForm, phone: e.target.value })} + className={styles.input} + /> +
+
+ + setEditForm({ ...editForm, address: e.target.value })} + className={styles.input} + /> +
+
+ + setEditForm({ ...editForm, hallCount: e.target.value })} + className={styles.input} + /> +
+
+ + +
+
+ ) : ( + <> +
+

{b.name}

+

{b.phone ? `Tel: ${b.phone}` : 'Telefon yok'} | {b.address || 'Adres yok'}

+ Salon Sayısı: {b.hallCount || 1} +
+
+ + +
+ + )}
))}
diff --git a/src/app/admin/classes/ClassList.tsx b/src/app/admin/classes/ClassList.tsx index aba394d..e57668b 100644 --- a/src/app/admin/classes/ClassList.tsx +++ b/src/app/admin/classes/ClassList.tsx @@ -1,9 +1,20 @@ 'use client'; +import { useState } from 'react'; import { useRouter } from 'next/navigation'; import styles from '../branches/branches.module.css'; -export function ClassList({ classes }: { classes: any[] }) { +export function ClassList({ + classes, + branches, + instructors +}: { + classes: any[], + branches: any[], + instructors: any[] +}) { const router = useRouter(); + const [editingId, setEditingId] = useState(null); + const [editForm, setEditForm] = useState(null); const handleDelete = async (id: string) => { if (!confirm('Silmek istediğinize emin misiniz?')) return; @@ -11,16 +22,101 @@ export function ClassList({ classes }: { classes: any[] }) { router.refresh(); }; + const startEdit = (c: any) => { + setEditingId(c.id); + setEditForm({ + name: c.name, + description: c.description || '', + branchId: c.branchId, + instructorId: c.instructorId || '' + }); + }; + + const handleUpdate = async (e: React.FormEvent) => { + e.preventDefault(); + const res = await fetch(`/api/classes/${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.'); + } + }; + return (
{classes.map(c => (
-
-

{c.name}

-

Şube: {c.branch?.name}

- {c.instructor &&

Hoca: {c.instructor.name}

} -
- + {editingId === c.id ? ( +
+
+ + setEditForm({ ...editForm, name: e.target.value })} + className={styles.input} + required + /> +
+
+ + setEditForm({ ...editForm, description: e.target.value })} + className={styles.input} + /> +
+
+ + +
+
+ + +
+
+ + +
+
+ ) : ( + <> +
+

{c.name}

+

Şube: {c.branch?.name}

+ {c.instructor &&

Hoca: {c.instructor.name}

} + {c.description} +
+
+ + +
+ + )}
))}
diff --git a/src/app/admin/classes/page.tsx b/src/app/admin/classes/page.tsx index c9b467d..d7a3742 100644 --- a/src/app/admin/classes/page.tsx +++ b/src/app/admin/classes/page.tsx @@ -4,9 +4,11 @@ import { ClassList } from './ClassList'; import styles from '../branches/branches.module.css'; export default async function Page() { - const classes = await prisma.danceClass.findMany({ include: { branch: true, instructor: true }, orderBy: { createdAt: 'desc' } }); - const branches = await prisma.branch.findMany(); - const instructors = await prisma.instructor.findMany(); + const [classes, branches, instructors] = await Promise.all([ + prisma.danceClass.findMany({ include: { branch: true, instructor: true }, orderBy: { createdAt: 'desc' } }), + prisma.branch.findMany({ select: { id: true, name: true } }), + prisma.instructor.findMany({ select: { id: true, name: true } }) + ]); return (
@@ -14,7 +16,7 @@ export default async function Page() {

Sınıflar

- + ); } diff --git a/src/app/admin/instructors/InstructorList.tsx b/src/app/admin/instructors/InstructorList.tsx index 9b6c614..dca64bc 100644 --- a/src/app/admin/instructors/InstructorList.tsx +++ b/src/app/admin/instructors/InstructorList.tsx @@ -1,9 +1,12 @@ 'use client'; +import { useState } from 'react'; import { useRouter } from 'next/navigation'; import styles from '../branches/branches.module.css'; export function InstructorList({ instructors }: { instructors: any[] }) { const router = useRouter(); + const [editingId, setEditingId] = useState(null); + const [editForm, setEditForm] = useState(null); const handleDelete = async (id: string) => { if (!confirm('Silmek istediğinize emin misiniz?')) return; @@ -11,15 +14,79 @@ export function InstructorList({ instructors }: { instructors: any[] }) { router.refresh(); }; + const startEdit = (instructor: any) => { + setEditingId(instructor.id); + setEditForm({ + name: instructor.name, + phone: instructor.phone || '', + bio: instructor.bio || '' + }); + }; + + const handleUpdate = async (e: React.FormEvent) => { + e.preventDefault(); + const res = await fetch(`/api/instructors/${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.'); + } + }; + return (
{instructors.map(i => (
-
-

{i.name}

-

{i.bio}

-
- + {editingId === i.id ? ( +
+
+ + setEditForm({ ...editForm, name: e.target.value })} + className={styles.input} + required + /> +
+
+ + setEditForm({ ...editForm, phone: e.target.value })} + className={styles.input} + /> +
+
+ + setEditForm({ ...editForm, bio: e.target.value })} + className={styles.input} + /> +
+
+ + +
+
+ ) : ( + <> +
+

{i.name} {i.phone && ({i.phone})}

+

{i.bio || 'Biyografi bulunmuyor'}

+
+
+ + +
+ + )}
))}
diff --git a/src/app/api/classes/[id]/route.ts b/src/app/api/classes/[id]/route.ts index f28462c..1ffdb32 100644 --- a/src/app/api/classes/[id]/route.ts +++ b/src/app/api/classes/[id]/route.ts @@ -1,6 +1,28 @@ 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 danceClass = await prisma.danceClass.update({ + where: { id }, + data: { + name: json.name, + description: json.description, + branchId: json.branchId, + instructorId: json.instructorId + }, + }); + return NextResponse.json({ success: true, danceClass }); + } catch (error) { + return NextResponse.json({ error: 'Failed to update class' }, { status: 500 }); + } +} + export async function DELETE( request: Request, { params }: { params: Promise<{ id: string }> } diff --git a/src/app/api/instructors/[id]/route.ts b/src/app/api/instructors/[id]/route.ts index 3d0cf41..073b8ac 100644 --- a/src/app/api/instructors/[id]/route.ts +++ b/src/app/api/instructors/[id]/route.ts @@ -1,6 +1,27 @@ 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 instructor = await prisma.instructor.update({ + where: { id }, + data: { + name: json.name, + bio: json.bio, + phone: json.phone + }, + }); + return NextResponse.json({ success: true, instructor }); + } catch (error) { + return NextResponse.json({ error: 'Failed to update instructor' }, { status: 500 }); + } +} + export async function DELETE( request: Request, { params }: { params: Promise<{ id: string }> }