update aja

This commit is contained in:
nihonbuzz
2026-01-24 10:20:55 +07:00
parent 62dc61aa31
commit 594f3727f5
23 changed files with 609 additions and 110 deletions

View File

@@ -1,12 +1,12 @@
# LPK NihonBuzz Academy Ecosystem Blueprint 🇯🇵 # LPK Nihonbuzz Academy Ecosystem Blueprint 🇯🇵
Dokumen ini merupakan cetak biru (blueprint) teknis mutakhir untuk seluruh ekosistem digital **LPK NihonBuzz**, yang dirancang sebagai platform pelatihan kerja terintegrasi, manajemen administrasi kantor (Buku Kantor), dan validasi sertifikasi kompetensi Bahasa Jepang. Dokumen ini merupakan cetak biru (blueprint) teknis mutakhir untuk seluruh ekosistem digital **LPK Nihonbuzz**, yang dirancang sebagai platform pelatihan kerja terintegrasi, manajemen administrasi kantor (Buku Kantor), dan validasi sertifikasi kompetensi Bahasa Jepang.
--- ---
## 1. High-Level Architecture ## 1. High-Level Architecture
Eksosistem LPK NihonBuzz terbagi menjadi dua entitas utama yang saling terintegrasi namun berjalan di infrastruktur yang dioptimalkan untuk kebutuhan masing-masing. Eksosistem LPK Nihonbuzz terbagi menjadi dua entitas utama yang saling terintegrasi namun berjalan di infrastruktur yang dioptimalkan untuk kebutuhan masing-masing.
```mermaid ```mermaid
graph TD graph TD

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 337 KiB

View File

