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:
parent
51e5870390
commit
3ff3b7f0a1
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
@ -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 = () => {
|
||||||
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue