feat: implementasi Kodeva - sistem manajemen kursus coding

Fitur lengkap:
- Auth JWT + RBAC (Admin/Teacher) + halaman login
- Shell 3 kolom (left nav, topbar, right rail dashboard)
- CRUD siswa + follow up kanban + status calon/student/ex
- Dashboard: siswa aktif, kehadiran, bar sumber lead, perlu follow up
- Pembayaran prorata per pertemuan (cash/transfer/qris)
- Kelas, enrollment, generate sesi, kehadiran bulk
- Jurnal aktivitas + upload multi-foto
- Raport + placement test (tampil di detail siswa)
- Pengaturan & seed data contoh

Stack: Next.js 16, TypeScript, Tailwind v4, Prisma (SQLite), Recharts, Zod
This commit is contained in:
Amadea
2026-09-18 19:24:59 +07:00
commit 61ce9d7926
86 changed files with 13392 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
import type { InputHTMLAttributes, LabelHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes } from "react";
import { cn } from "@/lib/utils";
export function Label({ className, ...props }: LabelHTMLAttributes<HTMLLabelElement>) {
return <label className={cn("mb-1.5 block text-[13px] font-medium text-ink-700", className)} {...props} />;
}
const baseField =
"w-full rounded-[12px] border border-ink-300 bg-surface px-3 text-sm text-ink-900 placeholder:text-ink-500 transition-colors focus:border-brand-500 focus:outline-none focus:ring-[3px] focus:ring-brand-500/25 disabled:bg-ink-100";
export function Input({ className, ...props }: InputHTMLAttributes<HTMLInputElement>) {
return <input className={cn(baseField, "h-10", className)} {...props} />;
}
export function Textarea({ className, ...props }: TextareaHTMLAttributes<HTMLTextAreaElement>) {
return <textarea className={cn(baseField, "min-h-[88px] py-2.5", className)} {...props} />;
}
export function Select({ className, children, ...props }: SelectHTMLAttributes<HTMLSelectElement>) {
return (
<select className={cn(baseField, "h-10 appearance-none bg-[length:16px] pr-9", className)} {...props}>
{children}
</select>
);
}
export function Field({
label,
required,
hint,
error,
children,
className,
}: {
label?: string;
required?: boolean;
hint?: string;
error?: string;
children: React.ReactNode;
className?: string;
}) {
return (
<div className={className}>
{label ? (
<Label>
{label} {required ? <span className="text-danger-500">*</span> : null}
</Label>
) : null}
{children}
{error ? (
<p className="mt-1 text-[12px] text-danger-500">{error}</p>
) : hint ? (
<p className="mt-1 text-[12px] text-ink-500">{hint}</p>
) : null}
</div>
);
}