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
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
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 {
.bg-grid {
background-size: 40px 40px;
@@ -76,6 +119,13 @@
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 {
@@ -96,4 +146,3 @@
.plyr--video {
@apply rounded-2xl overflow-hidden shadow-2xl;
}

View File

@@ -28,7 +28,7 @@ export default function CourseCard({
{/* 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" />
<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">
<img
src={thumbnail || "/brand/Nihonbuzz-Academy-Logo-Branding-Pattern-Landscape.png"}

View File

@@ -30,7 +30,7 @@ const itemVariants: Variants = {
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">
<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 */}
<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" />
@@ -45,7 +45,7 @@ export function Hero() {
variants={containerVariants}
initial="hidden"
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}>
<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(
"flex items-center justify-between transition-all duration-300 ease-in-out pointer-events-auto",
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"
)}
>

View File

@@ -9,7 +9,7 @@ const Card = React.forwardRef<
<div
ref={ref}
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
)}
{...props}

View File

@@ -4,7 +4,7 @@ import React from "react";
export default function AuthLayout({ children }: { children: React.ReactNode }) {
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">
{/* Background Decorations - Pure CSS */}
<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 { 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 (
<ThemeProvider defaultTheme="dark" storageKey="nihonbuzz-theme">
<div className="min-h-screen bg-background text-foreground selection:bg-primary/30 selection:text-primary">
<ThemeProvider defaultTheme="dark" storageKey="nihonbuzz-academy-theme">
<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 />
<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 />
</div>
</ThemeProvider>

View File

@@ -24,33 +24,34 @@ export default function ConfirmPassword() {
<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="w-full max-w-md bg-white p-8 rounded-3xl shadow-2xl border border-gray-100">
<div className="mb-6 text-sm text-gray-600 dark:text-gray-400">
This is a secure area of the application. Please confirm your
password before continuing.
<div className="w-full max-w-md">
<div className="card-glass rounded-3xl p-8">
<div className="mb-6 text-sm text-gray-600 dark:text-gray-400">
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>
<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>
</GuestLayout>

View File

@@ -5,6 +5,7 @@ import { FormEventHandler } from 'react';
import { Button } from '@/Components/ui/button';
import { Input } from '@/Components/ui/input';
import { Label } from '@/Components/ui/label';
import { Card } from '@/Components/ui/card';
export default function ForgotPassword({ status }: { status?: string }) {
const { data, setData, post, processing, errors } = useForm({
@@ -22,7 +23,7 @@ export default function ForgotPassword({ status }: { status?: string }) {
<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="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">
Forgot your password? No problem. Just let us know your email
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 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
</Button>
</button>
</div>
</form>
</div>
</Card>
</div>
</GuestLayout>
);

View File

@@ -34,7 +34,7 @@ export default function Login({
<Head title="Log in" />
<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">
<CardTitle className="text-3xl font-extrabold tracking-tight">Selamat Datang Kembali</CardTitle>
<CardDescription className="text-gray-500">
@@ -101,9 +101,9 @@ export default function Login({
</div>
<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
</Button>
</button>
</div>
<div className="relative">
@@ -119,7 +119,7 @@ export default function Login({
<Button
type="button"
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' })}
>
<svg className="w-5 h-5" viewBox="0 0 24 24">

View File

@@ -28,7 +28,7 @@ export default function Register() {
<Head title="Register" />
<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">
<CardTitle className="text-3xl font-extrabold tracking-tight">Buat Akun Baru</CardTitle>
<CardDescription className="text-gray-500">
@@ -101,9 +101,9 @@ export default function Register() {
</div>
<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
</Button>
</button>
</div>
<div className="text-center text-sm text-gray-500">

View File

@@ -33,58 +33,59 @@ export default function ResetPassword({
<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="w-full max-w-md bg-white p-8 rounded-3xl shadow-2xl border border-gray-100">
<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">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
name="email"
value={data.email}
className="block w-full"
autoComplete="username"
onChange={(e) => setData('email', e.target.value)}
/>
<InputError message={errors.email} />
</div>
<div className="w-full max-w-md">
<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">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
name="email"
value={data.email}
className="block w-full"
autoComplete="username"
onChange={(e) => setData('email', e.target.value)}
/>
<InputError message={errors.email} />
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
name="password"
value={data.password}
className="block w-full"
autoComplete="new-password"
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
name="password"
value={data.password}
className="block w-full"
autoComplete="new-password"
onChange={(e) => setData('password', e.target.value)}
/>
<InputError message={errors.password} />
</div>
onChange={(e) => setData('password', e.target.value)}
/>
<InputError message={errors.password} />
</div>
<div className="space-y-2">
<Label htmlFor="password_confirmation">Confirm Password</Label>
<Input
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">
<Label htmlFor="password_confirmation">Confirm Password</Label>
<Input
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="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 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}>
Reset Password
</button>
</div>
</form>
</div>
</div>
</div>
</GuestLayout>

View File

@@ -2,6 +2,7 @@ import GuestLayout from '@/Layouts/GuestLayout';
import { Head, Link, useForm } from '@inertiajs/react';
import { FormEventHandler } from 'react';
import { Button } from '@/Components/ui/button';
import { Card } from '@/Components/ui/card';
export default function VerifyEmail({ status }: { status?: string }) {
const { post, processing } = useForm({});
@@ -17,7 +18,7 @@ export default function VerifyEmail({ status }: { status?: string }) {
<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="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">
Thanks for signing up! Before getting started, could you verify
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">
<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
</Button>
</button>
<Link
href={route('logout')}
@@ -46,7 +47,7 @@ export default function VerifyEmail({ status }: { status?: string }) {
Log Out
</Link>
</form>
</div>
</Card>
</div>
</GuestLayout>
);

View File

@@ -57,7 +57,7 @@ export default function Dashboard({
<div className="space-y-8 animate-in fade-in duration-700">
{/* Stats Grid */}
<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">
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Total XP</CardTitle>
<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>
</CardContent>
</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">
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Streak</CardTitle>
<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>
</CardContent>
</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">
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Kursus</CardTitle>
<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>
</CardContent>
</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">
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Sertifikat</CardTitle>
<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) {
return (
<GuestLayout>
<GuestLayout variant="landing">
<Head title="Ecosystem Belajar Bahasa Jepang Modern" />
<Hero />
</GuestLayout>

View File

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

View File

@@ -30,6 +30,20 @@
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
<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 -->
@routes
@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>
);
}