@@ -60,6 +60,49 @@
} }
} }
@layer components {
.btn {
@apply inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 h-11 px-6 shadow-sm;
}
.btn-sm {
@apply h-9 rounded-md px-3 text-xs shadow-sm;
}
.btn-lg {
@apply h-12 rounded-xl px-8 shadow-sm;
}
.btn-icon {
@apply h-11 w-11 px-0 shadow-sm;
}
.btn-primary {
@apply bg-primary text-primary-foreground shadow hover:bg-primary/90 dark:hover:bg-primary/80;
}
.btn-secondary {
@apply bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80 dark:hover:bg-secondary/70;
}
.btn-outline {
@apply border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent dark:hover:text-accent-foreground;
}
.btn-ghost {
@apply shadow-none hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50 dark:hover:text-foreground;
}
.btn-destructive {
@apply bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90 dark:hover:bg-destructive/80;
}
.btn-link {
@apply text-primary underline-offset-4 hover:underline shadow-none bg-transparent hover:bg-transparent;
}
/* Glassmorphism Utilities */
.card-glass {
@apply bg-white/50 dark:bg-black/30 backdrop-blur-xl border border-white/40 dark:border-white/10 shadow-sm;
}
.btn-glass {
@apply border border-white/40 dark:border-white/10 bg-white/40 dark:bg-white/5 hover:bg-white/60 dark:hover:bg-white/10 backdrop-blur-md text-foreground shadow-sm;
}
}
@layer utilities { @layer utilities {
.bg-grid { .bg-grid {
background-size: 40px 40px; background-size: 40px 40px;
@@ -76,6 +119,13 @@
mask-image: linear-gradient(180deg, white, rgba(255, 255, 255, 0)); mask-image: linear-gradient(180deg, white, rgba(255, 255, 255, 0));
-webkit-mask-image: linear-gradient(180deg, white, rgba(255, 255, 255, 0)); -webkit-mask-image: linear-gradient(180deg, white, rgba(255, 255, 255, 0));
} }
.bg-wave-global {
background-color: transparent;
background-image: url('/brand/wave-pattern-global.svg');
background-repeat: repeat;
background-size: 400px auto; /* Adjust scale as needed, 400px seems a good starting point for visibility */
}
} }
@layer base { @layer base {
@@ -96,4 +146,3 @@
.plyr--video { .plyr--video {
@apply rounded-2xl overflow-hidden shadow-2xl; @apply rounded-2xl overflow-hidden shadow-2xl;
} }

View File

@@ -28,7 +28,7 @@ export default function CourseCard({
{/* Hover Glow Effect */} {/* Hover Glow Effect */}
<div className="absolute inset-0 bg-gradient-to-br from-primary/10 via-orange-500/5 to-transparent blur-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-3xl" /> <div className="absolute inset-0 bg-gradient-to-br from-primary/10 via-orange-500/5 to-transparent blur-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-3xl" />
<Card className="relative overflow-hidden bg-card/50 backdrop-blur-sm transition-all duration-500 border-border/50 group-hover:border-primary/30 shadow-sm group-hover:shadow-2xl group-hover:shadow-primary/5 group-hover:-translate-y-1 rounded-2xl"> <Card className="relative overflow-hidden transition-all duration-500 group-hover:border-primary/50 group-hover:-translate-y-1 rounded-2xl">
<div className="relative aspect-[16/9] overflow-hidden"> <div className="relative aspect-[16/9] overflow-hidden">
<img <img
src={thumbnail || "/brand/Nihonbuzz-Academy-Logo-Branding-Pattern-Landscape.png"} src={thumbnail || "/brand/Nihonbuzz-Academy-Logo-Branding-Pattern-Landscape.png"}

View File

@@ -30,7 +30,7 @@ const itemVariants: Variants = {
export function Hero() { export function Hero() {
return ( return (
<section className="relative min-h-screen flex flex-col items-center justify-center overflow-hidden w-full bg-background selection:bg-primary/30 selection:text-primary pt-20"> <section className="relative min-h-screen flex flex-col items-center justify-center overflow-hidden w-full bg-background selection:bg-primary/30 selection:text-primary">
{/* Background Gradients - Boosted for Light Mode using CSS variables */} {/* Background Gradients - Boosted for Light Mode using CSS variables */}
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[1000px] h-[600px] bg-primary/20 dark:bg-primary/10 blur-[120px] rounded-full opacity-[var(--hero-glow-opacity)] pointer-events-none" /> <div className="absolute top-0 left-1/2 -translate-x-1/2 w-[1000px] h-[600px] bg-primary/20 dark:bg-primary/10 blur-[120px] rounded-full opacity-[var(--hero-glow-opacity)] pointer-events-none" />
<div className="absolute bottom-0 right-0 w-[800px] h-[800px] bg-blue-500/10 dark:bg-blue-500/5 blur-[150px] rounded-full opacity-[var(--hero-glow-opacity)] pointer-events-none" /> <div className="absolute bottom-0 right-0 w-[800px] h-[800px] bg-blue-500/10 dark:bg-blue-500/5 blur-[150px] rounded-full opacity-[var(--hero-glow-opacity)] pointer-events-none" />
@@ -45,7 +45,7 @@ export function Hero() {
variants={containerVariants} variants={containerVariants}
initial="hidden" initial="hidden"
animate="visible" animate="visible"
className="relative z-10 container px-4 mx-auto flex flex-col items-center text-center gap-8" className="relative z-10 container px-4 mx-auto flex flex-col items-center text-center gap-8 pt-24 lg:pt-32"
> >
<motion.div variants={itemVariants}> <motion.div variants={itemVariants}>
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full border border-primary/10 bg-primary/5 backdrop-blur-sm shadow-inner text-sm text-primary font-bold mb-6"> <div className="inline-flex items-center gap-2 px-4 py-2 rounded-full border border-primary/10 bg-primary/5 backdrop-blur-sm shadow-inner text-sm text-primary font-bold mb-6">

View File

@@ -0,0 +1,91 @@
"use client";
import { motion, Variants } from "framer-motion";
import { ArrowRight, Sparkles } from "lucide-react";
import { Button } from "@/Components/ui/button";
import { Link } from "@inertiajs/react";
const containerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
delayChildren: 0.3,
},
},
};
const itemVariants: Variants = {
hidden: { y: 20, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: {
duration: 0.8,
ease: [0.215, 0.610, 0.355, 1.000],
},
},
};
export function Hero() {
return (
<section className="relative min-h-screen flex flex-col items-center justify-center overflow-hidden w-full bg-background selection:bg-primary/30 selection:text-primary pt-20">
{/* Background Gradients - Boosted for Light Mode using CSS variables */}
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[1000px] h-[600px] bg-primary/20 dark:bg-primary/10 blur-[120px] rounded-full opacity-[var(--hero-glow-opacity)] pointer-events-none" />
<div className="absolute bottom-0 right-0 w-[800px] h-[800px] bg-blue-500/10 dark:bg-blue-500/5 blur-[150px] rounded-full opacity-[var(--hero-glow-opacity)] pointer-events-none" />
{/* Grid Pattern Overlay */}
<div className="absolute inset-0 bg-grid z-0" />
{/* Japanese Wave Pattern - Adjusted Opacity */}
<div className="absolute inset-0 bg-seigaiha z-0 opacity-80 dark:opacity-40" />
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
className="relative z-10 container px-4 mx-auto flex flex-col items-center text-center gap-8"
>
<motion.div variants={itemVariants}>
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full border border-primary/10 bg-primary/5 backdrop-blur-sm shadow-inner text-sm text-primary font-bold mb-6">
<Sparkles className="w-4 h-4" />
<span>Pendaftaran Batch 2026 Dibuka!</span>
</div>
</motion.div>
<motion.h1
variants={itemVariants}
className="text-5xl md:text-7xl lg:text-8xl font-black tracking-tight text-foreground leading-[1.1]"
>
Hubungkan Impianmu <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary via-primary/80 to-primary/40 italic">
Ke Negeri Sakura
</span>
</motion.h1>
<motion.p
variants={itemVariants}
className="max-w-2xl text-lg md:text-xl text-muted-foreground leading-relaxed font-medium"
>
Platform edukasi terdepan untuk penguasaan Bahasa Jepang & karir profesional.
Belajar JLPT N5 hingga N2 dengan kurikulum modern dan Spaced Repetition System.
</motion.p>
<motion.div variants={itemVariants} className="flex flex-col sm:flex-row gap-5 mt-4">
<Button asChild size="lg" className="rounded-full bg-primary hover:bg-primary/90 text-white min-w-[180px] h-14 shadow-xl shadow-primary/20 font-bold text-lg">
<Link href={route('register')}>
Mulai Belajar Gratis
<ArrowRight className="w-5 h-5 ml-2" />
</Link>
</Button>
<Button asChild size="lg" variant="outline" className="rounded-full border-border hover:bg-muted min-w-[180px] h-14 font-bold text-lg text-foreground">
<Link href="#programs">
Lihat Program
</Link>
</Button>
</motion.div>
</motion.div>
</section>
);
}

View File

@@ -39,7 +39,7 @@ export function Navbar() {
className={cn( className={cn(
"flex items-center justify-between transition-all duration-300 ease-in-out pointer-events-auto", "flex items-center justify-between transition-all duration-300 ease-in-out pointer-events-auto",
scrolled scrolled
? "bg-background/80 backdrop-blur-xl border border-white/10 shadow-2xl w-full max-w-5xl px-10 py-3 rounded-full gap-8" ? "bg-background/80 backdrop-blur-xl border border-white/10 shadow-none w-full max-w-5xl px-10 py-3 rounded-full gap-8"
: "bg-transparent border-transparent w-full max-w-6xl px-6 py-3 rounded-full" : "bg-transparent border-transparent w-full max-w-6xl px-6 py-3 rounded-full"
)} )}
> >

View File

@@ -9,7 +9,7 @@ const Card = React.forwardRef<
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"rounded-xl border bg-card text-card-foreground shadow", "rounded-xl border border-white/40 dark:border-white/10 bg-white/50 dark:bg-black/30 backdrop-blur-xl text-card-foreground shadow-none",
className className
)} )}
{...props} {...props}

View File

@@ -4,7 +4,7 @@ import React from "react";
export default function AuthLayout({ children }: { children: React.ReactNode }) { export default function AuthLayout({ children }: { children: React.ReactNode }) {
return ( return (
<ThemeProvider defaultTheme="dark" storageKey="nihonbuzz-theme"> <ThemeProvider defaultTheme="dark" storageKey="nihonbuzz-academy-theme">
<div className="min-h-screen bg-background relative overflow-hidden flex items-center justify-center p-4"> <div className="min-h-screen bg-background relative overflow-hidden flex items-center justify-center p-4">
{/* Background Decorations - Pure CSS */} {/* Background Decorations - Pure CSS */}
<div className="absolute inset-0 z-0"> <div className="absolute inset-0 z-0">

View File

@@ -2,12 +2,29 @@ import { ThemeProvider } from "@/Components/ThemeProvider";
import { Navbar } from "@/Components/Landing/Navbar"; import { Navbar } from "@/Components/Landing/Navbar";
import { Footer } from "@/Components/Landing/Footer"; import { Footer } from "@/Components/Landing/Footer";
export default function GuestLayout({ children }: { children: React.ReactNode }) { import { cn } from "@/lib/utils";
export default function GuestLayout({
children,
variant = 'default'
}: {
children: React.ReactNode,
variant?: 'default' | 'landing'
}) {
return ( return (
<ThemeProvider defaultTheme="dark" storageKey="nihonbuzz-theme"> <ThemeProvider defaultTheme="dark" storageKey="nihonbuzz-academy-theme">
<div className="min-h-screen bg-background text-foreground selection:bg-primary/30 selection:text-primary"> <div className="min-h-screen flex flex-col bg-background text-foreground selection:bg-primary/30 selection:text-primary relative isolate">
{/* Global Background Pattern */}
{/* SVG is white by default. In light mode, we invert it to black to be visible on white bg. */}
<div className="fixed inset-0 z-[-1] bg-wave-global invert dark:invert-0 opacity-20 dark:opacity-5 pointer-events-none" />
<Navbar /> <Navbar />
<main>{children}</main> <main className={cn(
"min-h-screen flex flex-col items-center justify-center overflow-hidden relative",
variant === 'default' ? "pt-20 lg:pt-24 px-4" : "pt-0 px-0"
)}>
{children}
</main>
<Footer /> <Footer />
</div> </div>
</ThemeProvider> </ThemeProvider>

View File

@@ -24,33 +24,34 @@ export default function ConfirmPassword() {
<Head title="Confirm Password" /> <Head title="Confirm Password" />
<div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8"> <div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div className="w-full max-w-md bg-white p-8 rounded-3xl shadow-2xl border border-gray-100"> <div className="w-full max-w-md">
<div className="mb-6 text-sm text-gray-600 dark:text-gray-400"> <div className="card-glass rounded-3xl p-8">
This is a secure area of the application. Please confirm your <div className="mb-6 text-sm text-gray-600 dark:text-gray-400">
password before continuing. This is a secure area of the application. Please confirm your
password before continuing.
</div>
<form onSubmit={submit} className="space-y-6">
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
name="password"
value={data.password}
className="block w-full"
onChange={(e) => setData('password', e.target.value)}
/>
<InputError message={errors.password} />
</div>
<div className="flex items-center justify-end">
<button className="btn btn-primary w-full h-auto py-3 rounded-xl shadow-sm font-bold text-white" disabled={processing}>
Confirm
</button>
</div>
</form>
</div> </div>
<form onSubmit={submit} className="space-y-6">
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
name="password"
value={data.password}
className="block w-full"
onChange={(e) => setData('password', e.target.value)}
/>
<InputError message={errors.password} />
</div>
<div className="flex items-center justify-end">
<Button className="w-full bg-nihonbuzz-red hover:bg-nihonbuzz-red/90 text-white font-bold py-3 rounded-xl" disabled={processing}>
Confirm
</Button>
</div>
</form>
</div> </div>
</div> </div>
</GuestLayout> </GuestLayout>

View File

@@ -5,6 +5,7 @@ import { FormEventHandler } from 'react';
import { Button } from '@/Components/ui/button'; import { Button } from '@/Components/ui/button';
import { Input } from '@/Components/ui/input'; import { Input } from '@/Components/ui/input';
import { Label } from '@/Components/ui/label'; import { Label } from '@/Components/ui/label';
import { Card } from '@/Components/ui/card';
export default function ForgotPassword({ status }: { status?: string }) { export default function ForgotPassword({ status }: { status?: string }) {
const { data, setData, post, processing, errors } = useForm({ const { data, setData, post, processing, errors } = useForm({
@@ -22,7 +23,7 @@ export default function ForgotPassword({ status }: { status?: string }) {
<Head title="Forgot Password" /> <Head title="Forgot Password" />
<div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8"> <div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div className="w-full max-w-md bg-white p-8 rounded-3xl shadow-2xl border border-gray-100"> <Card className="w-full max-w-md rounded-3xl overflow-hidden p-8">
<div className="mb-6 text-sm text-gray-600 dark:text-gray-400"> <div className="mb-6 text-sm text-gray-600 dark:text-gray-400">
Forgot your password? No problem. Just let us know your email Forgot your password? No problem. Just let us know your email
address and we will email you a password reset link that will address and we will email you a password reset link that will
@@ -51,12 +52,12 @@ export default function ForgotPassword({ status }: { status?: string }) {
</div> </div>
<div className="flex items-center justify-end"> <div className="flex items-center justify-end">
<Button className="w-full bg-nihonbuzz-red hover:bg-nihonbuzz-red/90 text-white font-bold py-3 rounded-xl" disabled={processing}> <button className="btn btn-primary w-full h-auto py-3 rounded-xl shadow-sm font-bold text-white" disabled={processing}>
Email Password Reset Link Email Password Reset Link
</Button> </button>
</div> </div>
</form> </form>
</div> </Card>
</div> </div>
</GuestLayout> </GuestLayout>
); );

View File

@@ -34,7 +34,7 @@ export default function Login({
<Head title="Log in" /> <Head title="Log in" />
<div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8"> <div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<Card className="w-full max-w-md border-gray-100 shadow-2xl rounded-3xl overflow-hidden"> <Card className="w-full max-w-md rounded-3xl overflow-hidden">
<CardHeader className="space-y-1 text-center pt-8"> <CardHeader className="space-y-1 text-center pt-8">
<CardTitle className="text-3xl font-extrabold tracking-tight">Selamat Datang Kembali</CardTitle> <CardTitle className="text-3xl font-extrabold tracking-tight">Selamat Datang Kembali</CardTitle>
<CardDescription className="text-gray-500"> <CardDescription className="text-gray-500">
@@ -101,9 +101,9 @@ export default function Login({
</div> </div>
<div className="pt-2"> <div className="pt-2">
<Button className="w-full py-6 rounded-2xl text-base font-bold shadow-lg shadow-nihonbuzz-red/20 bg-nihonbuzz-red hover:bg-nihonbuzz-red/90" disabled={processing}> <button className="btn btn-primary w-full h-auto py-3 rounded-xl shadow-sm text-sm font-bold" disabled={processing}>
Masuk Sekarang Masuk Sekarang
</Button> </button>
</div> </div>
<div className="relative"> <div className="relative">
@@ -119,7 +119,7 @@ export default function Login({
<Button <Button
type="button" type="button"
variant="outline" variant="outline"
className="w-full py-6 rounded-2xl text-sm font-bold border-gray-100 hover:bg-gray-50 flex items-center justify-center gap-3 transition-all" className="btn btn-glass w-full h-auto py-3 rounded-xl text-sm font-bold flex items-center justify-center gap-3 transition-all"
onClick={() => window.location.href = route('social.redirect', { provider: 'google' })} onClick={() => window.location.href = route('social.redirect', { provider: 'google' })}
> >
<svg className="w-5 h-5" viewBox="0 0 24 24"> <svg className="w-5 h-5" viewBox="0 0 24 24">

View File

@@ -28,7 +28,7 @@ export default function Register() {
<Head title="Register" /> <Head title="Register" />
<div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8"> <div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<Card className="w-full max-w-md border-gray-100 shadow-2xl rounded-3xl overflow-hidden"> <Card className="w-full max-w-md rounded-3xl overflow-hidden">
<CardHeader className="space-y-1 text-center pt-8"> <CardHeader className="space-y-1 text-center pt-8">
<CardTitle className="text-3xl font-extrabold tracking-tight">Buat Akun Baru</CardTitle> <CardTitle className="text-3xl font-extrabold tracking-tight">Buat Akun Baru</CardTitle>
<CardDescription className="text-gray-500"> <CardDescription className="text-gray-500">
@@ -101,9 +101,9 @@ export default function Register() {
</div> </div>
<div className="pt-2"> <div className="pt-2">
<Button className="w-full py-6 rounded-2xl text-base font-bold shadow-lg shadow-nihonbuzz-red/20 bg-nihonbuzz-red hover:bg-nihonbuzz-red/90" disabled={processing}> <button className="btn btn-primary w-full h-auto py-3 rounded-xl shadow-sm text-sm font-bold" disabled={processing}>
Daftar Sekarang Daftar Sekarang
</Button> </button>
</div> </div>
<div className="text-center text-sm text-gray-500"> <div className="text-center text-sm text-gray-500">

View File

@@ -33,58 +33,59 @@ export default function ResetPassword({
<Head title="Reset Password" /> <Head title="Reset Password" />
<div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8"> <div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div className="w-full max-w-md bg-white p-8 rounded-3xl shadow-2xl border border-gray-100"> <div className="w-full max-w-md">
<h2 className="text-2xl font-bold mb-6 text-center">Reset Password</h2> <div className="card-glass rounded-3xl p-8">
<h2 className="text-2xl font-bold mb-6 text-center">Reset Password</h2>
<form onSubmit={submit} className="space-y-6">
<div className="space-y-2"> <form onSubmit={submit} className="space-y-6">
<Label htmlFor="email">Email</Label> <div className="space-y-2">
<Input <Label htmlFor="email">Email</Label>
id="email" <Input
type="email" id="email"
name="email" type="email"
value={data.email} name="email"
className="block w-full" value={data.email}
autoComplete="username" className="block w-full"
onChange={(e) => setData('email', e.target.value)} autoComplete="username"
/> onChange={(e) => setData('email', e.target.value)}
<InputError message={errors.email} /> />
</div> <InputError message={errors.email} />
</div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="password">Password</Label> <Label htmlFor="password">Password</Label>
<Input <Input
id="password" id="password"
type="password" type="password"
name="password" name="password"
value={data.password} value={data.password}
className="block w-full" className="block w-full"
autoComplete="new-password" autoComplete="new-password"
onChange={(e) => setData('password', e.target.value)}
/>
<InputError message={errors.password} />
</div>
onChange={(e) => setData('password', e.target.value)} <div className="space-y-2">
/> <Label htmlFor="password_confirmation">Confirm Password</Label>
<InputError message={errors.password} /> <Input
</div> type="password"
name="password_confirmation"
value={data.password_confirmation}
className="block w-full"
autoComplete="new-password"
onChange={(e) => setData('password_confirmation', e.target.value)}
/>
<InputError message={errors.password_confirmation} />
</div>
<div className="space-y-2"> <div className="flex items-center justify-end">
<Label htmlFor="password_confirmation">Confirm Password</Label> <button className="btn btn-primary w-full h-auto py-3 rounded-xl shadow-sm font-bold text-white" disabled={processing}>
<Input Reset Password
type="password" </button>
name="password_confirmation" </div>
value={data.password_confirmation} </form>
className="block w-full" </div>
autoComplete="new-password"
onChange={(e) => setData('password_confirmation', e.target.value)}
/>
<InputError message={errors.password_confirmation} />
</div>
<div className="flex items-center justify-end">
<Button className="w-full bg-nihonbuzz-red hover:bg-nihonbuzz-red/90 text-white font-bold py-3 rounded-xl" disabled={processing}>
Reset Password
</Button>
</div>
</form>
</div> </div>
</div> </div>
</GuestLayout> </GuestLayout>

View File

@@ -2,6 +2,7 @@ import GuestLayout from '@/Layouts/GuestLayout';
import { Head, Link, useForm } from '@inertiajs/react'; import { Head, Link, useForm } from '@inertiajs/react';
import { FormEventHandler } from 'react'; import { FormEventHandler } from 'react';
import { Button } from '@/Components/ui/button'; import { Button } from '@/Components/ui/button';
import { Card } from '@/Components/ui/card';
export default function VerifyEmail({ status }: { status?: string }) { export default function VerifyEmail({ status }: { status?: string }) {
const { post, processing } = useForm({}); const { post, processing } = useForm({});
@@ -17,7 +18,7 @@ export default function VerifyEmail({ status }: { status?: string }) {
<Head title="Email Verification" /> <Head title="Email Verification" />
<div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8"> <div className="flex flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div className="w-full max-w-md bg-white p-8 rounded-3xl shadow-2xl border border-gray-100 text-center"> <Card className="w-full max-w-md rounded-3xl overflow-hidden p-8 text-center">
<div className="mb-6 text-sm text-gray-600 dark:text-gray-400"> <div className="mb-6 text-sm text-gray-600 dark:text-gray-400">
Thanks for signing up! Before getting started, could you verify Thanks for signing up! Before getting started, could you verify
your email address by clicking on the link we just emailed to your email address by clicking on the link we just emailed to
@@ -33,9 +34,9 @@ export default function VerifyEmail({ status }: { status?: string }) {
)} )}
<form onSubmit={submit} className="space-y-4"> <form onSubmit={submit} className="space-y-4">
<Button className="w-full bg-nihonbuzz-red hover:bg-nihonbuzz-red/90 text-white font-bold py-3 rounded-xl" disabled={processing}> <button className="btn btn-primary w-full h-auto py-3 rounded-xl shadow-sm font-bold text-white" disabled={processing}>
Resend Verification Email Resend Verification Email
</Button> </button>
<Link <Link
href={route('logout')} href={route('logout')}
@@ -46,7 +47,7 @@ export default function VerifyEmail({ status }: { status?: string }) {
Log Out Log Out
</Link> </Link>
</form> </form>
</div> </Card>
</div> </div>
</GuestLayout> </GuestLayout>
); );

View File

@@ -57,7 +57,7 @@ export default function Dashboard({
<div className="space-y-8 animate-in fade-in duration-700"> <div className="space-y-8 animate-in fade-in duration-700">
{/* Stats Grid */} {/* Stats Grid */}
<div className="grid gap-4 grid-cols-2 lg:grid-cols-4"> <div className="grid gap-4 grid-cols-2 lg:grid-cols-4">
<Card className="border-border/50 bg-card/50 backdrop-blur-sm"> <Card className="shadow-none">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Total XP</CardTitle> <CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Total XP</CardTitle>
<Trophy className="h-4 w-4 text-yellow-500" /> <Trophy className="h-4 w-4 text-yellow-500" />
@@ -66,7 +66,7 @@ export default function Dashboard({
<div className="text-2xl font-black">{stats.xp_points.toLocaleString()}</div> <div className="text-2xl font-black">{stats.xp_points.toLocaleString()}</div>
</CardContent> </CardContent>
</Card> </Card>
<Card className="border-border/50 bg-card/50 backdrop-blur-sm"> <Card className="shadow-none">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Streak</CardTitle> <CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Streak</CardTitle>
<Flame className="h-4 w-4 text-orange-500" /> <Flame className="h-4 w-4 text-orange-500" />
@@ -75,7 +75,7 @@ export default function Dashboard({
<div className="text-2xl font-black">{stats.current_streak} Hari</div> <div className="text-2xl font-black">{stats.current_streak} Hari</div>
</CardContent> </CardContent>
</Card> </Card>
<Card className="border-border/50 bg-card/50 backdrop-blur-sm"> <Card className="shadow-none">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Kursus</CardTitle> <CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Kursus</CardTitle>
<BookOpen className="h-4 w-4 text-blue-500" /> <BookOpen className="h-4 w-4 text-blue-500" />
@@ -84,7 +84,7 @@ export default function Dashboard({
<div className="text-2xl font-black">{stats.active_courses} Aktif</div> <div className="text-2xl font-black">{stats.active_courses} Aktif</div>
</CardContent> </CardContent>
</Card> </Card>
<Card className="border-border/50 bg-card/50 backdrop-blur-sm"> <Card className="shadow-none">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Sertifikat</CardTitle> <CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Sertifikat</CardTitle>
<GraduationCap className="h-4 w-4 text-green-500" /> <GraduationCap className="h-4 w-4 text-green-500" />

View File

@@ -5,7 +5,7 @@ import { Hero } from '@/Components/Landing/Hero';
export default function Welcome({ auth }: PageProps) { export default function Welcome({ auth }: PageProps) {
return ( return (
<GuestLayout> <GuestLayout variant="landing">
<Head title="Ecosystem Belajar Bahasa Jepang Modern" /> <Head title="Ecosystem Belajar Bahasa Jepang Modern" />
<Hero /> <Hero />
</GuestLayout> </GuestLayout>

View File

@@ -19,7 +19,7 @@ createInertiaApp({
const root = createRoot(el); const root = createRoot(el);
root.render( root.render(
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme"> <ThemeProvider defaultTheme="dark" storageKey="nihonbuzz-academy-theme">
<App {...props} /> <App {...props} />
</ThemeProvider> </ThemeProvider>
); );

View File

@@ -30,6 +30,20 @@
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png"> <meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<script>
(function() {
const storageKey = 'nihonbuzz-academy-theme';
const theme = localStorage.getItem(storageKey);
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
if (theme === 'dark' || (!theme && systemTheme === 'dark')) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
})();
</script>
<!-- Scripts --> <!-- Scripts -->
@routes @routes
@viteReactRefresh @viteReactRefresh

91
tmp_hero_first.tsx Normal file
View File

@@ -0,0 +1,91 @@
"use client";
import { motion, Variants } from "framer-motion";
import { ArrowRight, Sparkles } from "lucide-react";
import { Button } from "@/Components/ui/button";
import { Link } from "@inertiajs/react";
const containerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
delayChildren: 0.3,
},
},
};
const itemVariants: Variants = {
hidden: { y: 20, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: {
duration: 0.8,
ease: [0.215, 0.610, 0.355, 1.000],
},
},
};
export function Hero() {
return (
<section className="relative min-h-screen flex flex-col items-center justify-center overflow-hidden w-full bg-background selection:bg-primary/30 selection:text-primary pt-20">
{/* Background Gradients - Boosted for Light Mode using CSS variables */}
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[1000px] h-[600px] bg-primary/20 dark:bg-primary/10 blur-[120px] rounded-full opacity-[var(--hero-glow-opacity)] pointer-events-none" />
<div className="absolute bottom-0 right-0 w-[800px] h-[800px] bg-blue-500/10 dark:bg-blue-500/5 blur-[150px] rounded-full opacity-[var(--hero-glow-opacity)] pointer-events-none" />
{/* Grid Pattern Overlay */}
<div className="absolute inset-0 bg-grid z-0" />
{/* Japanese Wave Pattern - Adjusted Opacity */}
<div className="absolute inset-0 bg-seigaiha z-0 opacity-80 dark:opacity-40" />
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
className="relative z-10 container px-4 mx-auto flex flex-col items-center text-center gap-8"
>
<motion.div variants={itemVariants}>
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full border border-primary/10 bg-primary/5 backdrop-blur-sm shadow-inner text-sm text-primary font-bold mb-6">
<Sparkles className="w-4 h-4" />
<span>Pendaftaran Batch 2026 Dibuka!</span>
</div>
</motion.div>
<motion.h1
variants={itemVariants}
className="text-5xl md:text-7xl lg:text-8xl font-black tracking-tight text-foreground leading-[1.1]"
>
Hubungkan Impianmu <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary via-primary/80 to-primary/40 italic">
Ke Negeri Sakura
</span>
</motion.h1>
<motion.p
variants={itemVariants}
className="max-w-2xl text-lg md:text-xl text-muted-foreground leading-relaxed font-medium"
>
Platform edukasi terdepan untuk penguasaan Bahasa Jepang & karir profesional.
Belajar JLPT N5 hingga N2 dengan kurikulum modern dan Spaced Repetition System.
</motion.p>
<motion.div variants={itemVariants} className="flex flex-col sm:flex-row gap-5 mt-4">
<Button asChild size="lg" className="rounded-full bg-primary hover:bg-primary/90 text-white min-w-[180px] h-14 shadow-xl shadow-primary/20 font-bold text-lg">
<Link href={route('register')}>
Mulai Belajar Gratis
<ArrowRight className="w-5 h-5 ml-2" />
</Link>
</Button>
<Button asChild size="lg" variant="outline" className="rounded-full border-border hover:bg-muted min-w-[180px] h-14 font-bold text-lg text-foreground">
<Link href="#programs">
Lihat Program
</Link>
</Button>
</motion.div>
</motion.div>
</section>
);
}

91
tmp_hero_v1.tsx Normal file
View File

@@ -0,0 +1,91 @@
"use client";
import { motion, Variants } from "framer-motion";
import { ArrowRight, Sparkles } from "lucide-react";
import { Button } from "@/Components/ui/button";
import { Link } from "@inertiajs/react";
const containerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
delayChildren: 0.3,
},
},
};
const itemVariants: Variants = {
hidden: { y: 20, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: {
duration: 0.8,
ease: [0.215, 0.610, 0.355, 1.000],
},
},
};
export function Hero() {
return (
<section className="relative min-h-screen flex flex-col items-center justify-center overflow-hidden w-full bg-background selection:bg-primary/30 selection:text-primary pt-20">
{/* Background Gradients - Boosted for Light Mode using CSS variables */}
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[1000px] h-[600px] bg-primary/20 dark:bg-primary/10 blur-[120px] rounded-full opacity-[var(--hero-glow-opacity)] pointer-events-none" />
<div className="absolute bottom-0 right-0 w-[800px] h-[800px] bg-blue-500/10 dark:bg-blue-500/5 blur-[150px] rounded-full opacity-[var(--hero-glow-opacity)] pointer-events-none" />
{/* Grid Pattern Overlay */}
<div className="absolute inset-0 bg-grid z-0" />
{/* Japanese Wave Pattern - Adjusted Opacity */}
<div className="absolute inset-0 bg-seigaiha z-0 opacity-80 dark:opacity-40" />
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
className="relative z-10 container px-4 mx-auto flex flex-col items-center text-center gap-8"
>
<motion.div variants={itemVariants}>
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full border border-primary/10 bg-primary/5 backdrop-blur-sm shadow-inner text-sm text-primary font-bold mb-6">
<Sparkles className="w-4 h-4" />
<span>Pendaftaran Batch 2026 Dibuka!</span>
</div>
</motion.div>
<motion.h1
variants={itemVariants}
className="text-5xl md:text-7xl lg:text-8xl font-black tracking-tight text-foreground leading-[1.1]"
>
Hubungkan Impianmu <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary via-primary/80 to-primary/40 italic">
Ke Negeri Sakura
</span>
</motion.h1>
<motion.p
variants={itemVariants}
className="max-w-2xl text-lg md:text-xl text-muted-foreground leading-relaxed font-medium"
>
Platform edukasi terdepan untuk penguasaan Bahasa Jepang & karir profesional.
Belajar JLPT N5 hingga N2 dengan kurikulum modern dan Spaced Repetition System.
</motion.p>
<motion.div variants={itemVariants} className="flex flex-col sm:flex-row gap-5 mt-4">
<Button asChild size="lg" className="rounded-full bg-primary hover:bg-primary/90 text-white min-w-[180px] h-14 shadow-xl shadow-primary/20 font-bold text-lg">
<Link href={route('register')}>
Mulai Belajar Gratis
<ArrowRight className="w-5 h-5 ml-2" />
</Link>
</Button>
<Button asChild size="lg" variant="outline" className="rounded-full border-border hover:bg-muted min-w-[180px] h-14 font-bold text-lg text-foreground">
<Link href="#programs">
Lihat Program
</Link>
</Button>
</motion.div>
</motion.div>
</section>
);
}