Student Registeration CRUD & Listing

This commit is contained in:
kertenkerem
2026-01-08 01:14:35 +03:00
parent d5de503f86
commit 8fa560cd5e
6 changed files with 236 additions and 0 deletions

Binary file not shown.

View File

@@ -22,6 +22,7 @@ model Branch {
instructors Instructor[] // Implicit many-to-many
classes DanceClass[]
lessons Lesson[]
students Student[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
@@ -79,3 +80,15 @@ model Fee {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Student {
id String @id @default(uuid())
name String
email String?
phone String
birthDate DateTime
branchId String?
branch Branch? @relation(fields: [branchId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

View File

@@ -0,0 +1,28 @@
import { NextResponse } from 'next/server';
import { prisma } from '@/infrastructure/db/prisma';
export async function POST(request: Request) {
try {
const json = await request.json();
// Basic validation
if (!json.name || !json.phone || !json.birthDate) {
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
}
const student = await prisma.student.create({
data: {
name: json.name,
email: json.email || null,
phone: json.phone,
birthDate: new Date(json.birthDate), // Ensure date format
branchId: json.branchId || null
},
});
return NextResponse.json({ success: true, student });
} catch (error) {
console.error('Registration error:', error);
return NextResponse.json({ error: 'Failed to register' }, { status: 500 });
}
}

View File

@@ -0,0 +1,78 @@
'use client';
import { useState } from 'react';
import styles from './register.module.css';
export function RegisterForm({ branches }: { branches: any[] }) {
const [formData, setFormData] = useState({
name: '',
email: '',
phone: '',
birthDate: '',
branchId: ''
});
const [status, setStatus] = useState<'idle' | 'success' | 'error'>('idle');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setStatus('idle');
const res = await fetch('/api/register', {
method: 'POST',
body: JSON.stringify(formData),
headers: { 'Content-Type': 'application/json' }
});
if (res.ok) {
setStatus('success');
setFormData({ name: '', email: '', phone: '', birthDate: '', branchId: '' });
} else {
setStatus('error');
}
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
return (
<form onSubmit={handleSubmit} className={styles.card}>
<h1 className={styles.title}>Dance School</h1>
<p className={styles.subtitle}>Öğrenci Kayıt Formu</p>
{status === 'success' && <div className={styles.success}>Kayıt başarıyla oluşturuldu!</div>}
{status === 'error' && <div className={styles.error}>Bir hata oluştu. Lütfen tekrar deneyin.</div>}
<div className={styles.formGroup}>
<label className={styles.label}>Ad Soyad</label>
<input name="name" value={formData.name} onChange={handleChange} className={styles.input} required placeholder="Adınız" />
</div>
<div className={styles.formGroup}>
<label className={styles.label}>Email (İsteğe bağlı)</label>
<input name="email" type="email" value={formData.email} onChange={handleChange} className={styles.input} placeholder="ornek@email.com" />
</div>
<div className={styles.formGroup}>
<label className={styles.label}>Telefon</label>
<input name="phone" type="tel" value={formData.phone} onChange={handleChange} className={styles.input} required placeholder="5XX XXX XX XX" />
</div>
<div className={styles.formGroup}>
<label className={styles.label}>Doğum Tarihi</label>
<input name="birthDate" type="date" value={formData.birthDate} onChange={handleChange} className={styles.input} required />
</div>
<div className={styles.formGroup}>
<label className={styles.label}>Tercih Edilen Şube</label>
<select name="branchId" value={formData.branchId} onChange={handleChange} className={styles.select}>
<option value="">Seçiniz (Opsiyonel)</option>
{branches.map(b => (
<option key={b.id} value={b.id}>{b.name}</option>
))}
</select>
</div>
<button type="submit" className={styles.button}>Kayıt Ol</button>
</form>
);
}

15
src/app/register/page.tsx Normal file
View File

@@ -0,0 +1,15 @@
import { prisma } from '@/infrastructure/db/prisma';
import { RegisterForm } from './RegisterForm';
import styles from './register.module.css';
export default async function RegisterPage() {
const branches = await prisma.branch.findMany({
select: { id: true, name: true }
});
return (
<div className={styles.container}>
<RegisterForm branches={branches} />
</div>
);
}

View File

@@ -0,0 +1,102 @@
.container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #1e1e2f 0%, #2a2a40 100%);
padding: 2rem;
}
.card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 3rem;
border-radius: 20px;
width: 100%;
max-width: 500px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
color: white;
}
.title {
text-align: center;
font-size: 2rem;
margin-bottom: 0.5rem;
background: linear-gradient(90deg, #ff8a00, #e52e71);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 700;
}
.subtitle {
text-align: center;
color: #aaa;
margin-bottom: 2rem;
}
.formGroup {
margin-bottom: 1.5rem;
}
.label {
display: block;
margin-bottom: 0.5rem;
color: #ddd;
font-size: 0.9rem;
letter-spacing: 0.5px;
}
.input,
.select {
width: 100%;
padding: 1rem;
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: white;
font-size: 1rem;
transition: all 0.3s ease;
}
.input:focus,
.select:focus {
outline: none;
border-color: #e52e71;
background: rgba(0, 0, 0, 0.4);
}
.button {
width: 100%;
padding: 1rem;
background: linear-gradient(90deg, #ff8a00, #e52e71);
border: none;
border-radius: 10px;
color: white;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
margin-top: 1rem;
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(229, 46, 113, 0.3);
}
.success {
text-align: center;
color: #4caf50;
background: rgba(76, 175, 80, 0.1);
padding: 1rem;
border-radius: 10px;
margin-bottom: 1rem;
border: 1px solid rgba(76, 175, 80, 0.3);
}
.error {
text-align: center;
color: #ff5252;
margin-bottom: 1rem;
}