init
This commit is contained in:
parent
22f85d731c
commit
0d339a35e2
@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
### Phase 1: Project Setup & Foundation
|
### Phase 1: Project Setup & Foundation
|
||||||
|
|
||||||
- [ ] Initialize Next.js 16+ project with TypeScript
|
- [x] Initialize Next.js 16+ project with TypeScript
|
||||||
- [ ] Install and configure Better Auth framework
|
- [x] Install and configure Better Auth framework
|
||||||
- [ ] Set up Prisma ORM with database connection
|
- [x] Set up Prisma ORM with database connection
|
||||||
- [ ] Configure project structure following AI_GUIDE.md patterns
|
- [ ] Configure project structure following AI_GUIDE.md patterns
|
||||||
- [ ] Set up ESLint, TypeScript configuration
|
- [ ] Set up ESLint, TypeScript configuration
|
||||||
- [ ] Create basic layout and routing structure
|
- [ ] Create basic layout and routing structure
|
||||||
|
|||||||
170
app/(app)/dashboard/layout.tsx
Normal file
170
app/(app)/dashboard/layout.tsx
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/**
|
||||||
|
* File: layout.tsx
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Dashboard layout for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export default function DashboardLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-navy-900 via-navy-800 to-gray-900">
|
||||||
|
{/* Background overlay */}
|
||||||
|
<div className="absolute inset-0 bg-black/20"></div>
|
||||||
|
|
||||||
|
<div className="relative z-10 flex min-h-screen">
|
||||||
|
{/* Sidebar */}
|
||||||
|
<div className="w-64 bg-navy-900/50 backdrop-blur-sm border-r border-white/10">
|
||||||
|
<div className="p-6">
|
||||||
|
{/* Logo */}
|
||||||
|
<Link href="/dashboard" className="flex items-center space-x-3">
|
||||||
|
<div className="w-8 h-8 bg-gold-400 rounded-lg flex items-center justify-center">
|
||||||
|
<span className="text-navy-900 font-bold text-sm">kV</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-white font-bold text-xl">
|
||||||
|
kreati<span className="text-gold-400">Vortex</span>
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Navigation */}
|
||||||
|
<nav className="px-4 pb-6">
|
||||||
|
<ul className="space-y-2">
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="/dashboard"
|
||||||
|
className="flex items-center space-x-3 px-4 py-3 text-gray-300 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 00-1-1v-4a1 1 0 011-1h2m4 0a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||||
|
</svg>
|
||||||
|
<span>Beranda</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="/dashboard/teori"
|
||||||
|
className="flex items-center space-x-3 px-4 py-3 text-gray-300 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
||||||
|
</svg>
|
||||||
|
<span>Teori</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="/dashboard/praktik"
|
||||||
|
className="flex items-center space-x-3 px-4 py-3 text-gray-300 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
<span>Praktik</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="/dashboard/template-makalah"
|
||||||
|
className="flex items-center space-x-3 px-4 py-3 text-gray-300 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
|
</svg>
|
||||||
|
<span>Template Makalah</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Additional sections */}
|
||||||
|
<div className="mt-8">
|
||||||
|
<h3 className="px-4 text-xs font-semibold text-gray-400 uppercase tracking-wider mb-4">
|
||||||
|
Komunitas
|
||||||
|
</h3>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="/dashboard/videos"
|
||||||
|
className="flex items-center space-x-3 px-4 py-3 text-gray-300 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
<span>Video Saya</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="/dashboard/forum"
|
||||||
|
className="flex items-center space-x-3 px-4 py-3 text-gray-300 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z" />
|
||||||
|
</svg>
|
||||||
|
<span>Forum</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href="/dashboard/assignments"
|
||||||
|
className="flex items-center space-x-3 px-4 py-3 text-gray-300 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2v2a2 2 0 01-2 2M9 5a2 2 0 012-2v2a2 2 0 01-2 2m-3 7h8m-8 4h8m-8 4h8" />
|
||||||
|
</svg>
|
||||||
|
<span>Tugas</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<div className="flex-1 overflow-auto">
|
||||||
|
{/* Top bar */}
|
||||||
|
<header className="bg-navy-800/50 backdrop-blur-sm border-b border-white/10 px-6 py-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h1 className="text-xl font-semibold text-white">
|
||||||
|
Dashboard
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
{/* Notifications */}
|
||||||
|
<button className="relative p-2 text-gray-300 hover:text-white transition-colors">
|
||||||
|
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 10v6.159c0 .538.214 1.055.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||||
|
</svg>
|
||||||
|
<span className="absolute top-1 right-1 w-2 h-2 bg-gold-400 rounded-full"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Profile */}
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="text-right">
|
||||||
|
<p className="text-sm font-medium text-white">John Doe</p>
|
||||||
|
<p className="text-xs text-gray-400">Calon Pendidik</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-10 h-10 bg-gold-400 rounded-full flex items-center justify-center">
|
||||||
|
<span className="text-navy-900 font-semibold">JD</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Page content */}
|
||||||
|
<main className="p-6">
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
130
app/(app)/dashboard/page.tsx
Normal file
130
app/(app)/dashboard/page.tsx
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* File: page.tsx
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Dashboard page for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function Dashboard() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="mb-8">
|
||||||
|
<h2 className="text-2xl font-bold text-white mb-2">
|
||||||
|
Selamat Datang di kreatiVortex!
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-300">
|
||||||
|
Platform pembelajaran tari tradisional Indonesia
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Stats Cards */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||||
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-300">Total Video</p>
|
||||||
|
<p className="text-2xl font-bold text-white">24</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-12 h-12 bg-gold-400/20 rounded-lg flex items-center justify-center">
|
||||||
|
<svg className="w-6 h-6 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-300">Kelas Aktif</p>
|
||||||
|
<p className="text-2xl font-bold text-white">3</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-12 h-12 bg-gold-400/20 rounded-lg flex items-center justify-center">
|
||||||
|
<svg className="w-6 h-6 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-300">Tugas Selesai</p>
|
||||||
|
<p className="text-2xl font-bold text-white">12</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-12 h-12 bg-gold-400/20 rounded-lg flex items-center justify-center">
|
||||||
|
<svg className="w-6 h-6 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm7-2a2 2 0 11-4 0v4a2 2 0 014 0V9z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-300">Forum Post</p>
|
||||||
|
<p className="text-2xl font-bold text-white">48</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-12 h-12 bg-gold-400/20 rounded-lg flex items-center justify-center">
|
||||||
|
<svg className="w-6 h-6 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Recent Activity */}
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
|
{/* Recent Videos */}
|
||||||
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20">
|
||||||
|
<h3 className="text-lg font-semibold text-white mb-4">Video Terbaru</h3>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="w-16 h-12 bg-gold-400/20 rounded-lg flex items-center justify-center">
|
||||||
|
<svg className="w-6 h-6 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="text-white font-medium">Tari Piring - Dasar</h4>
|
||||||
|
<p className="text-sm text-gray-400">2 jam yang lalu</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="w-16 h-12 bg-gold-400/20 rounded-lg flex items-center justify-center">
|
||||||
|
<svg className="w-6 h-6 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="text-white font-medium">Tari Saman - Gerakan Tangan</h4>
|
||||||
|
<p className="text-sm text-gray-400">5 jam yang lalu</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Recent Forum Posts */}
|
||||||
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20">
|
||||||
|
<h3 className="text-lg font-semibold text-white mb-4">Diskusi Terbaru</h3>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="border-l border-gold-400 pl-4">
|
||||||
|
<h4 className="text-white font-medium mb-1">Tips untuk pemula Tari Piring</h4>
|
||||||
|
<p className="text-sm text-gray-400 mb-2">Saya ingin berbagi beberapa tips untuk yang baru mulai belajar...</p>
|
||||||
|
<p className="text-xs text-gray-500">Oleh Sarah Pendidik • 1 jam yang lalu</p>
|
||||||
|
</div>
|
||||||
|
<div className="border-l border-gold-400 pl-4">
|
||||||
|
<h4 className="text-white font-medium mb-1">Costum untuk pertunjukan</h4>
|
||||||
|
<p className="text-sm text-gray-400 mb-2">Apakah ada saran untuk costum yang tepat untuk pertunjukan tari...</p>
|
||||||
|
<p className="text-xs text-gray-500">Oleh Budi Student • 3 jam yang lalu</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
17
app/api/auth/[...all]/route.ts
Normal file
17
app/api/auth/[...all]/route.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* File: route.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Basic auth API for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
return NextResponse.json({ message: 'Auth API - GET' });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST() {
|
||||||
|
return NextResponse.json({ message: 'Auth API - POST' });
|
||||||
|
}
|
||||||
116
app/auth/signin/page.tsx
Normal file
116
app/auth/signin/page.tsx
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* File: page.tsx
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Sign in page for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
export default function SignIn() {
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
// TODO: Implement actual sign in logic
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
router.push('/dashboard');
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-navy-900 via-navy-800 to-gray-900 flex items-center justify-center px-4">
|
||||||
|
<div className="absolute inset-0 bg-black/20"></div>
|
||||||
|
|
||||||
|
<div className="relative z-10 w-full max-w-md">
|
||||||
|
<div className="bg-white/10 backdrop-blur-lg rounded-2xl p-8 border border-white/20">
|
||||||
|
{/* Logo */}
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<h1 className="text-3xl font-bold text-white mb-2">
|
||||||
|
kreati<span className="text-gold-400">Vortex</span>
|
||||||
|
</h1>
|
||||||
|
<p className="text-gray-300">Masuk ke akun Anda</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Form */}
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<label htmlFor="email" className="block text-sm font-medium text-gray-300 mb-2">
|
||||||
|
Email
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-gold-400 focus:border-transparent"
|
||||||
|
placeholder="nama@email.com"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="password" className="block text-sm font-medium text-gray-300 mb-2">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-gold-400 focus:border-transparent"
|
||||||
|
placeholder="••••••••"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<input
|
||||||
|
id="remember"
|
||||||
|
type="checkbox"
|
||||||
|
className="h-4 w-4 bg-white/10 border-white/20 rounded focus:ring-gold-400"
|
||||||
|
/>
|
||||||
|
<label htmlFor="remember" className="ml-2 block text-sm text-gray-300">
|
||||||
|
Ingat saya
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<Link href="/auth/forgot-password" className="text-sm text-gold-400 hover:text-gold-300">
|
||||||
|
Lupa password?
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={isLoading}
|
||||||
|
className="w-full py-3 px-4 bg-gold-500 hover:bg-gold-400 text-navy-900 font-semibold rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
{isLoading ? 'Memproses...' : 'Masuk'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{/* Sign up link */}
|
||||||
|
<div className="mt-8 text-center">
|
||||||
|
<p className="text-gray-300">
|
||||||
|
Belum punya akun?{' '}
|
||||||
|
<Link href="/auth/signup" className="text-gold-400 hover:text-gold-300 font-medium">
|
||||||
|
Daftar sekarang
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
168
app/auth/signup/page.tsx
Normal file
168
app/auth/signup/page.tsx
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/**
|
||||||
|
* File: page.tsx
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Sign up page for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
export default function SignUp() {
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
role: 'CALON_PENDIDIK' as 'CALON_PENDIDIK' | 'UMUM',
|
||||||
|
});
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (formData.password !== formData.confirmPassword) {
|
||||||
|
alert('Password tidak cocok!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
// TODO: Implement actual sign up logic
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
router.push('/auth/signin');
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
[e.target.name]: e.target.value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-navy-900 via-navy-800 to-gray-900 flex items-center justify-center px-4 py-12">
|
||||||
|
<div className="absolute inset-0 bg-black/20"></div>
|
||||||
|
|
||||||
|
<div className="relative z-10 w-full max-w-md">
|
||||||
|
<div className="bg-white/10 backdrop-blur-lg rounded-2xl p-8 border border-white/20">
|
||||||
|
{/* Logo */}
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<h1 className="text-3xl font-bold text-white mb-2">
|
||||||
|
kreati<span className="text-gold-400">Vortex</span>
|
||||||
|
</h1>
|
||||||
|
<p className="text-gray-300">Buat akun baru</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Form */}
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<label htmlFor="name" className="block text-sm font-medium text-gray-300 mb-2">
|
||||||
|
Nama Lengkap
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="name"
|
||||||
|
name="name"
|
||||||
|
type="text"
|
||||||
|
value={formData.name}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-gold-400 focus:border-transparent"
|
||||||
|
placeholder="John Doe"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="email" className="block text-sm font-medium text-gray-300 mb-2">
|
||||||
|
Email
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
type="email"
|
||||||
|
value={formData.email}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-gold-400 focus:border-transparent"
|
||||||
|
placeholder="nama@email.com"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="role" className="block text-sm font-medium text-gray-300 mb-2">
|
||||||
|
Daftar sebagai
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="role"
|
||||||
|
name="role"
|
||||||
|
value={formData.role}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-gold-400 focus:border-transparent"
|
||||||
|
>
|
||||||
|
<option value="CALON_PENDIDIK" className="bg-navy-800">Calon Pendidik</option>
|
||||||
|
<option value="UMUM" className="bg-navy-800">Umum</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="password" className="block text-sm font-medium text-gray-300 mb-2">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
value={formData.password}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-gold-400 focus:border-transparent"
|
||||||
|
placeholder="••••••••"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-300 mb-2">
|
||||||
|
Konfirmasi Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="confirmPassword"
|
||||||
|
name="confirmPassword"
|
||||||
|
type="password"
|
||||||
|
value={formData.confirmPassword}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-gold-400 focus:border-transparent"
|
||||||
|
placeholder="••••••••"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={isLoading}
|
||||||
|
className="w-full py-3 px-4 bg-gold-500 hover:bg-gold-400 text-navy-900 font-semibold rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
{isLoading ? 'Mendaftar...' : 'Daftar'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{/* Sign in link */}
|
||||||
|
<div className="mt-8 text-center">
|
||||||
|
<p className="text-gray-300">
|
||||||
|
Sudah punya akun?{' '}
|
||||||
|
<Link href="/auth/signin" className="text-gold-400 hover:text-gold-300 font-medium">
|
||||||
|
Masuk
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -41,6 +41,28 @@
|
|||||||
--radius-md: calc(var(--radius) - 2px);
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
|
||||||
|
/* kreatiVortex custom colors */
|
||||||
|
--color-navy-50: #f0f4ff;
|
||||||
|
--color-navy-100: #dae9ff;
|
||||||
|
--color-navy-200: #bdd7ff;
|
||||||
|
--color-navy-300: #8fc2ff;
|
||||||
|
--color-navy-400: #5ca7ff;
|
||||||
|
--color-navy-500: #3182ff;
|
||||||
|
--color-navy-600: #1e5fd6;
|
||||||
|
--color-navy-700: #1e40af;
|
||||||
|
--color-navy-800: #1e3a8a;
|
||||||
|
--color-navy-900: #000080;
|
||||||
|
--color-gold-50: #fffbeb;
|
||||||
|
--color-gold-100: #fef3c7;
|
||||||
|
--color-gold-200: #fde68a;
|
||||||
|
--color-gold-300: #fcd34d;
|
||||||
|
--color-gold-400: #fbbf24;
|
||||||
|
--color-gold-500: #f59e0b;
|
||||||
|
--color-gold-600: #d97706;
|
||||||
|
--color-gold-700: #b45309;
|
||||||
|
--color-gold-800: #92400e;
|
||||||
|
--color-gold-900: #78350f;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
|||||||
143
app/page.tsx
143
app/page.tsx
@ -1,65 +1,96 @@
|
|||||||
import Image from "next/image";
|
/**
|
||||||
|
* File: page.tsx
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Landing page for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
<div className="min-h-screen bg-gradient-to-br from-navy-900 via-navy-800 to-gray-900 text-white">
|
||||||
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
|
{/* Background overlay effect */}
|
||||||
<Image
|
<div className="absolute inset-0 bg-black/20"></div>
|
||||||
className="dark:invert"
|
|
||||||
src="/next.svg"
|
{/* Main content */}
|
||||||
alt="Next.js logo"
|
<div className="relative z-10 flex min-h-screen flex-col items-center justify-center px-4">
|
||||||
width={100}
|
<div className="max-w-4xl w-full text-center">
|
||||||
height={20}
|
{/* Logo/Title */}
|
||||||
priority
|
<div className="mb-12">
|
||||||
/>
|
<h1 className="text-5xl md:text-7xl font-bold text-white mb-4">
|
||||||
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
|
kreati<span className="text-gold-400">Vortex</span>
|
||||||
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
</h1>
|
||||||
To get started, edit the page.tsx file.
|
<p className="text-xl md:text-2xl text-gray-300">
|
||||||
</h1>
|
Platform Pembelajaran Tari Online
|
||||||
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
</p>
|
||||||
Looking for a starting point or more instructions? Head over to{" "}
|
</div>
|
||||||
<a
|
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
{/* Description */}
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
<div className="mb-16 max-w-2xl mx-auto">
|
||||||
|
<p className="text-lg text-gray-300 leading-relaxed">
|
||||||
|
Bergabunglah dengan komunitas pembelajaran tari tradisional Indonesia.
|
||||||
|
Pelajari berbagai tarian dari seluruh nusantara dengan panduan dari para ahli.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Action Buttons */}
|
||||||
|
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
||||||
|
<Link
|
||||||
|
href="/auth/signin"
|
||||||
|
className="px-8 py-4 bg-navy-700 hover:bg-navy-600 text-white font-semibold rounded-lg transition-colors duration-200 min-w-[160px] text-center"
|
||||||
>
|
>
|
||||||
Templates
|
Masuk
|
||||||
</a>{" "}
|
</Link>
|
||||||
or the{" "}
|
<Link
|
||||||
<a
|
href="/auth/signup"
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
className="px-8 py-4 bg-gold-500 hover:bg-gold-400 text-navy-900 font-semibold rounded-lg transition-colors duration-200 min-w-[160px] text-center"
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
|
||||||
>
|
>
|
||||||
Learning
|
Daftar
|
||||||
</a>{" "}
|
</Link>
|
||||||
center.
|
<Link
|
||||||
</p>
|
href="/auth/signup"
|
||||||
|
className="px-8 py-4 border-2 border-gold-400 text-gold-400 hover:bg-gold-400 hover:text-navy-900 font-semibold rounded-lg transition-colors duration-200 min-w-[160px] text-center"
|
||||||
|
>
|
||||||
|
Mulai Sekarang
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Features */}
|
||||||
|
<div className="mt-24 grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="w-16 h-16 bg-gold-400/20 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||||
|
<svg className="w-8 h-8 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-semibold text-white mb-2">Video Pembelajaran</h3>
|
||||||
|
<p className="text-gray-400">Akses video tutorial tari dari berbagai daerah di Indonesia</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="w-16 h-16 bg-gold-400/20 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||||
|
<svg className="w-8 h-8 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-semibold text-white mb-2">Forum Diskusi</h3>
|
||||||
|
<p className="text-gray-400">Berinteraksi dengan sesama pecinta tari dan para ahli</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="w-16 h-16 bg-gold-400/20 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||||
|
<svg className="w-8 h-8 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-semibold text-white mb-2">Tugas & Evaluasi</h3>
|
||||||
|
<p className="text-gray-400">Kumpulkan tugas dan dapatkan feedback dari para pendidik</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
|
</div>
|
||||||
<a
|
|
||||||
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
className="dark:invert"
|
|
||||||
src="/vercel.svg"
|
|
||||||
alt="Vercel logomark"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Deploy Now
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Documentation
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
11
bun.lock
11
bun.lock
@ -6,7 +6,10 @@
|
|||||||
"name": "kreativortex",
|
"name": "kreativortex",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/adapter-pg": "^7.0.1",
|
"@prisma/adapter-pg": "^7.0.1",
|
||||||
|
"@prisma/client": "^7.0.1",
|
||||||
|
"@types/bcryptjs": "^3.0.0",
|
||||||
"@types/pg": "^8.15.6",
|
"@types/pg": "^8.15.6",
|
||||||
|
"bcryptjs": "^3.0.3",
|
||||||
"better-auth": "^1.4.3",
|
"better-auth": "^1.4.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
@ -272,6 +275,10 @@
|
|||||||
|
|
||||||
"@prisma/adapter-pg": ["@prisma/adapter-pg@7.0.1", "", { "dependencies": { "@prisma/driver-adapter-utils": "7.0.1", "pg": "^8.16.3", "postgres-array": "3.0.4" } }, "sha512-01GpPPhLMoDMF4ipgfZz0L87fla/TV/PBQcmHy+9vV1ml6gUoqF8dUIRNI5Yf2YKpOwzQg9sn8C7dYD1Yio9Ug=="],
|
"@prisma/adapter-pg": ["@prisma/adapter-pg@7.0.1", "", { "dependencies": { "@prisma/driver-adapter-utils": "7.0.1", "pg": "^8.16.3", "postgres-array": "3.0.4" } }, "sha512-01GpPPhLMoDMF4ipgfZz0L87fla/TV/PBQcmHy+9vV1ml6gUoqF8dUIRNI5Yf2YKpOwzQg9sn8C7dYD1Yio9Ug=="],
|
||||||
|
|
||||||
|
"@prisma/client": ["@prisma/client@7.0.1", "", { "dependencies": { "@prisma/client-runtime-utils": "7.0.1" }, "peerDependencies": { "prisma": "*", "typescript": ">=5.4.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-O74T6xcfaGAq5gXwCAvfTLvI6fmC3and2g5yLRMkNjri1K8mSpEgclDNuUWs9xj5AwNEMQ88NeD3asI+sovm1g=="],
|
||||||
|
|
||||||
|
"@prisma/client-runtime-utils": ["@prisma/client-runtime-utils@7.0.1", "", {}, "sha512-R26BVX9D/iw4toUmZKZf3jniM/9pMGHHdZN5LVP2L7HNiCQKNQQx/9LuMtjepbgRqSqQO3oHN0yzojHLnKTGEw=="],
|
||||||
|
|
||||||
"@prisma/config": ["@prisma/config@7.0.1", "", { "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", "effect": "3.18.4", "empathic": "2.0.0" } }, "sha512-MacIjXdo+hNKxPvtMzDXykIIc8HCRWoyjQ2nguJTFqLDzJBD5L6QRaANGTLOqbGtJ3sFvLRmfXhrFg3pWoK1BA=="],
|
"@prisma/config": ["@prisma/config@7.0.1", "", { "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", "effect": "3.18.4", "empathic": "2.0.0" } }, "sha512-MacIjXdo+hNKxPvtMzDXykIIc8HCRWoyjQ2nguJTFqLDzJBD5L6QRaANGTLOqbGtJ3sFvLRmfXhrFg3pWoK1BA=="],
|
||||||
|
|
||||||
"@prisma/debug": ["@prisma/debug@7.0.1", "", {}, "sha512-5+25XokVeAK2Z2C9W457AFw7Hk032Q3QI3G58KYKXPlpgxy+9FvV1+S1jqfJ2d4Nmq9LP/uACrM6OVhpJMSr8w=="],
|
"@prisma/debug": ["@prisma/debug@7.0.1", "", {}, "sha512-5+25XokVeAK2Z2C9W457AFw7Hk032Q3QI3G58KYKXPlpgxy+9FvV1+S1jqfJ2d4Nmq9LP/uACrM6OVhpJMSr8w=="],
|
||||||
@ -330,6 +337,8 @@
|
|||||||
|
|
||||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||||
|
|
||||||
|
"@types/bcryptjs": ["@types/bcryptjs@3.0.0", "", { "dependencies": { "bcryptjs": "*" } }, "sha512-WRZOuCuaz8UcZZE4R5HXTco2goQSI2XxjGY3hbM/xDvwmqFWd4ivooImsMx65OKM6CtNKbnZ5YL+YwAwK7c1dg=="],
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
|
|
||||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||||
@ -446,6 +455,8 @@
|
|||||||
|
|
||||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.32", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw=="],
|
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.32", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw=="],
|
||||||
|
|
||||||
|
"bcryptjs": ["bcryptjs@3.0.3", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g=="],
|
||||||
|
|
||||||
"better-auth": ["better-auth@1.4.3", "", { "dependencies": { "@better-auth/core": "1.4.3", "@better-auth/telemetry": "1.4.3", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "@standard-schema/spec": "^1.0.0", "better-call": "1.1.0", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.1.12" } }, "sha512-cMY6PxXZ9Ep+KmLUcVEQ5RwtZtdawxTbDqUIgIIUYWJgq0KwNkQfFNimSYjHI0cNZwwAJyvbV42+uLogsDOUqQ=="],
|
"better-auth": ["better-auth@1.4.3", "", { "dependencies": { "@better-auth/core": "1.4.3", "@better-auth/telemetry": "1.4.3", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "@standard-schema/spec": "^1.0.0", "better-call": "1.1.0", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.1.12" } }, "sha512-cMY6PxXZ9Ep+KmLUcVEQ5RwtZtdawxTbDqUIgIIUYWJgq0KwNkQfFNimSYjHI0cNZwwAJyvbV42+uLogsDOUqQ=="],
|
||||||
|
|
||||||
"better-call": ["better-call@1.1.0", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1" } }, "sha512-7CecYG+yN8J1uBJni/Mpjryp8bW/YySYsrGEWgFe048ORASjq17keGjbKI2kHEOSc6u8pi11UxzkJ7jIovQw6w=="],
|
"better-call": ["better-call@1.1.0", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1" } }, "sha512-7CecYG+yN8J1uBJni/Mpjryp8bW/YySYsrGEWgFe048ORASjq17keGjbKI2kHEOSc6u8pi11UxzkJ7jIovQw6w=="],
|
||||||
|
|||||||
9
components/ActionButton/index.ts
Normal file
9
components/ActionButton/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* File: index.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: ActionButton component export
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ActionButton component will be created here
|
||||||
9
components/Common/index.ts
Normal file
9
components/Common/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* File: index.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Common components exports
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Common components will be exported here as they are created
|
||||||
9
components/Forms/index.ts
Normal file
9
components/Forms/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* File: index.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Forms components exports
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Form components will be exported here as they are created
|
||||||
9
components/Layouts/index.ts
Normal file
9
components/Layouts/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* File: index.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Layouts components exports
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Layout components will be exported here as they are created
|
||||||
9
components/index.ts
Normal file
9
components/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* File: index.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Main component exports for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Components will be exported here as they are created
|
||||||
13
lib/auth-client.ts
Normal file
13
lib/auth-client.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* File: client.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Client-side auth configuration for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createAuthClient } from "better-auth/react";
|
||||||
|
|
||||||
|
export const authClient = createAuthClient({
|
||||||
|
baseURL: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
|
||||||
|
});
|
||||||
25
lib/auth.ts
25
lib/auth.ts
@ -1,3 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* File: auth.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Better Auth configuration for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
import { betterAuth } from "better-auth";
|
import { betterAuth } from "better-auth";
|
||||||
import { prismaAdapter } from "better-auth/adapters/prisma";
|
import { prismaAdapter } from "better-auth/adapters/prisma";
|
||||||
import { prisma } from "./prisma";
|
import { prisma } from "./prisma";
|
||||||
@ -6,4 +14,21 @@ export const auth = betterAuth({
|
|||||||
database: prismaAdapter(prisma, {
|
database: prismaAdapter(prisma, {
|
||||||
provider: "postgresql",
|
provider: "postgresql",
|
||||||
}),
|
}),
|
||||||
|
emailAndPassword: {
|
||||||
|
enabled: true,
|
||||||
|
requireEmailVerification: false,
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
||||||
|
updateAge: 60 * 60 * 24, // 1 day
|
||||||
|
cookieCache: {
|
||||||
|
enabled: true,
|
||||||
|
maxAge: 5 * 60, // 5 minutes
|
||||||
|
},
|
||||||
|
},
|
||||||
|
account: {
|
||||||
|
accountLinking: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
@ -13,7 +13,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/adapter-pg": "^7.0.1",
|
"@prisma/adapter-pg": "^7.0.1",
|
||||||
|
"@prisma/client": "^7.0.1",
|
||||||
|
"@types/bcryptjs": "^3.0.0",
|
||||||
"@types/pg": "^8.15.6",
|
"@types/pg": "^8.15.6",
|
||||||
|
"bcryptjs": "^3.0.3",
|
||||||
"better-auth": "^1.4.3",
|
"better-auth": "^1.4.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|||||||
@ -5,7 +5,7 @@ export default defineConfig({
|
|||||||
schema: 'prisma',
|
schema: 'prisma',
|
||||||
migrations: {
|
migrations: {
|
||||||
path: 'prisma/migrations',
|
path: 'prisma/migrations',
|
||||||
seed: 'tsx prisma/seed.ts',
|
seed: 'node prisma/seed.js',
|
||||||
},
|
},
|
||||||
datasource: {
|
datasource: {
|
||||||
url: env('DATABASE_URL'),
|
url: env('DATABASE_URL'),
|
||||||
|
|||||||
78
prisma/migrations/20251128173915_init/migration.sql
Normal file
78
prisma/migrations/20251128173915_init/migration.sql
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "user" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"emailVerified" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"image" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "user_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "session" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"token" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"ipAddress" TEXT,
|
||||||
|
"userAgent" TEXT,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "session_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "account" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"accountId" TEXT NOT NULL,
|
||||||
|
"providerId" TEXT NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"accessToken" TEXT,
|
||||||
|
"refreshToken" TEXT,
|
||||||
|
"idToken" TEXT,
|
||||||
|
"accessTokenExpiresAt" TIMESTAMP(3),
|
||||||
|
"refreshTokenExpiresAt" TIMESTAMP(3),
|
||||||
|
"scope" TEXT,
|
||||||
|
"password" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "account_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "verification" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"identifier" TEXT NOT NULL,
|
||||||
|
"value" TEXT NOT NULL,
|
||||||
|
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "verification_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "user_email_key" ON "user"("email");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "session_userId_idx" ON "session"("userId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "session_token_key" ON "session"("token");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "account_userId_idx" ON "account"("userId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "verification_identifier_idx" ON "verification"("identifier");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (e.g., Git)
|
||||||
|
provider = "postgresql"
|
||||||
@ -1,62 +0,0 @@
|
|||||||
model User {
|
|
||||||
id String @id
|
|
||||||
name String
|
|
||||||
email String
|
|
||||||
emailVerified Boolean @default(false)
|
|
||||||
image String?
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
sessions Session[]
|
|
||||||
accounts Account[]
|
|
||||||
|
|
||||||
@@unique([email])
|
|
||||||
@@map("user")
|
|
||||||
}
|
|
||||||
|
|
||||||
model Session {
|
|
||||||
id String @id
|
|
||||||
expiresAt DateTime
|
|
||||||
token String
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
ipAddress String?
|
|
||||||
userAgent String?
|
|
||||||
userId String
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
||||||
|
|
||||||
@@unique([token])
|
|
||||||
@@index([userId])
|
|
||||||
@@map("session")
|
|
||||||
}
|
|
||||||
|
|
||||||
model Account {
|
|
||||||
id String @id
|
|
||||||
accountId String
|
|
||||||
providerId String
|
|
||||||
userId String
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
||||||
accessToken String?
|
|
||||||
refreshToken String?
|
|
||||||
idToken String?
|
|
||||||
accessTokenExpiresAt DateTime?
|
|
||||||
refreshTokenExpiresAt DateTime?
|
|
||||||
scope String?
|
|
||||||
password String?
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
@@index([userId])
|
|
||||||
@@map("account")
|
|
||||||
}
|
|
||||||
|
|
||||||
model Verification {
|
|
||||||
id String @id
|
|
||||||
identifier String
|
|
||||||
value String
|
|
||||||
expiresAt DateTime
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
|
|
||||||
@@index([identifier])
|
|
||||||
@@map("verification")
|
|
||||||
}
|
|
||||||
@ -13,3 +13,311 @@ generator client {
|
|||||||
datasource db {
|
datasource db {
|
||||||
provider = "postgresql"
|
provider = "postgresql"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// AUTH MODELS (imported from auth.prisma)
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
// Note: User, Session, Account, and Verification models are imported from auth.prisma
|
||||||
|
// We need to extend the User model here to add the profile relation
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id String @id
|
||||||
|
name String
|
||||||
|
email String
|
||||||
|
emailVerified Boolean @default(false)
|
||||||
|
image String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
sessions Session[]
|
||||||
|
accounts Account[]
|
||||||
|
profile UserProfile?
|
||||||
|
|
||||||
|
@@unique([email])
|
||||||
|
@@map("user")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Session {
|
||||||
|
id String @id
|
||||||
|
expiresAt DateTime
|
||||||
|
token String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
ipAddress String?
|
||||||
|
userAgent String?
|
||||||
|
userId String
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([token])
|
||||||
|
@@index([userId])
|
||||||
|
@@map("session")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Account {
|
||||||
|
id String @id
|
||||||
|
accountId String
|
||||||
|
providerId String
|
||||||
|
userId String
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
accessToken String?
|
||||||
|
refreshToken String?
|
||||||
|
idToken String?
|
||||||
|
accessTokenExpiresAt DateTime?
|
||||||
|
refreshTokenExpiresAt DateTime?
|
||||||
|
scope String?
|
||||||
|
password String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@index([userId])
|
||||||
|
@@map("account")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Verification {
|
||||||
|
id String @id
|
||||||
|
identifier String
|
||||||
|
value String
|
||||||
|
expiresAt DateTime
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@index([identifier])
|
||||||
|
@@map("verification")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// KREATIVORTEX PLATFORM MODELS
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
model UserRole {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String @unique
|
||||||
|
description String
|
||||||
|
permissions String[] // JSON array of permissions
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
users UserProfile[]
|
||||||
|
|
||||||
|
@@map("user_roles")
|
||||||
|
}
|
||||||
|
|
||||||
|
model UserProfile {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
userId String @unique
|
||||||
|
roleId String
|
||||||
|
nim String? // Nomor Induk Mahasiswa
|
||||||
|
phone String?
|
||||||
|
address String?
|
||||||
|
bio String?
|
||||||
|
avatar String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
role UserRole @relation(fields: [roleId], references: [id])
|
||||||
|
|
||||||
|
// Relations as educator
|
||||||
|
classesTeaching Class[]
|
||||||
|
videos Video[]
|
||||||
|
forums Forum[]
|
||||||
|
assignments Assignment[]
|
||||||
|
|
||||||
|
// Relations as student
|
||||||
|
classesEnrolled ClassMember[]
|
||||||
|
forumPosts ForumPost[] @relation("ForumPosts")
|
||||||
|
assignmentSubmissions AssignmentSubmission[]
|
||||||
|
comments Comment[] @relation("CommentAuthor")
|
||||||
|
|
||||||
|
// Additional relations
|
||||||
|
reviewedSubmissions AssignmentSubmission[] @relation("SubmissionReviewer")
|
||||||
|
|
||||||
|
@@map("user_profiles")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Class {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
description String
|
||||||
|
code String @unique
|
||||||
|
educatorId String
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
maxStudents Int?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
createdBy String
|
||||||
|
updatedBy String
|
||||||
|
|
||||||
|
educator UserProfile @relation(fields: [educatorId], references: [id], onDelete: Cascade)
|
||||||
|
members ClassMember[]
|
||||||
|
videos Video[]
|
||||||
|
forums Forum[]
|
||||||
|
assignments Assignment[]
|
||||||
|
|
||||||
|
@@map("classes")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ClassMember {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
classId String
|
||||||
|
studentId String
|
||||||
|
joinedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
|
||||||
|
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
|
||||||
|
student UserProfile @relation(fields: [studentId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([classId, studentId])
|
||||||
|
@@map("class_members")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Video {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
title String
|
||||||
|
description String?
|
||||||
|
videoUrl String
|
||||||
|
videoType VideoType @default(YOUTUBE)
|
||||||
|
thumbnailUrl String?
|
||||||
|
duration Int? // in seconds
|
||||||
|
uploaderId String
|
||||||
|
classId String?
|
||||||
|
isPublic Boolean @default(true)
|
||||||
|
viewCount Int @default(0)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
createdBy String
|
||||||
|
updatedBy String
|
||||||
|
|
||||||
|
uploader UserProfile @relation(fields: [uploaderId], references: [id], onDelete: Cascade)
|
||||||
|
class Class? @relation(fields: [classId], references: [id], onDelete: SetNull)
|
||||||
|
comments Comment[]
|
||||||
|
|
||||||
|
@@map("videos")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Forum {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
title String
|
||||||
|
description String?
|
||||||
|
type ForumType @default(GENERAL)
|
||||||
|
classId String?
|
||||||
|
createdBy String
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
updatedBy String
|
||||||
|
|
||||||
|
creator UserProfile @relation(fields: [createdBy], references: [id], onDelete: Cascade)
|
||||||
|
class Class? @relation(fields: [classId], references: [id], onDelete: SetNull)
|
||||||
|
posts ForumPost[]
|
||||||
|
|
||||||
|
@@map("forums")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ForumPost {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
title String
|
||||||
|
content String
|
||||||
|
forumId String
|
||||||
|
authorId String
|
||||||
|
parentId String? // for replies
|
||||||
|
isPinned Boolean @default(false)
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
updatedBy String
|
||||||
|
|
||||||
|
forum Forum @relation(fields: [forumId], references: [id], onDelete: Cascade)
|
||||||
|
author UserProfile @relation("ForumPosts", fields: [authorId], references: [id], onDelete: Cascade)
|
||||||
|
parent ForumPost? @relation("PostReplies", fields: [parentId], references: [id], onDelete: Cascade)
|
||||||
|
replies ForumPost[] @relation("PostReplies")
|
||||||
|
comments Comment[]
|
||||||
|
|
||||||
|
@@map("forum_posts")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Assignment {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
title String
|
||||||
|
description String
|
||||||
|
dueDate DateTime
|
||||||
|
classId String
|
||||||
|
educatorId String
|
||||||
|
maxScore Int
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
createdBy String
|
||||||
|
updatedBy String
|
||||||
|
|
||||||
|
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
|
||||||
|
educator UserProfile @relation(fields: [educatorId], references: [id], onDelete: Cascade)
|
||||||
|
submissions AssignmentSubmission[]
|
||||||
|
|
||||||
|
@@map("assignments")
|
||||||
|
}
|
||||||
|
|
||||||
|
model AssignmentSubmission {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
assignmentId String
|
||||||
|
studentId String
|
||||||
|
documentUrl String
|
||||||
|
documentType String // MIME type
|
||||||
|
score Int?
|
||||||
|
feedback String?
|
||||||
|
status SubmissionStatus @default(SUBMITTED)
|
||||||
|
submittedAt DateTime @default(now())
|
||||||
|
reviewedAt DateTime?
|
||||||
|
reviewedBy String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
updatedBy String
|
||||||
|
|
||||||
|
assignment Assignment @relation(fields: [assignmentId], references: [id], onDelete: Cascade)
|
||||||
|
student UserProfile @relation(fields: [studentId], references: [id], onDelete: Cascade)
|
||||||
|
reviewer UserProfile? @relation("SubmissionReviewer", fields: [reviewedBy], references: [id], onDelete: SetNull)
|
||||||
|
|
||||||
|
@@map("assignment_submissions")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Comment {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
content String
|
||||||
|
authorId String
|
||||||
|
videoId String?
|
||||||
|
forumPostId String?
|
||||||
|
parentId String? // for replies
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
updatedBy String
|
||||||
|
|
||||||
|
author UserProfile @relation("CommentAuthor", fields: [authorId], references: [id], onDelete: Cascade)
|
||||||
|
video Video? @relation(fields: [videoId], references: [id], onDelete: Cascade)
|
||||||
|
forumPost ForumPost? @relation(fields: [forumPostId], references: [id], onDelete: Cascade)
|
||||||
|
parent Comment? @relation("CommentReplies", fields: [parentId], references: [id], onDelete: Cascade)
|
||||||
|
replies Comment[] @relation("CommentReplies")
|
||||||
|
|
||||||
|
@@map("comments")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// ENUMS
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
enum VideoType {
|
||||||
|
YOUTUBE
|
||||||
|
LOCAL
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ForumType {
|
||||||
|
GENERAL
|
||||||
|
CLASS
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SubmissionStatus {
|
||||||
|
SUBMITTED
|
||||||
|
REVIEWED
|
||||||
|
REVISED
|
||||||
|
APPROVED
|
||||||
|
}
|
||||||
|
|||||||
51
tailwind.config.ts
Normal file
51
tailwind.config.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* File: tailwind.config.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Tailwind CSS configuration for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Config } from "tailwindcss";
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
darkMode: "class",
|
||||||
|
content: [
|
||||||
|
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
navy: {
|
||||||
|
50: '#f0f4ff',
|
||||||
|
100: '#dae9ff',
|
||||||
|
200: '#bdd7ff',
|
||||||
|
300: '#8fc2ff',
|
||||||
|
400: '#5ca7ff',
|
||||||
|
500: '#3182ff',
|
||||||
|
600: '#1e5fd6',
|
||||||
|
700: '#1e40af',
|
||||||
|
800: '#1e3a8a',
|
||||||
|
900: '#000080',
|
||||||
|
},
|
||||||
|
gold: {
|
||||||
|
50: '#fffbeb',
|
||||||
|
100: '#fef3c7',
|
||||||
|
200: '#fde68a',
|
||||||
|
300: '#fcd34d',
|
||||||
|
400: '#fbbf24',
|
||||||
|
500: '#f59e0b',
|
||||||
|
600: '#d97706',
|
||||||
|
700: '#b45309',
|
||||||
|
800: '#92400e',
|
||||||
|
900: '#78350f',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
115
types/api.ts
Normal file
115
types/api.ts
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* File: api.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: API response type definitions for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface ApiResponse<T = unknown> {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
data?: T;
|
||||||
|
error?: string;
|
||||||
|
pagination?: {
|
||||||
|
page: number;
|
||||||
|
limit: number;
|
||||||
|
total: number;
|
||||||
|
totalPages: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PaginationParams {
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
search?: string;
|
||||||
|
sortBy?: string;
|
||||||
|
sortOrder?: 'asc' | 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VideoFilters extends PaginationParams {
|
||||||
|
uploaderId?: string;
|
||||||
|
classId?: string;
|
||||||
|
videoType?: 'YOUTUBE' | 'LOCAL';
|
||||||
|
isPublic?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ForumFilters extends PaginationParams {
|
||||||
|
type?: 'GENERAL' | 'CLASS';
|
||||||
|
classId?: string;
|
||||||
|
authorId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssignmentFilters extends PaginationParams {
|
||||||
|
classId?: string;
|
||||||
|
educatorId?: string;
|
||||||
|
status?: 'ACTIVE' | 'INACTIVE' | 'DUE_SOON' | 'OVERDUE';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ClassFilters extends PaginationParams {
|
||||||
|
educatorId?: string;
|
||||||
|
isActive?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserFilters extends PaginationParams {
|
||||||
|
role?: 'ADMINISTRATOR' | 'PENDIDIK' | 'CALON_PENDIDIK' | 'UMUM';
|
||||||
|
isActive?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateVideoData {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
videoUrl: string;
|
||||||
|
videoType: 'YOUTUBE' | 'LOCAL';
|
||||||
|
thumbnailUrl?: string;
|
||||||
|
classId?: string;
|
||||||
|
isPublic: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateForumData {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
type: 'GENERAL' | 'CLASS';
|
||||||
|
classId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateForumPostData {
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
forumId: string;
|
||||||
|
parentId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAssignmentData {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
dueDate: Date;
|
||||||
|
classId: string;
|
||||||
|
maxScore: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateClassData {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
code: string;
|
||||||
|
maxStudents?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateCommentData {
|
||||||
|
content: string;
|
||||||
|
videoId?: string;
|
||||||
|
forumPostId?: string;
|
||||||
|
parentId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateUserData {
|
||||||
|
name?: string;
|
||||||
|
email?: string;
|
||||||
|
role?: 'ADMINISTRATOR' | 'PENDIDIK' | 'CALON_PENDIDIK' | 'UMUM';
|
||||||
|
profile?: {
|
||||||
|
phone?: string;
|
||||||
|
address?: string;
|
||||||
|
bio?: string;
|
||||||
|
avatar?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
110
types/auth.ts
Normal file
110
types/auth.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* File: auth.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Authentication type definitions for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface AuthUser {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
role: 'ADMINISTRATOR' | 'PENDIDIK' | 'CALON_PENDIDIK' | 'UMUM';
|
||||||
|
image?: string;
|
||||||
|
profile?: {
|
||||||
|
nim?: string;
|
||||||
|
phone?: string;
|
||||||
|
bio?: string;
|
||||||
|
avatar?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoginCredentials {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RegisterData {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
confirmPassword: string;
|
||||||
|
role?: 'CALON_PENDIDIK' | 'UMUM';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RoleUpgradeData {
|
||||||
|
role: 'PENDIDIK' | 'CALON_PENDIDIK';
|
||||||
|
additionalInfo?: {
|
||||||
|
institution?: string;
|
||||||
|
experience?: string;
|
||||||
|
specialization?: string;
|
||||||
|
nim?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthResponse {
|
||||||
|
user: AuthUser;
|
||||||
|
token?: string;
|
||||||
|
message: string;
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Permission {
|
||||||
|
canViewUsers: boolean;
|
||||||
|
canCreateUsers: boolean;
|
||||||
|
canUpdateUsers: boolean;
|
||||||
|
canDeleteUsers: boolean;
|
||||||
|
canManageClasses: boolean;
|
||||||
|
canManageVideos: boolean;
|
||||||
|
canManageForums: boolean;
|
||||||
|
canManageAssignments: boolean;
|
||||||
|
canViewReports: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ROLE_PERMISSIONS: Record<string, Permission> = {
|
||||||
|
ADMINISTRATOR: {
|
||||||
|
canViewUsers: true,
|
||||||
|
canCreateUsers: true,
|
||||||
|
canUpdateUsers: true,
|
||||||
|
canDeleteUsers: true,
|
||||||
|
canManageClasses: true,
|
||||||
|
canManageVideos: true,
|
||||||
|
canManageForums: true,
|
||||||
|
canManageAssignments: true,
|
||||||
|
canViewReports: true,
|
||||||
|
},
|
||||||
|
PENDIDIK: {
|
||||||
|
canViewUsers: false,
|
||||||
|
canCreateUsers: false,
|
||||||
|
canUpdateUsers: false,
|
||||||
|
canDeleteUsers: false,
|
||||||
|
canManageClasses: true,
|
||||||
|
canManageVideos: true,
|
||||||
|
canManageForums: true,
|
||||||
|
canManageAssignments: true,
|
||||||
|
canViewReports: false,
|
||||||
|
},
|
||||||
|
CALON_PENDIDIK: {
|
||||||
|
canViewUsers: false,
|
||||||
|
canCreateUsers: false,
|
||||||
|
canUpdateUsers: false,
|
||||||
|
canDeleteUsers: false,
|
||||||
|
canManageClasses: false,
|
||||||
|
canManageVideos: false,
|
||||||
|
canManageForums: true,
|
||||||
|
canManageAssignments: false,
|
||||||
|
canViewReports: false,
|
||||||
|
},
|
||||||
|
UMUM: {
|
||||||
|
canViewUsers: false,
|
||||||
|
canCreateUsers: false,
|
||||||
|
canUpdateUsers: false,
|
||||||
|
canDeleteUsers: false,
|
||||||
|
canManageClasses: false,
|
||||||
|
canManageVideos: false,
|
||||||
|
canManageForums: true,
|
||||||
|
canManageAssignments: false,
|
||||||
|
canViewReports: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
174
types/prisma.ts
Normal file
174
types/prisma.ts
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/**
|
||||||
|
* File: prisma.ts
|
||||||
|
* Created by: AI Assistant
|
||||||
|
* Date: 2025-11-29
|
||||||
|
* Purpose: Prisma type definitions for kreatiVortex platform
|
||||||
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface UserWithRelations {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
emailVerified: boolean;
|
||||||
|
image?: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
role: UserRole;
|
||||||
|
profile?: UserProfile;
|
||||||
|
classesTeaching?: Class[];
|
||||||
|
classesEnrolled?: Class[];
|
||||||
|
videos?: Video[];
|
||||||
|
forumPosts?: ForumPost[];
|
||||||
|
assignments?: Assignment[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserRole {
|
||||||
|
id: string;
|
||||||
|
name: 'ADMINISTRATOR' | 'PENDIDIK' | 'CALON_PENDIDIK' | 'UMUM';
|
||||||
|
description: string;
|
||||||
|
permissions: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserProfile {
|
||||||
|
id: string;
|
||||||
|
userId: string;
|
||||||
|
nim?: string;
|
||||||
|
phone?: string;
|
||||||
|
address?: string;
|
||||||
|
bio?: string;
|
||||||
|
avatar?: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Class {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
code: string;
|
||||||
|
educatorId: string;
|
||||||
|
educator: UserWithRelations;
|
||||||
|
isActive: boolean;
|
||||||
|
maxStudents?: number;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
createdBy: string;
|
||||||
|
updatedBy: string;
|
||||||
|
students?: UserWithRelations[];
|
||||||
|
videos?: Video[];
|
||||||
|
forums?: Forum[];
|
||||||
|
assignments?: Assignment[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Video {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
videoUrl: string;
|
||||||
|
videoType: 'YOUTUBE' | 'LOCAL';
|
||||||
|
thumbnailUrl?: string;
|
||||||
|
duration?: number;
|
||||||
|
uploaderId: string;
|
||||||
|
uploader: UserWithRelations;
|
||||||
|
classId?: string;
|
||||||
|
class?: Class;
|
||||||
|
isPublic: boolean;
|
||||||
|
viewCount: number;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
createdBy: string;
|
||||||
|
updatedBy: string;
|
||||||
|
comments?: Comment[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Forum {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
type: 'GENERAL' | 'CLASS';
|
||||||
|
classId?: string;
|
||||||
|
class?: Class;
|
||||||
|
createdBy: string;
|
||||||
|
creator: UserWithRelations;
|
||||||
|
isActive: boolean;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
updatedBy: string;
|
||||||
|
posts?: ForumPost[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ForumPost {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
forumId: string;
|
||||||
|
forum: Forum;
|
||||||
|
authorId: string;
|
||||||
|
author: UserWithRelations;
|
||||||
|
parentId?: string;
|
||||||
|
parent?: ForumPost;
|
||||||
|
replies?: ForumPost[];
|
||||||
|
isPinned: boolean;
|
||||||
|
isActive: boolean;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
updatedBy: string;
|
||||||
|
comments?: Comment[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Assignment {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
dueDate: Date;
|
||||||
|
classId: string;
|
||||||
|
class: Class;
|
||||||
|
educatorId: string;
|
||||||
|
educator: UserWithRelations;
|
||||||
|
maxScore: number;
|
||||||
|
isActive: boolean;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
createdBy: string;
|
||||||
|
updatedBy: string;
|
||||||
|
submissions?: AssignmentSubmission[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssignmentSubmission {
|
||||||
|
id: string;
|
||||||
|
assignmentId: string;
|
||||||
|
assignment: Assignment;
|
||||||
|
studentId: string;
|
||||||
|
student: UserWithRelations;
|
||||||
|
documentUrl: string;
|
||||||
|
documentType: string;
|
||||||
|
score?: number;
|
||||||
|
feedback?: string;
|
||||||
|
status: 'SUBMITTED' | 'REVIEWED' | 'REVISED' | 'APPROVED';
|
||||||
|
submittedAt: Date;
|
||||||
|
reviewedAt?: Date;
|
||||||
|
reviewedBy?: string;
|
||||||
|
reviewer?: UserWithRelations;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
updatedBy: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Comment {
|
||||||
|
id: string;
|
||||||
|
content: string;
|
||||||
|
authorId: string;
|
||||||
|
author: UserWithRelations;
|
||||||
|
videoId?: string;
|
||||||
|
video?: Video;
|
||||||
|
forumPostId?: string;
|
||||||
|
forumPost?: ForumPost;
|
||||||
|
parentId?: string;
|
||||||
|
parent?: Comment;
|
||||||
|
replies?: Comment[];
|
||||||
|
isActive: boolean;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
updatedBy: string;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user