refactor: 重構 review 組件架構 + 修正 API 端口配置

- 重構組件命名: Simple* → 更語義化命名
  - SimpleFlipCard → FlipMemory
  - VocabChoiceTest → VocabChoiceQuiz
  - SimpleProgress → QuizProgress
  - SimpleResults → QuizResult
  - SimpleTestHeader → QuizHeader

- 重新組織目錄結構:
  - components/review/simple/ → components/review/quiz/ & ui/
  - 分離測驗邏輯組件 (quiz/) 和 UI 組件 (ui/)

- 修正 API 配置:
  - 更新 frontend/lib/config/api.ts: localhost:5008 → localhost:5000
  - 配合後端實際運行端口

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
鄭沛軒 2025-10-05 04:43:48 +08:00
parent 51e5870390
commit 3ff3b7f0a1
7 changed files with 19 additions and 19 deletions

View File

@ -2,10 +2,10 @@
import { Navigation } from '@/components/shared/Navigation' import { Navigation } from '@/components/shared/Navigation'
import './globals.css' import './globals.css'
import { SimpleFlipCard } from '@/components/review/simple/SimpleFlipCard' import { FlipMemory } from '@/components/review/quiz/FlipMemory'
import { VocabChoiceTest } from '@/components/review/simple/VocabChoiceTest' import { VocabChoiceQuiz } from '@/components/review/quiz/VocabChoiceQuiz'
import { SimpleProgress } from '@/components/review/simple/SimpleProgress' import { QuizProgress } from '@/components/review/ui/QuizProgress'
import { SimpleResults } from '@/components/review/simple/SimpleResults' import { QuizResult } from '@/components/review/quiz/QuizResult'
import { SIMPLE_CARDS } from '@/lib/data/reviewSimpleData' import { SIMPLE_CARDS } from '@/lib/data/reviewSimpleData'
import { useReviewSession } from '@/hooks/review/useReviewSession' import { useReviewSession } from '@/hooks/review/useReviewSession'
@ -32,7 +32,7 @@ export default function SimpleReviewPage() {
<Navigation /> <Navigation />
<div className="py-8"> <div className="py-8">
<div className="max-w-4xl mx-auto px-4"> <div className="max-w-4xl mx-auto px-4">
<SimpleResults <QuizResult
score={score} score={score}
totalCards={SIMPLE_CARDS.length} totalCards={SIMPLE_CARDS.length}
onRestart={handleRestart} onRestart={handleRestart}
@ -72,7 +72,7 @@ export default function SimpleReviewPage() {
<div className="py-8"> <div className="py-8">
<div className="max-w-4xl mx-auto px-4"> <div className="max-w-4xl mx-auto px-4">
{/* 使用修改後的 SimpleProgress 組件 */} {/* 使用修改後的 SimpleProgress 組件 */}
<SimpleProgress <QuizProgress
currentTestItem={currentTestItem} currentTestItem={currentTestItem}
totalTestItems={totalTestItems} totalTestItems={totalTestItems}
completedTestItems={completedTestItems} completedTestItems={completedTestItems}
@ -84,7 +84,7 @@ export default function SimpleReviewPage() {
{currentTestItem && currentCard && ( {currentTestItem && currentCard && (
<> <>
{currentTestItem.testType === 'flip-card' && ( {currentTestItem.testType === 'flip-card' && (
<SimpleFlipCard <FlipMemory
card={currentCard} card={currentCard}
onAnswer={handleAnswer} onAnswer={handleAnswer}
onSkip={handleSkip} onSkip={handleSkip}
@ -92,7 +92,7 @@ export default function SimpleReviewPage() {
)} )}
{currentTestItem.testType === 'vocab-choice' && ( {currentTestItem.testType === 'vocab-choice' && (
<VocabChoiceTest <VocabChoiceQuiz
card={currentCard} card={currentCard}
options={vocabOptions} options={vocabOptions}
onAnswer={handleAnswer} onAnswer={handleAnswer}

View File

@ -2,7 +2,7 @@
import { useState, useRef, useEffect, useCallback } from 'react' import { useState, useRef, useEffect, useCallback } from 'react'
import { CardState } from '@/lib/data/reviewSimpleData' import { CardState } from '@/lib/data/reviewSimpleData'
import { SimpleTestHeader } from './SimpleTestHeader' import { QuizHeader } from '../ui/QuizHeader'
interface SimpleFlipCardProps { interface SimpleFlipCardProps {
card: CardState card: CardState
@ -10,7 +10,7 @@ interface SimpleFlipCardProps {
onSkip: () => void onSkip: () => void
} }
export function SimpleFlipCard({ card, onAnswer, onSkip }: SimpleFlipCardProps) { export function FlipMemory({ card, onAnswer, onSkip }: SimpleFlipCardProps) {
const [isFlipped, setIsFlipped] = useState(false) const [isFlipped, setIsFlipped] = useState(false)
const [selectedConfidence, setSelectedConfidence] = useState<number | null>(null) const [selectedConfidence, setSelectedConfidence] = useState<number | null>(null)
const [cardHeight, setCardHeight] = useState<number>(400) const [cardHeight, setCardHeight] = useState<number>(400)
@ -103,7 +103,7 @@ export function SimpleFlipCard({ card, onAnswer, onSkip }: SimpleFlipCardProps)
style={{ backfaceVisibility: 'hidden' }} style={{ backfaceVisibility: 'hidden' }}
> >
<div className="p-8 h-full"> <div className="p-8 h-full">
<SimpleTestHeader <QuizHeader
title="翻卡記憶" title="翻卡記憶"
cefr={card.cefr} cefr={card.cefr}
/> />
@ -137,7 +137,7 @@ export function SimpleFlipCard({ card, onAnswer, onSkip }: SimpleFlipCardProps)
}} }}
> >
<div className="p-8 h-full"> <div className="p-8 h-full">
<SimpleTestHeader <QuizHeader
title="翻卡記憶" title="翻卡記憶"
cefr={card.cefr} cefr={card.cefr}
/> />

View File

@ -4,7 +4,7 @@ interface SimpleResultsProps {
onRestart: () => void onRestart: () => void
} }
export function SimpleResults({ score, totalCards, onRestart }: SimpleResultsProps) { export function QuizResult({ score, totalCards, onRestart }: SimpleResultsProps) {
const accuracy = Math.round((score.correct / score.total) * 100) const accuracy = Math.round((score.correct / score.total) * 100)
const getPerformanceMessage = () => { const getPerformanceMessage = () => {

View File

@ -2,7 +2,7 @@
import { useState, useCallback } from 'react' import { useState, useCallback } from 'react'
import { CardState } from '@/lib/data/reviewSimpleData' import { CardState } from '@/lib/data/reviewSimpleData'
import { SimpleTestHeader } from './SimpleTestHeader' import { QuizHeader } from '../ui/QuizHeader'
interface VocabChoiceTestProps { interface VocabChoiceTestProps {
card: CardState card: CardState
@ -11,7 +11,7 @@ interface VocabChoiceTestProps {
onSkip: () => void onSkip: () => void
} }
export function VocabChoiceTest({ card, options, onAnswer, onSkip }: VocabChoiceTestProps) { export function VocabChoiceQuiz({ card, options, onAnswer, onSkip }: VocabChoiceTestProps) {
const [selectedAnswer, setSelectedAnswer] = useState<string | null>(null) const [selectedAnswer, setSelectedAnswer] = useState<string | null>(null)
const [showResult, setShowResult] = useState(false) const [showResult, setShowResult] = useState(false)
@ -43,7 +43,7 @@ export function VocabChoiceTest({ card, options, onAnswer, onSkip }: VocabChoice
return ( return (
<div> <div>
<div className="bg-white rounded-xl shadow-lg p-8"> <div className="bg-white rounded-xl shadow-lg p-8">
<SimpleTestHeader <QuizHeader
title="詞彙選擇" title="詞彙選擇"
cefr={card.cefr} cefr={card.cefr}
/> />

View File

@ -5,7 +5,7 @@ interface SimpleTestHeaderProps {
cefr: string cefr: string
} }
export function SimpleTestHeader({ title, cefr }: SimpleTestHeaderProps) { export function QuizHeader({ title, cefr }: SimpleTestHeaderProps) {
return ( return (
<div className="flex justify-between items-start mb-6"> <div className="flex justify-between items-start mb-6">
<h2 className="text-2xl font-bold text-gray-900">{title}</h2> <h2 className="text-2xl font-bold text-gray-900">{title}</h2>

View File

@ -8,7 +8,7 @@ interface SimpleProgressProps {
testItems?: TestItem[] // 用於顯示測驗項目統計 testItems?: TestItem[] // 用於顯示測驗項目統計
} }
export function SimpleProgress({ currentTestItem, totalTestItems, completedTestItems, score, testItems }: SimpleProgressProps) { export function QuizProgress({ currentTestItem, totalTestItems, completedTestItems, score, testItems }: SimpleProgressProps) {
const progress = (completedTestItems / totalTestItems) * 100 const progress = (completedTestItems / totalTestItems) * 100
const accuracy = score.total > 0 ? Math.round((score.correct / score.total) * 100) : 0 const accuracy = score.total > 0 ? Math.round((score.correct / score.total) * 100) : 0

View File

@ -5,7 +5,7 @@
// API基礎配置 // API基礎配置
export const API_CONFIG = { export const API_CONFIG = {
BASE_URL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5008', BASE_URL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000',
ENDPOINTS: { ENDPOINTS: {
AUTH: '/api/auth', AUTH: '/api/auth',
FLASHCARDS: '/api', FLASHCARDS: '/api',