Student Registeration CRUD & Listing
This commit is contained in:
BIN
prisma/dev.db
BIN
prisma/dev.db
Binary file not shown.
@@ -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
|
||||
}
|
||||
|
||||
28
src/app/api/register/route.ts
Normal file
28
src/app/api/register/route.ts
Normal 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 });
|
||||
}
|
||||
}
|
||||
78
src/app/register/RegisterForm.tsx
Normal file
78
src/app/register/RegisterForm.tsx
Normal 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
15
src/app/register/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
102
src/app/register/register.module.css
Normal file
102
src/app/register/register.module.css
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user