update
This commit is contained in:
@@ -3,7 +3,7 @@ import { useNavigate } from '@tanstack/react-router'
|
||||
import { CircularProgress } from '@/components/CircularProgress'
|
||||
import { useTestStore } from '@/store/test-store'
|
||||
import { TOEIC_PARTS } from '@/temp/local-data'
|
||||
import { fetchQuestions } from '@/hooks/use-questions'
|
||||
import { fetchQuestionsForTest } from '@/hooks/use-questions'
|
||||
import { useRequireAuth } from '@/hooks/use-require-auth'
|
||||
|
||||
export function ToeicPractice() {
|
||||
@@ -16,8 +16,9 @@ export function ToeicPractice() {
|
||||
if (!requireAuth()) return
|
||||
setLoadingPartId(partId)
|
||||
try {
|
||||
const questions = await fetchQuestions(partId, 10)
|
||||
startExam(partId, partName, questions)
|
||||
// TODO: replace hardcoded testId=1 with real test selection
|
||||
const parts = await fetchQuestionsForTest(1, [partId])
|
||||
startExam({ testId: 1, testName: partName, parts, totalSeconds: 0 })
|
||||
navigate({ to: '/toeic/session' })
|
||||
} catch (err) {
|
||||
console.error('Failed to load questions:', err)
|
||||
|
||||
@@ -10,66 +10,96 @@ export function ToeicTestList() {
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="px-6 py-8 max-w-6xl mx-auto page-enter">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-extrabold text-slate-800 mb-2">Đề Thi TOEIC</h1>
|
||||
<p className="text-slate-500">Chọn đề thi để bắt đầu luyện tập hoặc thi thử toàn bộ.</p>
|
||||
<div className="px-6 lg:px-10 py-10 max-w-6xl mx-auto page-enter">
|
||||
{/* Editorial head */}
|
||||
<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">Luyện đề</div>
|
||||
<h1 className="at-title text-4xl lg:text-[44px]">
|
||||
TOEIC <i>Mock Tests</i>
|
||||
</h1>
|
||||
<p className="mt-4 text-sm" style={{ color: 'var(--at-mute)' }}>
|
||||
Chọn đề để bắt đầu luyện tập — {tests.length} đề thi
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isLoading && (
|
||||
<div className="grid grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{Array.from({ length: 6 }).map((_, i) => (
|
||||
<div key={i} className="bg-white rounded-2xl border border-slate-200 p-5 animate-pulse h-44" />
|
||||
<div
|
||||
key={i}
|
||||
className="rounded-2xl h-44 animate-pulse"
|
||||
style={{ background: 'var(--at-surface)', border: '1px solid var(--at-line)' }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="bg-red-50 border border-red-100 rounded-2xl p-6 text-red-600 text-sm">
|
||||
<div
|
||||
className="rounded-2xl p-6 text-sm"
|
||||
style={{ background: 'var(--at-bad-soft)', border: '1px solid rgba(193,68,62,0.2)', color: 'var(--at-bad)' }}
|
||||
>
|
||||
Không thể tải danh sách đề thi. Vui lòng thử lại.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoading && !error && tests.length === 0 && (
|
||||
<div className="text-center py-20 text-slate-400">
|
||||
<span className="material-symbols-outlined" style={{ fontSize: 48 }}>library_books</span>
|
||||
<p className="mt-3 font-medium">Chưa có đề thi nào. Dữ liệu đang được cập nhật.</p>
|
||||
<div
|
||||
className="rounded-2xl p-16 text-center"
|
||||
style={{ background: 'var(--at-surface)', border: '1px solid var(--at-line)' }}
|
||||
>
|
||||
<span className="material-symbols-outlined mb-3 block" style={{ fontSize: 48, color: 'var(--at-mute-2)' }}>
|
||||
library_books
|
||||
</span>
|
||||
<p className="at-serif text-lg" style={{ color: 'var(--at-ink)' }}>Chưa có đề thi nào.</p>
|
||||
<p className="text-sm mt-1" style={{ color: 'var(--at-mute)' }}>Dữ liệu đang được cập nhật.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tests.length > 0 && (
|
||||
<div className="grid grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{tests.map((test) => (
|
||||
<div
|
||||
key={test.id}
|
||||
className="bg-white rounded-2xl border border-slate-200 p-5 flex flex-col shadow-sm hover:-translate-y-1 hover:shadow-md transition-all duration-200"
|
||||
className="rounded-2xl p-6 flex flex-col transition-all hover:-translate-y-1"
|
||||
style={{ background: 'var(--at-surface)', border: '1px solid var(--at-line)' }}
|
||||
>
|
||||
{/* Category badge */}
|
||||
{test.categoryName && (
|
||||
<span className="self-start text-xs font-bold px-2.5 py-1 rounded-full bg-blue-50 text-blue-600 border border-blue-100 mb-3">
|
||||
<span className="at-chip at-chip-brand self-start mb-3">
|
||||
<span className="at-chip-dot" />
|
||||
{test.categoryName}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<h3 className="font-extrabold text-lg text-slate-800 mb-1 leading-snug">{test.title}</h3>
|
||||
<h3
|
||||
className="at-serif text-[20px] leading-[1.2] tracking-tight mb-2"
|
||||
style={{ color: 'var(--at-ink)', fontWeight: 500 }}
|
||||
>
|
||||
{test.title}
|
||||
</h3>
|
||||
{test.description && (
|
||||
<p className="text-xs text-slate-400 mb-3 line-clamp-2">{test.description}</p>
|
||||
<p className="text-xs leading-[1.5] mb-3 line-clamp-2" style={{ color: 'var(--at-mute)' }}>
|
||||
{test.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-3 text-xs text-slate-500 mt-auto mb-4">
|
||||
<div className="flex items-center gap-4 text-xs mt-auto mb-4" style={{ color: 'var(--at-mute)' }}>
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="material-symbols-outlined" style={{ fontSize: 14 }}>list_alt</span>
|
||||
{test.totalQuestions} câu
|
||||
<span className="material-symbols-outlined" style={{ fontSize: 13 }}>list_alt</span>
|
||||
<b className="tabular-nums" style={{ color: 'var(--at-ink)' }}>{test.totalQuestions}</b> câu
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="material-symbols-outlined" style={{ fontSize: 14 }}>timer</span>
|
||||
{test.durationMinutes} phút
|
||||
<span className="material-symbols-outlined" style={{ fontSize: 13 }}>timer</span>
|
||||
<b className="tabular-nums" style={{ color: 'var(--at-ink)' }}>{test.durationMinutes}</b> phút
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => navigate({ to: '/toeic/$testId', params: { testId: String(test.id) } })}
|
||||
className="w-full py-2.5 bg-blue-600 text-white rounded-xl text-sm font-semibold hover:bg-blue-700 transition-colors"
|
||||
className="w-full py-2.5 rounded-xl text-[13px] font-semibold transition-opacity hover:opacity-90"
|
||||
style={{ background: 'var(--at-ink)', color: 'var(--at-paper)' }}
|
||||
>
|
||||
Bắt đầu
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user