feat: Implement a weekly schedule calendar on the homepage and extend branch management with hall count and phone fields.
This commit is contained in:
BIN
prisma/dev.db
BIN
prisma/dev.db
Binary file not shown.
@@ -19,6 +19,7 @@ model Branch {
|
|||||||
name String
|
name String
|
||||||
address String?
|
address String?
|
||||||
phone String?
|
phone String?
|
||||||
|
hallCount Int? @default(1)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
classes DanceClass[]
|
classes DanceClass[]
|
||||||
|
|||||||
@@ -6,16 +6,20 @@ import styles from './branches.module.css';
|
|||||||
export function AddBranchForm() {
|
export function AddBranchForm() {
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [address, setAddress] = useState('');
|
const [address, setAddress] = useState('');
|
||||||
|
const [phone, setPhone] = useState('');
|
||||||
|
const [hallCount, setHallCount] = useState('1');
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await fetch('/api/branches', {
|
await fetch('/api/branches', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ name, address }),
|
body: JSON.stringify({ name, address, phone, hallCount: parseInt(hallCount) }),
|
||||||
});
|
});
|
||||||
setName('');
|
setName('');
|
||||||
setAddress('');
|
setAddress('');
|
||||||
|
setPhone('');
|
||||||
|
setHallCount('1');
|
||||||
router.refresh();
|
router.refresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -25,10 +29,18 @@ export function AddBranchForm() {
|
|||||||
<label>Şube Adı</label>
|
<label>Şube Adı</label>
|
||||||
<input value={name} onChange={e => setName(e.target.value)} className={styles.input} required />
|
<input value={name} onChange={e => setName(e.target.value)} className={styles.input} required />
|
||||||
</div>
|
</div>
|
||||||
|
<div className={styles.inputGroup}>
|
||||||
|
<label>Telefon</label>
|
||||||
|
<input value={phone} onChange={e => setPhone(e.target.value)} className={styles.input} />
|
||||||
|
</div>
|
||||||
<div className={styles.inputGroup}>
|
<div className={styles.inputGroup}>
|
||||||
<label>Adres</label>
|
<label>Adres</label>
|
||||||
<input value={address} onChange={e => setAddress(e.target.value)} className={styles.input} />
|
<input value={address} onChange={e => setAddress(e.target.value)} className={styles.input} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className={styles.inputGroup}>
|
||||||
|
<label>Salon Sayısı</label>
|
||||||
|
<input type="number" min="1" value={hallCount} onChange={e => setHallCount(e.target.value)} className={styles.input} />
|
||||||
|
</div>
|
||||||
<button type="submit" className={styles.btn}>Ekle</button>
|
<button type="submit" className={styles.btn}>Ekle</button>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ export function BranchList({ branches }: { branches: any[] }) {
|
|||||||
<div key={b.id} className={styles.card}>
|
<div key={b.id} className={styles.card}>
|
||||||
<div>
|
<div>
|
||||||
<h3>{b.name}</h3>
|
<h3>{b.name}</h3>
|
||||||
<p>{b.address}</p>
|
<p>{b.phone ? `Tel: ${b.phone}` : 'Telefon yok'} | {b.address || 'Adres yok'}</p>
|
||||||
|
<small>Salon Sayısı: {b.hallCount || 1}</small>
|
||||||
</div>
|
</div>
|
||||||
<button onClick={() => handleDelete(b.id)} className={`${styles.btn} ${styles.btnDelete}`}>Sil</button>
|
<button onClick={() => handleDelete(b.id)} className={`${styles.btn} ${styles.btnDelete}`}>Sil</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,7 +25,10 @@ export async function PUT(
|
|||||||
const json = await request.json();
|
const json = await request.json();
|
||||||
const branch = await prisma.branch.update({
|
const branch = await prisma.branch.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: json,
|
data: {
|
||||||
|
...json,
|
||||||
|
hallCount: json.hallCount ? parseInt(json.hallCount) : undefined
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return NextResponse.json(branch);
|
return NextResponse.json(branch);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ export async function POST(request: Request) {
|
|||||||
data: {
|
data: {
|
||||||
name: json.name,
|
name: json.name,
|
||||||
address: json.address,
|
address: json.address,
|
||||||
phone: json.phone
|
phone: json.phone,
|
||||||
|
hallCount: json.hallCount ? parseInt(json.hallCount) : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return NextResponse.json(branch);
|
return NextResponse.json(branch);
|
||||||
|
|||||||
@@ -1,141 +1,101 @@
|
|||||||
.page {
|
.page {
|
||||||
--background: #fafafa;
|
--primary: #b21f1f;
|
||||||
--foreground: #fff;
|
--secondary: #1a2a6c;
|
||||||
|
--accent: #fdbb2d;
|
||||||
|
--bg-gradient: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
--white: #ffffff;
|
||||||
|
--text: #333333;
|
||||||
|
--text-muted: #666666;
|
||||||
|
|
||||||
--text-primary: #000;
|
|
||||||
--text-secondary: #666;
|
|
||||||
|
|
||||||
--button-primary-hover: #383838;
|
|
||||||
--button-secondary-hover: #f2f2f2;
|
|
||||||
--button-secondary-border: #ebebeb;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
align-items: center;
|
display: flex;
|
||||||
justify-content: center;
|
flex-direction: column;
|
||||||
font-family: var(--font-geist-sans);
|
background: var(--bg-gradient);
|
||||||
background-color: var(--background);
|
font-family: var(--font-outfit, 'Outfit', sans-serif);
|
||||||
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.nav {
|
||||||
|
padding: 1.5rem 4rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 100vh;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 800px;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
background-color: var(--foreground);
|
align-items: center;
|
||||||
padding: 120px 60px;
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.intro {
|
.logo {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 800;
|
||||||
|
background: linear-gradient(to right, var(--secondary), var(--primary));
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adminLink {
|
||||||
|
padding: 0.6rem 1.2rem;
|
||||||
|
background: var(--secondary);
|
||||||
|
color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adminLink:hover {
|
||||||
|
background: var(--primary);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
flex: 1;
|
||||||
|
padding: 4rem 2rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
gap: 3rem;
|
||||||
text-align: left;
|
|
||||||
gap: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.intro h1 {
|
.hero {
|
||||||
max-width: 320px;
|
text-align: center;
|
||||||
font-size: 40px;
|
max-width: 800px;
|
||||||
font-weight: 600;
|
margin: 0 auto;
|
||||||
line-height: 48px;
|
|
||||||
letter-spacing: -2.4px;
|
|
||||||
text-wrap: balance;
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.intro p {
|
.hero h1 {
|
||||||
max-width: 440px;
|
font-size: 3.5rem;
|
||||||
font-size: 18px;
|
font-weight: 900;
|
||||||
line-height: 32px;
|
margin-bottom: 1rem;
|
||||||
text-wrap: balance;
|
color: #111;
|
||||||
color: var(--text-secondary);
|
letter-spacing: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.intro a {
|
.hero p {
|
||||||
font-weight: 500;
|
font-size: 1.25rem;
|
||||||
color: var(--text-primary);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctas {
|
.footer {
|
||||||
display: flex;
|
padding: 2rem;
|
||||||
flex-direction: row;
|
text-align: center;
|
||||||
width: 100%;
|
background: rgba(255, 255, 255, 0.5);
|
||||||
max-width: 440px;
|
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
gap: 16px;
|
color: var(--text-muted);
|
||||||
font-size: 14px;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctas a {
|
@media (max-width: 768px) {
|
||||||
display: flex;
|
.nav {
|
||||||
justify-content: center;
|
padding: 1rem 2rem;
|
||||||
align-items: center;
|
|
||||||
height: 40px;
|
|
||||||
padding: 0 16px;
|
|
||||||
border-radius: 128px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
transition: 0.2s;
|
|
||||||
cursor: pointer;
|
|
||||||
width: fit-content;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.primary {
|
.hero h1 {
|
||||||
background: var(--text-primary);
|
font-size: 2.5rem;
|
||||||
color: var(--background);
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.secondary {
|
|
||||||
border-color: var(--button-secondary-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enable hover only on non-touch devices */
|
|
||||||
@media (hover: hover) and (pointer: fine) {
|
|
||||||
a.primary:hover {
|
|
||||||
background: var(--button-primary-hover);
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.secondary:hover {
|
|
||||||
background: var(--button-secondary-hover);
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.main {
|
|
||||||
padding: 48px 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.intro {
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.intro h1 {
|
|
||||||
font-size: 32px;
|
|
||||||
line-height: 40px;
|
|
||||||
letter-spacing: -1.92px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.logo {
|
|
||||||
filter: invert();
|
|
||||||
}
|
|
||||||
|
|
||||||
.page {
|
|
||||||
--background: #000;
|
|
||||||
--foreground: #000;
|
|
||||||
|
|
||||||
--text-primary: #ededed;
|
|
||||||
--text-secondary: #999;
|
|
||||||
|
|
||||||
--button-primary-hover: #ccc;
|
|
||||||
--button-secondary-hover: #1a1a1a;
|
|
||||||
--button-secondary-border: #1a1a1a;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
105
src/app/page.tsx
105
src/app/page.tsx
@@ -1,66 +1,57 @@
|
|||||||
import Image from "next/image";
|
import { prisma } from "@/infrastructure/db/prisma";
|
||||||
|
import { ScheduleCalendar } from "@/components/ScheduleCalendar";
|
||||||
|
import Link from "next/link";
|
||||||
import styles from "./page.module.css";
|
import styles from "./page.module.css";
|
||||||
|
|
||||||
export default function Home() {
|
export default async function Home() {
|
||||||
|
// Calculate start and end of current week
|
||||||
|
const now = new Date();
|
||||||
|
const dayOfWeek = now.getDay(); // 0 is Sunday
|
||||||
|
const diff = now.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1); // Adjust to Monday
|
||||||
|
const startOfWeek = new Date(now.setDate(diff));
|
||||||
|
startOfWeek.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const endOfWeek = new Date(startOfWeek);
|
||||||
|
endOfWeek.setDate(endOfWeek.getDate() + 7);
|
||||||
|
|
||||||
|
const lessons = await prisma.lesson.findMany({
|
||||||
|
where: {
|
||||||
|
startTime: {
|
||||||
|
gte: startOfWeek,
|
||||||
|
lt: endOfWeek,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
instructor: true,
|
||||||
|
branch: true,
|
||||||
|
class: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
startTime: 'asc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.page}>
|
<div className={styles.page}>
|
||||||
|
<nav className={styles.nav}>
|
||||||
|
<div className={styles.logo}>Dance School</div>
|
||||||
|
<Link href="/admin/dashboard" className={styles.adminLink}>
|
||||||
|
Yönetici Paneli
|
||||||
|
</Link>
|
||||||
|
</nav>
|
||||||
|
|
||||||
<main className={styles.main}>
|
<main className={styles.main}>
|
||||||
<Image
|
<section className={styles.hero}>
|
||||||
className={styles.logo}
|
<h1>Dansın Ritmini Keşfedin</h1>
|
||||||
src="/next.svg"
|
<p>Haftalık kurs programımızı aşağıdan takip edebilirsiniz.</p>
|
||||||
alt="Next.js logo"
|
</section>
|
||||||
width={100}
|
|
||||||
height={20}
|
<ScheduleCalendar lessons={lessons} />
|
||||||
priority
|
|
||||||
/>
|
|
||||||
<div className={styles.intro}>
|
|
||||||
<h1>To get started, edit the page.tsx file.</h1>
|
|
||||||
<p>
|
|
||||||
Looking for a starting point or more instructions? Head over to{" "}
|
|
||||||
<a
|
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Templates
|
|
||||||
</a>{" "}
|
|
||||||
or the{" "}
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Learning
|
|
||||||
</a>{" "}
|
|
||||||
center.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className={styles.ctas}>
|
|
||||||
<a
|
|
||||||
className={styles.primary}
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
className={styles.logo}
|
|
||||||
src="/vercel.svg"
|
|
||||||
alt="Vercel logomark"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Deploy Now
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className={styles.secondary}
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Documentation
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<footer className={styles.footer}>
|
||||||
|
<p>© {new Date().getFullYear()} Dance School Yönetim Sistemi. Tüm hakları saklıdır.</p>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
93
src/components/ScheduleCalendar.module.css
Normal file
93
src/components/ScheduleCalendar.module.css
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
.container {
|
||||||
|
padding: 2rem;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
font-family: var(--font-outfit, 'Outfit', sans-serif);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(7, 1fr);
|
||||||
|
gap: 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dayColumn {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dayHeader {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem;
|
||||||
|
background: #333;
|
||||||
|
color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lessonCard {
|
||||||
|
background: white;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||||
|
border-left: 4px solid #b21f1f;
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lessonCard:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lessonTime {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #666;
|
||||||
|
font-weight: 500;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lessonName {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lessonDetails {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
color: #aaa;
|
||||||
|
font-style: italic;
|
||||||
|
grid-column: span 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.calendar {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
80
src/components/ScheduleCalendar.tsx
Normal file
80
src/components/ScheduleCalendar.tsx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
'use client';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import styles from './ScheduleCalendar.module.css';
|
||||||
|
|
||||||
|
interface Lesson {
|
||||||
|
id: string;
|
||||||
|
name?: string | null;
|
||||||
|
startTime: Date | string;
|
||||||
|
endTime: Date | string;
|
||||||
|
type: string;
|
||||||
|
class?: { name: string } | null;
|
||||||
|
instructor: { name: string };
|
||||||
|
branch: { name: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ScheduleCalendar({ lessons }: { lessons: Lesson[] }) {
|
||||||
|
const days = ['Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi', 'Pazar'];
|
||||||
|
|
||||||
|
// Group lessons by day
|
||||||
|
const groupedLessons = useMemo(() => {
|
||||||
|
const groups: Record<number, Lesson[]> = {};
|
||||||
|
lessons.forEach(lesson => {
|
||||||
|
const date = new Date(lesson.startTime);
|
||||||
|
// JS getDay() returns 0 for Sunday, 1 for Monday etc.
|
||||||
|
// We want 0 for Monday, 6 for Sunday
|
||||||
|
let dayIndex = date.getDay() - 1;
|
||||||
|
if (dayIndex === -1) dayIndex = 6;
|
||||||
|
|
||||||
|
if (!groups[dayIndex]) groups[dayIndex] = [];
|
||||||
|
groups[dayIndex].push(lesson);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort lessons by time in each day
|
||||||
|
Object.keys(groups).forEach(day => {
|
||||||
|
groups[parseInt(day)].sort((a, b) =>
|
||||||
|
new Date(a.startTime).getTime() - new Date(b.startTime).getTime()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}, [lessons]);
|
||||||
|
|
||||||
|
const formatTime = (dateStr: string | Date) => {
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
return date.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit', hour12: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<h1 className={styles.title}>Haftalık Ders Programı</h1>
|
||||||
|
<div className={styles.calendar}>
|
||||||
|
{days.map((day, index) => (
|
||||||
|
<div key={day} className={styles.dayColumn}>
|
||||||
|
<div className={styles.dayHeader}>{day}</div>
|
||||||
|
{groupedLessons[index]?.map(lesson => (
|
||||||
|
<div key={lesson.id} className={styles.lessonCard}>
|
||||||
|
<span className={styles.lessonTime}>
|
||||||
|
{formatTime(lesson.startTime)} - {formatTime(lesson.endTime)}
|
||||||
|
</span>
|
||||||
|
<div className={styles.lessonName}>
|
||||||
|
{lesson.class?.name || lesson.name || 'İsimsiz Ders'}
|
||||||
|
</div>
|
||||||
|
<div className={styles.lessonDetails}>
|
||||||
|
<div> Eğitmen: {lesson.instructor.name}</div>
|
||||||
|
<div> Şube: {lesson.branch.name}</div>
|
||||||
|
<div> Tür: {lesson.type}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{(!groupedLessons[index] || groupedLessons[index].length === 0) && (
|
||||||
|
<div style={{ textAlign: 'center', padding: '1rem', color: '#ccc', fontSize: '0.8rem' }}>
|
||||||
|
Ders yok
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user