This commit is contained in:
2026-04-18 23:16:52 +07:00
parent 3e0b3f6a6d
commit 309609fccb
32 changed files with 2261 additions and 1030 deletions

View File

@@ -6,199 +6,293 @@ const FEATURES = [
{
to: '/toeic',
icon: 'assignment',
iconBg: 'bg-blue-50',
iconColor: 'text-blue-600',
borderColor: 'border-l-blue-600',
title: 'Luyện đề TOEIC',
desc: 'Kho đề thi cập nhật theo cấu trúc mới nhất. Phân tích điểm yếu chi tiết từng Part.',
cta: 'Bắt đầu ngay',
ctaColor: 'text-blue-600',
title: 'Luyện đề',
accent: 'TOEIC',
desc: 'Kho đề thi cập nhật theo cấu trúc mới nhất. Phân tích điểm yếu từng Part.',
stat: '350+ câu hỏi',
},
{
to: '/writing',
icon: 'auto_fix_high',
iconBg: 'bg-green-50',
iconColor: 'text-green-600',
borderColor: 'border-l-green-600',
title: 'AI Chấm Writing',
desc: 'Phản hồi tức thì về ngữ pháp, từ vựng, cấu trúc và bài viết mẫu từ AI.',
cta: 'Thử ngay',
ctaColor: 'text-green-600',
title: 'AI chấm',
accent: 'Writing',
desc: 'Phản hồi tức thì về ngữ pháp, từ vựng, cấu trúc và bài viết mẫu.',
stat: '3 lượt / ngày',
},
{
to: '/flash-card',
icon: 'menu_book',
iconBg: 'bg-amber-50',
iconColor: 'text-amber-600',
borderColor: 'border-l-amber-600',
title: 'Từ vựng thông minh',
desc: '720 từ TOEIC theo 6 chủ đề. Flashcard với hiệu ứng lật 3D.',
cta: 'Khám phá',
ctaColor: 'text-amber-600',
stat: '720 từ vựng',
title: 'Từ vựng',
accent: 'thông minh',
desc: 'Bộ thẻ TOEIC với spaced-repetition, lật 3D, ảnh minh hoạ.',
stat: '18 000+ từ',
},
]
export function Home() {
const user = useUser()
const openModal = useAuthModalStore((s) => s.open)
const firstName = user?.name ?? 'bạn'
return (
<div className="px-6 py-8 max-w-6xl mx-auto page-enter">
{/* Hero */}
<section className="flex flex-col lg:flex-row gap-10 items-center mb-12">
<div className="flex-1 min-w-0">
<div className="inline-flex items-center gap-2 bg-blue-50 text-blue-600 text-xs font-bold px-3 py-1.5 rounded-full mb-5 uppercase tracking-wider">
<span className="material-symbols-outlined" style={{ fontSize: 14 }}>auto_awesome</span>
AI-Powered Learning
</div>
<h1 className="text-4xl lg:text-5xl font-extrabold leading-tight text-slate-800 mb-4" style={{ letterSpacing: '-0.02em' }}>
Luyện TOEIC<br />thông minh<br />
<span className="text-blue-600 italic">cùng AI</span>
<div className="px-6 lg:px-10 py-10 max-w-6xl mx-auto page-enter">
{/* Page head — editorial */}
<div className="flex flex-col lg:flex-row lg:items-end lg:justify-between gap-6 mb-10">
<div>
<div className="at-eyebrow mb-3">Học TOEIC cùng AI</div>
<h1 className="at-title text-4xl lg:text-[44px]">
Chào <i>{firstName}</i>,<br />
hôm nay học <i>15 phút</i>?
</h1>
<p className="text-slate-500 text-lg leading-relaxed mb-8 max-w-md">
nhân hóa lộ trình học tập đ bứt phá điểm số trong thời gian ngắn nhất. AI phân tích điểm yếu tối ưu bài tập cho bạn.
</p>
<div className="flex gap-3 flex-wrap">
<Link
to="/toeic"
className="bg-blue-600 text-white px-8 py-3.5 rounded-xl font-bold text-sm hover:bg-blue-700 transition-colors shadow-lg shadow-blue-600/20"
>
Bắt đu ngay
</Link>
<Link
to="/writing"
className="border border-slate-200 px-8 py-3.5 rounded-xl font-bold text-sm text-slate-500 hover:bg-white hover:border-blue-600 hover:text-blue-600 transition-all"
>
Thử AI Writing
</Link>
</div>
<div className="flex gap-6 mt-8">
<div>
<div className="text-2xl font-extrabold text-blue-600">350+</div>
<div className="text-xs text-slate-400 mt-0.5">Câu hỏi TOEIC</div>
</div>
<div className="w-px bg-slate-200" />
<div>
<div className="text-2xl font-extrabold text-green-600">720</div>
<div className="text-xs text-slate-400 mt-0.5">Từ vựng</div>
</div>
<div className="w-px bg-slate-200" />
<div>
<div className="text-2xl font-extrabold text-amber-600">AI</div>
<div className="text-xs text-slate-400 mt-0.5">Writing Checker</div>
</div>
<div className="mt-4 text-sm" style={{ color: 'var(--at-mute)' }}>
Mục tiêu <b style={{ color: 'var(--at-ink)' }}>850</b>
<span className="mx-2 inline-block w-[3px] h-[3px] rounded-full align-middle" style={{ background: 'var(--at-mute-2)' }} />
hiện tại <b style={{ color: 'var(--at-ink)' }}>720</b>
<span className="mx-2 inline-block w-[3px] h-[3px] rounded-full align-middle" style={{ background: 'var(--at-mute-2)' }} />
còn <b style={{ color: 'var(--at-brand)' }}>130 điểm</b> nữa
</div>
</div>
<div className="flex gap-2.5">
<Link
to="/flash-card"
className="inline-flex items-center gap-2 px-5 py-3 rounded-xl text-[13.5px] font-semibold transition-colors"
style={{ background: 'var(--at-surface)', border: '1px solid var(--at-line)', color: 'var(--at-ink-2)' }}
>
Học từ vựng
</Link>
<Link
to="/toeic"
className="inline-flex items-center gap-2 px-5 py-3 rounded-xl text-[13.5px] font-semibold transition-colors hover:opacity-90"
style={{ background: 'var(--at-ink)', color: 'var(--at-paper)', border: '1px solid var(--at-ink)' }}
>
<span className="material-symbols-outlined" style={{ fontSize: 16 }}>play_arrow</span>
Tiếp tục học
</Link>
</div>
</div>
{/* Preview card — hidden on mobile */}
<div className="hidden lg:block flex-shrink-0 w-80">
<div className="bg-white rounded-2xl p-6 shadow-xl border border-slate-100">
<div className="flex items-center justify-between mb-5">
<div className="grid lg:grid-cols-[2fr_1fr] gap-5">
{/* MAIN COL */}
<div className="flex flex-col gap-5 min-w-0">
{/* Progress hero */}
<div className="rounded-2xl p-7 flex flex-wrap items-center gap-7" style={{ background: 'var(--at-surface)', border: '1px solid var(--at-line)' }}>
<ProgressRing value={85} />
<div className="flex-1 min-w-[240px]">
<div className="at-eyebrow mb-1">Lộ trình</div>
<div className="at-serif text-[22px] leading-[1.2] tracking-tight mb-3" style={{ color: 'var(--at-ink)' }}>
Bạn đang đi <i style={{ color: 'var(--at-brand)', fontStyle: 'italic' }}>đúng hướng</i> tuần này 4/7 ngày.
</div>
<div className="flex flex-wrap gap-4 items-stretch">
<Stat num="24" label="ngày còn lại" />
<div className="w-px self-stretch" style={{ background: 'var(--at-line)' }} />
<Stat num="+46" label="điểm tháng này" />
<div className="w-px self-stretch" style={{ background: 'var(--at-line)' }} />
<Stat num="68%" label="tỷ lệ đúng" color="var(--at-good)" />
</div>
</div>
</div>
{/* Feature cards */}
<div className="rounded-2xl p-6" style={{ background: 'var(--at-surface)', border: '1px solid var(--at-line)' }}>
<div className="at-eyebrow mb-1">Khám phá</div>
<h2 className="at-serif text-[22px] tracking-tight mb-5" style={{ color: 'var(--at-ink)', fontWeight: 500 }}>
Tính năng <i style={{ color: 'var(--at-brand)', fontStyle: 'italic' }}>nổi bật</i>
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
{FEATURES.map((f) => (
<Link
key={f.to}
to={f.to}
className="rounded-xl p-4 transition-all hover:-translate-y-0.5"
style={{ background: 'var(--at-paper-2)', border: '1px solid var(--at-line)' }}
>
<div className="flex items-center justify-between mb-3">
<div
className="w-9 h-9 rounded-lg grid place-items-center"
style={{ background: 'var(--at-brand-soft)', color: 'var(--at-brand)' }}
>
<span className="material-symbols-outlined" style={{ fontSize: 18 }}>{f.icon}</span>
</div>
<span className="at-chip at-chip-brand">
<span className="at-chip-dot" />
{f.stat}
</span>
</div>
<div className="at-serif text-[17px] leading-[1.15] tracking-tight mb-1" style={{ color: 'var(--at-ink)', fontWeight: 500 }}>
{f.title} <i style={{ color: 'var(--at-brand)', fontStyle: 'italic' }}>{f.accent}</i>
</div>
<div className="text-[12.5px] leading-[1.5]" style={{ color: 'var(--at-mute)' }}>{f.desc}</div>
</Link>
))}
</div>
</div>
{/* 7-day journey */}
<div className="rounded-2xl p-6" style={{ background: 'var(--at-surface)', border: '1px solid var(--at-line)' }}>
<div className="flex justify-between items-end mb-4">
<div>
<div className="font-bold text-base text-slate-800">Tiến đ tuần này</div>
<div className="text-xs text-slate-400 mt-0.5">Bạn đang làm rất tốt!</div>
<div className="at-eyebrow mb-1">Tuần này</div>
<div className="at-serif text-[20px] tracking-tight" style={{ color: 'var(--at-ink)', fontWeight: 500 }}>
Lộ trình <i style={{ color: 'var(--at-brand)', fontStyle: 'italic' }}>7 ngày</i>
</div>
</div>
<div className="bg-green-50 text-green-600 text-xs font-bold px-2.5 py-1 rounded-lg">+12%</div>
<span className="at-chip at-chip-good">
<span className="at-chip-dot" />
+24% so với tuần trước
</span>
</div>
<div className="mb-4">
<div className="flex justify-between text-xs font-semibold mb-1.5">
<span>Reading Score</span><span className="text-blue-600">420/495</span>
</div>
<div className="h-1.5 w-full rounded-full bg-slate-100">
<div className="h-full bg-blue-600 rounded-full" style={{ width: '85%' }} />
</div>
</div>
<div className="mb-4">
<div className="flex justify-between text-xs font-semibold mb-1.5">
<span>Listening Score</span><span className="text-green-600">380/495</span>
</div>
<div className="h-1.5 w-full rounded-full bg-slate-100">
<div className="h-full bg-green-600 rounded-full" style={{ width: '77%' }} />
</div>
</div>
<div className="grid grid-cols-2 gap-3 mt-4">
<div className="bg-blue-50 rounded-xl p-3 border-l-4 border-blue-600">
<span className="material-symbols-outlined text-blue-600" style={{ fontSize: 18 }}>local_fire_department</span>
<div className="text-xl font-extrabold text-blue-600 mt-1">14</div>
<div className="text-xs text-slate-400">Ngày Streak</div>
</div>
<div className="bg-green-50 rounded-xl p-3 border-l-4 border-green-600">
<span className="material-symbols-outlined text-green-600" style={{ fontSize: 18, fontVariationSettings: "'FILL' 1" }}>star</span>
<div className="text-xl font-extrabold text-green-600 mt-1">1,250</div>
<div className="text-xs text-slate-400">Điểm tích lũy</div>
</div>
</div>
<div className="mt-4 pt-4 border-t border-slate-100 flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-slate-100 flex items-center justify-center flex-shrink-0">
<span className="material-symbols-outlined text-slate-400" style={{ fontSize: 16 }}>psychology</span>
</div>
<p className="text-xs text-slate-500">
<span className="font-semibold">AI gợi ý:</span> Ôn thêm Part 5 Ngữ pháp
</p>
<div className="grid grid-cols-7 gap-2.5">
{['T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'CN'].map((d, i) => {
const h = [60, 85, 40, 90, 75, 0, 0][i]
const done = h > 0
const today = i === 4
return (
<div key={d} className="flex flex-col items-center gap-2">
<div className="w-full relative overflow-hidden rounded-[10px]" style={{ height: 96, background: 'var(--at-line-2)' }}>
<div
className="absolute left-0 right-0 bottom-0 rounded-[10px] transition-[height] duration-500"
style={{
height: `${h}%`,
background: today ? 'var(--at-brand)' : done ? 'var(--at-brand-soft)' : 'var(--at-line-2)',
}}
/>
</div>
<div
className={today ? 'at-serif italic' : ''}
style={{ fontSize: 11, color: today ? 'var(--at-brand)' : 'var(--at-mute)', fontWeight: today ? 700 : 500 }}
>
{d}
</div>
</div>
)
})}
</div>
</div>
</div>
</section>
{/* Feature cards */}
<section>
<h2 className="text-2xl font-extrabold text-slate-800 mb-1.5">Tính năng nổi bật</h2>
<p className="text-slate-500 mb-6">Hệ sinh thái học tập toàn diện đưc thiết kế đ tối ưu hoá điểm số.</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-5">
{FEATURES.map((f) => (
<Link
key={f.to}
to={f.to}
className={`bg-white rounded-2xl p-6 border border-slate-200 border-l-4 ${f.borderColor} hover:-translate-y-1 hover:shadow-md transition-all duration-200`}
>
<div className={`w-12 h-12 ${f.iconBg} rounded-xl flex items-center justify-center mb-4`}>
<span className={`material-symbols-outlined ${f.iconColor}`}>{f.icon}</span>
{/* SIDE COL */}
<div className="flex flex-col gap-5">
{/* Streak card (inky) */}
<div className="rounded-2xl p-5" style={{ background: 'var(--at-ink)', color: 'var(--at-paper)' }}>
<div className="flex items-center justify-between mb-3.5">
<div style={{ fontSize: 10, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'rgba(250,248,243,0.55)', fontWeight: 600 }}>
Streak
</div>
<h3 className="font-bold text-base text-slate-800 mb-2">{f.title}</h3>
<p className="text-slate-500 text-sm leading-relaxed mb-4">{f.desc}</p>
<div className={`flex items-center gap-1.5 text-sm font-bold ${f.ctaColor}`}>
{f.cta}
<span className="material-symbols-outlined" style={{ fontSize: 16 }}>arrow_forward</span>
<div className="w-10 h-10 rounded-xl grid place-items-center" style={{ background: 'rgba(255,255,255,0.08)', color: '#FFC27A' }}>
<span className="material-symbols-outlined" style={{ fontSize: 20, fontVariationSettings: "'FILL' 1" }}>local_fire_department</span>
</div>
</Link>
))}
</div>
</section>
{/* CTA banner */}
<section className="mt-10">
<div className="bg-blue-600 rounded-2xl p-8 flex items-center justify-between overflow-hidden relative">
<div className="absolute right-4 top-0 bottom-0 flex items-center opacity-10">
<span className="material-symbols-outlined text-white" style={{ fontSize: 120 }}>emoji_events</span>
</div>
<div className="at-serif" style={{ fontSize: 44, fontWeight: 400, letterSpacing: '-0.03em', lineHeight: 1, marginBottom: 4 }}>
7 <span className="italic opacity-65" style={{ fontSize: 18 }}>ngày</span>
</div>
<div style={{ fontSize: 12, color: 'rgba(250,248,243,0.55)', marginBottom: 14 }}>Kỷ lục: 21 ngày</div>
<div className="flex gap-1.5">
{['T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'CN'].map((d, i) => (
<div
key={i}
className="flex-1 rounded-md grid place-items-center"
style={{
height: 24,
background: i < 5 ? '#C15A34' : 'rgba(255,255,255,0.08)',
color: i < 5 ? 'white' : 'rgba(255,255,255,0.4)',
fontSize: 9,
fontWeight: 600,
}}
>
{d}
</div>
))}
</div>
</div>
<div className="relative z-10">
<h3 className="text-2xl font-extrabold text-white mb-2">Sẵn sàng chinh phục 990 TOEIC?</h3>
<p className="text-blue-100 mb-5">
{user
? `Chào ${user.name}! Tiếp tục luyện thi hôm nay.`
: 'Đăng ký miễn phí để lưu tiến độ và luyện thi không giới hạn.'}
</p>
{user ? (
<Link
to="/toeic"
className="inline-block bg-white text-blue-600 px-6 py-3 rounded-xl font-bold text-sm hover:bg-blue-50 transition-colors"
>
Luyện thi ngay
</Link>
) : (
{/* AI nudge */}
<div className="at-pullquote">
<div className="flex items-center gap-2 mb-2.5">
<div className="w-6 h-6 rounded-lg grid place-items-center" style={{ background: 'var(--at-brand)', color: 'white' }}>
<span className="material-symbols-outlined" style={{ fontSize: 14, fontVariationSettings: "'FILL' 1" }}>auto_awesome</span>
</div>
<div style={{ fontSize: 10, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--at-brand-ink)', fontWeight: 700 }}>
AI gợi ý
</div>
</div>
<div className="at-pullquote-q">
"Bạn yếu nhất <b style={{ fontWeight: 600 }}>Part 3</b> — dành 10 phút hôm nay có thể tăng <b style={{ fontWeight: 600 }}>30+ điểm</b>."
</div>
<div className="mt-2.5 text-[11px] opacity-70" style={{ color: 'var(--at-brand-ink)' }}> EnglishAI Coach</div>
</div>
{/* Pro tip */}
<div className="at-tip">
<div className="at-tip-label">Pro tip</div>
<div className="text-[12.5px] leading-[1.55]" style={{ color: 'var(--at-ink-2)' }}>
Học theo <b style={{ color: 'var(--at-warm)', fontWeight: 700 }}>cụm từ</b> (collocations) giúp bạn ghi nhớ nhanh hơn{' '}
<b style={{ color: 'var(--at-warm)', fontWeight: 700 }}>40%</b> so với học từ đơn lẻ.
</div>
</div>
{/* Guest CTA (only if not logged in) */}
{!user && (
<div className="rounded-2xl p-5" style={{ background: 'var(--at-surface)', border: '1px solid var(--at-line)' }}>
<div className="at-eyebrow mb-2">Khách</div>
<div className="at-serif text-[17px] leading-[1.2] tracking-tight mb-3" style={{ color: 'var(--at-ink)', fontWeight: 500 }}>
Đăng đ <i style={{ color: 'var(--at-brand)', fontStyle: 'italic' }}>lưu tiến đ</i>.
</div>
<button
onClick={() => openModal('register')}
className="bg-white text-blue-600 px-6 py-3 rounded-xl font-bold text-sm hover:bg-blue-50 transition-colors"
className="w-full py-2.5 rounded-xl text-[13.5px] font-semibold transition-opacity hover:opacity-90"
style={{ background: 'var(--at-ink)', color: 'var(--at-paper)' }}
>
Đăng miễn phí
</button>
)}
</div>
</div>
)}
</div>
</section>
</div>
</div>
)
}
function Stat({ num, label, color }: { num: string; label: string; color?: string }) {
return (
<div>
<div className="at-serif" style={{ fontSize: 26, fontWeight: 400, letterSpacing: '-0.02em', lineHeight: 1, color: color ?? 'var(--at-ink)' }}>
{num}
</div>
<div style={{ fontSize: 11, color: 'var(--at-mute)', marginTop: 4 }}>{label}</div>
</div>
)
}
function ProgressRing({ value }: { value: number }) {
const r = 58
const c = 2 * Math.PI * r
const offset = c - (value / 100) * c
return (
<div className="relative grid place-items-center" style={{ width: 132, height: 132 }}>
<svg width="132" height="132">
<circle cx="66" cy="66" r={r} fill="none" stroke="var(--at-line-2)" strokeWidth="7" />
<circle
cx="66"
cy="66"
r={r}
fill="none"
stroke="var(--at-brand)"
strokeWidth="7"
strokeDasharray={c}
strokeDashoffset={offset}
strokeLinecap="round"
transform="rotate(-90 66 66)"
style={{ transition: 'stroke-dashoffset 0.6s cubic-bezier(0.2, 0.7, 0.2, 1)' }}
/>
</svg>
<div className="absolute text-center">
<div className="at-serif" style={{ fontSize: 34, fontWeight: 400, letterSpacing: '-0.025em', lineHeight: 1, color: 'var(--at-ink)' }}>
720
</div>
<div style={{ fontSize: 10, color: 'var(--at-mute)', textTransform: 'uppercase', letterSpacing: '0.12em', marginTop: 4, fontWeight: 600 }}>
/ 850
</div>
</div>
</div>
)
}