Files
english/src/features/home/components/Home.tsx
2026-04-18 23:16:52 +07:00

299 lines
14 KiB
TypeScript

import { Link } from '@tanstack/react-router'
import { useUser } from '@/hooks/use-auth'
import { useAuthModalStore } from '@/store/auth-modal-store'
const FEATURES = [
{
to: '/toeic',
icon: 'assignment',
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',
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',
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 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>
<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>
<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="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>
<span className="at-chip at-chip-good">
<span className="at-chip-dot" />
+24% so với tuần trước
</span>
</div>
<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>
{/* 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>
<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>
</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>
{/* 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="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>
</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>
)
}