'use client'; import { useEffect, useState } from 'react'; import Link from 'next/link'; import { Skeleton } from '@/components/ui/skeleton'; import { Button } from '@/components/ui/button'; import { useT } from '@/i18n'; import { API_BASE } from '@/lib/config'; import { useAuth } from '@/lib/auth-context'; import { apiFetch } from '@/lib/auth'; interface Practice { id: number; title: string; scenario: string; description: string; difficulty: string; category: string; attemptCount: number; sortOrder: number; createdAt: string; } interface Submission { id: number; questionId: number; score: number | null; status: string; submittedAt: string; } const difficultyColors: Record = { BEGINNER: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400', INTERMEDIATE: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400', ADVANCED: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400', }; export default function PracticesPage() { const t = useT(); const { isLoggedIn } = useAuth(); const [practices, setPractices] = useState([]); const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); const [category, setCategory] = useState(''); const [difficulty, setDifficulty] = useState(''); const [search, setSearch] = useState(''); const [submissions, setSubmissions] = useState>({}); useEffect(() => { Promise.all([ fetch(`${API_BASE}/practices`).then(r => r.json()), fetch(`${API_BASE}/practices/categories`).then(r => r.json()), ]).then(([data, cats]) => { setPractices(data.items || []); setCategories(cats || []); }).finally(() => setLoading(false)); }, []); useEffect(() => { if (!isLoggedIn) return; apiFetch('/practices/submissions?limit=100') .then(r => r.json()) .then(data => { const map: Record = {}; (data.items || []).forEach((s: Submission) => { map[s.questionId] = s; }); setSubmissions(map); }) .catch(() => {}); }, [isLoggedIn]); useEffect(() => { const params = new URLSearchParams(); if (category) params.set('category', category); if (difficulty) params.set('difficulty', difficulty); if (search) params.set('search', search); fetch(`${API_BASE}/practices?${params}`) .then(r => r.json()) .then(data => setPractices(data.items || [])); }, [category, difficulty, search]); const difficulties = [ { id: 'BEGINNER', name: t.practices.beginner }, { id: 'INTERMEDIATE', name: t.practices.intermediate }, { id: 'ADVANCED', name: t.practices.advanced }, ]; return (

{t.practices.title}

{t.practices.desc}

setSearch(e.target.value)} placeholder={t.common.search} className="px-3 py-2 border border-border rounded-lg text-sm bg-background text-foreground w-48" />
{loading ? (
{[1,2,3,4,5,6].map(i => )}
) : (
{practices.map(p => { const sub = submissions[p.id]; return (
{(t.practices as any)[p.difficulty.toLowerCase()] || p.difficulty} {sub && ( {sub.status === 'SCORED' ? t.practices.scoreRange.replace('{score}', String(sub.score)).replace('{max}', '100') : (t.practices.status as any)[sub.status]} )}

{p.title}

{p.description}

{(t.practices.categories as any)[p.category] || p.category} · {p.attemptCount} 次练习
); })}
)}
); }