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:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user