dramaling-vocab-learning/frontend/app/flashcards/page.tsx

852 lines
40 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useState } from 'react'
import Link from 'next/link'
export default function FlashcardsPage() {
const [activeTab, setActiveTab] = useState('my-cards')
const [selectedSet, setSelectedSet] = useState<number | null>(null)
const [searchTerm, setSearchTerm] = useState('')
const [filterTag, setFilterTag] = useState('all')
const [selectedCards, setSelectedCards] = useState<number[]>([])
const [showCheckModal, setShowCheckModal] = useState(false)
const [checkingCard, setCheckingCard] = useState<any>(null)
const [checkResults, setCheckResults] = useState<any>(null)
const [errorReports, setErrorReports] = useState<any[]>([
{
id: 1,
cardId: 1,
word: 'negotiate',
reportType: '發音錯誤',
description: '美式發音標記有誤',
reportedAt: '2小時前',
status: 'pending'
},
{
id: 2,
cardId: 3,
word: 'perspective',
reportType: '翻譯不準確',
description: '翻譯缺少其他常用含義',
reportedAt: '1天前',
status: 'pending'
},
{
id: 3,
cardId: 5,
word: 'implement',
reportType: '例句錯誤',
description: '例句時態使用不當',
reportedAt: '3天前',
status: 'pending'
}
])
// Mock data
const cardSets = [
{
id: 1,
name: '美劇經典台詞',
description: '從熱門美劇中精選的實用對話',
cardCount: 45,
progress: 60,
lastStudied: '2 小時前',
tags: ['影視', '口語'],
color: 'bg-blue-500'
},
{
id: 2,
name: '商務英文必備',
description: '職場溝通和商業會議常用詞彙',
cardCount: 30,
progress: 30,
lastStudied: '昨天',
tags: ['商務', '正式'],
color: 'bg-purple-500'
},
{
id: 3,
name: '日常對話',
description: '生活中最常用的英文表達',
cardCount: 25,
progress: 80,
lastStudied: '3 天前',
tags: ['日常', '基礎'],
color: 'bg-green-500'
},
{
id: 4,
name: 'TOEFL 核心詞彙',
description: '托福考試高頻詞彙整理',
cardCount: 100,
progress: 15,
lastStudied: '1 週前',
tags: ['考試', '學術'],
color: 'bg-orange-500'
},
{
id: 5,
name: '科技新聞詞彙',
description: '科技領域專業術語和流行用語',
cardCount: 35,
progress: 45,
lastStudied: '5 天前',
tags: ['科技', '專業'],
color: 'bg-indigo-500'
}
]
const flashcards = [
{
id: 1,
word: 'negotiate',
translation: '協商',
definition: 'To discuss something with someone in order to reach an agreement',
partOfSpeech: 'verb',
pronunciation: '/nɪˈɡoʊʃieɪt/',
example: 'We need to negotiate a better deal with our suppliers.',
setId: 1,
mastery: 80,
nextReview: '明天'
},
{
id: 2,
word: 'accomplish',
translation: '完成',
definition: 'To finish something successfully or to achieve something',
partOfSpeech: 'verb',
pronunciation: '/əˈkɒmplɪʃ/',
example: 'She accomplished her goal of running a marathon.',
setId: 1,
mastery: 60,
nextReview: '今天'
},
{
id: 3,
word: 'perspective',
translation: '觀點',
definition: 'A particular way of considering something',
partOfSpeech: 'noun',
pronunciation: '/pərˈspektɪv/',
example: 'From my perspective, this is the best solution.',
setId: 2,
mastery: 90,
nextReview: '3天後'
},
{
id: 4,
word: 'substantial',
translation: '大量的',
definition: 'Large in size, value, or importance',
partOfSpeech: 'adjective',
pronunciation: '/səbˈstænʃəl/',
example: 'The company made a substantial profit last year.',
setId: 2,
mastery: 40,
nextReview: '今天'
},
{
id: 5,
word: 'implement',
translation: '實施',
definition: 'To put a decision, plan, or agreement into effect',
partOfSpeech: 'verb',
pronunciation: '/ˈɪmplɪment/',
example: 'We need to implement new safety measures.',
setId: 3,
mastery: 70,
nextReview: '明天'
},
]
const tags = ['all', '影視', '商務', '日常', '考試', '科技', '口語', '正式', '基礎', '學術', '專業']
const filteredSets = cardSets.filter(set =>
set.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
set.description.toLowerCase().includes(searchTerm.toLowerCase())
)
const filteredCards = flashcards.filter(card =>
card.word.toLowerCase().includes(searchTerm.toLowerCase()) ||
card.translation.includes(searchTerm)
)
return (
<div className="min-h-screen bg-gray-50">
{/* Navigation */}
<nav className="bg-white shadow-sm border-b">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
<div className="flex items-center space-x-8">
<Link href="/dashboard" className="text-2xl font-bold text-primary">DramaLing</Link>
<div className="hidden md:flex space-x-6">
<Link href="/dashboard" className="text-gray-600 hover:text-gray-900"></Link>
<Link href="/flashcards" className="text-gray-900 font-medium"></Link>
<Link href="/learn" className="text-gray-600 hover:text-gray-900"></Link>
<Link href="/generate" className="text-gray-600 hover:text-gray-900">AI </Link>
</div>
</div>
<div className="flex items-center space-x-4">
<Link
href="/generate"
className="bg-primary text-white px-4 py-2 rounded-lg hover:bg-primary-hover transition-colors text-sm font-medium"
>
+
</Link>
</div>
</div>
</div>
</nav>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header */}
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-2"></h1>
<p className="text-gray-600"></p>
</div>
{/* Search and Filter */}
<div className="bg-white rounded-lg shadow-sm p-4 mb-6">
<div className="flex flex-col md:flex-row gap-4">
<div className="flex-1">
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="搜尋詞卡或卡組..."
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent outline-none"
/>
</div>
<div className="flex gap-2">
<select
value={filterTag}
onChange={(e) => setFilterTag(e.target.value)}
className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent outline-none"
>
{tags.map(tag => (
<option key={tag} value={tag}>
{tag === 'all' ? '所有標籤' : tag}
</option>
))}
</select>
<button className="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
</button>
</div>
</div>
</div>
{/* Tabs */}
<div className="bg-white rounded-lg shadow-sm mb-6">
<div className="border-b border-gray-200">
<nav className="-mb-px flex space-x-8 px-6" aria-label="Tabs">
<button
onClick={() => setActiveTab('my-cards')}
className={`py-4 px-1 border-b-2 font-medium text-sm ${
activeTab === 'my-cards'
? 'border-primary text-primary'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
</button>
<button
onClick={() => setActiveTab('all-cards')}
className={`py-4 px-1 border-b-2 font-medium text-sm ${
activeTab === 'all-cards'
? 'border-primary text-primary'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
</button>
<button
onClick={() => setActiveTab('favorites')}
className={`py-4 px-1 border-b-2 font-medium text-sm ${
activeTab === 'favorites'
? 'border-primary text-primary'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
</button>
<button
onClick={() => setActiveTab('error-reports')}
className={`py-4 px-1 border-b-2 font-medium text-sm relative ${
activeTab === 'error-reports'
? 'border-primary text-primary'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
{errorReports.filter(r => r.status === 'pending').length > 0 && (
<span className="absolute -top-1 -right-2 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center">
{errorReports.filter(r => r.status === 'pending').length}
</span>
)}
</button>
</nav>
</div>
<div className="p-6">
{activeTab === 'my-cards' && (
<div>
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold"> {filteredSets.length} </h3>
<div className="flex gap-2">
<button className="p-2 text-gray-600 hover:text-gray-900">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 10h16M4 14h16M4 18h16" />
</svg>
</button>
<button className="p-2 text-gray-600 hover:text-gray-900">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
</svg>
</button>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{filteredSets.map(set => (
<div
key={set.id}
className="border rounded-lg hover:shadow-lg transition-shadow cursor-pointer"
onClick={() => setSelectedSet(set.id)}
>
<div className={`h-2 ${set.color} rounded-t-lg`}></div>
<div className="p-4">
<div className="flex justify-between items-start mb-2">
<h4 className="font-semibold text-lg">{set.name}</h4>
<button className="text-gray-400 hover:text-gray-600">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z" />
</svg>
</button>
</div>
<p className="text-sm text-gray-600 mb-3">{set.description}</p>
<div className="flex flex-wrap gap-1 mb-3">
{set.tags.map(tag => (
<span key={tag} className="px-2 py-1 bg-gray-100 text-gray-600 text-xs rounded-full">
{tag}
</span>
))}
</div>
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span className="text-gray-600"></span>
<span className="font-medium">{set.progress}%</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-primary h-2 rounded-full transition-all"
style={{ width: `${set.progress}%` }}
></div>
</div>
<div className="flex justify-between text-sm text-gray-600">
<span>{set.cardCount} </span>
<span>{set.lastStudied}</span>
</div>
</div>
<div className="mt-4 flex gap-2">
<Link
href={`/learn?set=${set.id}`}
className="flex-1 bg-primary text-white text-center py-2 rounded-lg hover:bg-primary-hover transition-colors text-sm font-medium"
>
</Link>
<button className="flex-1 border border-gray-300 py-2 rounded-lg hover:bg-gray-50 transition-colors text-sm font-medium">
</button>
</div>
</div>
</div>
))}
{/* Add New Set Card */}
<div className="border-2 border-dashed border-gray-300 rounded-lg hover:border-gray-400 transition-colors cursor-pointer flex items-center justify-center min-h-[280px]">
<div className="text-center">
<div className="w-12 h-12 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-3">
<svg className="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
</div>
<p className="text-gray-600 font-medium"></p>
<p className="text-sm text-gray-500 mt-1"></p>
</div>
</div>
</div>
</div>
)}
{activeTab === 'all-cards' && (
<div>
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold"> {filteredCards.length} </h3>
<div className="flex gap-2">
{selectedCards.length > 0 && (
<button
onClick={() => {
setCheckResults(null)
setShowCheckModal(true)
}}
className="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600 transition-colors text-sm font-medium flex items-center gap-2"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
({selectedCards.length})
</button>
)}
<button className="text-primary hover:text-primary-hover font-medium text-sm">
</button>
</div>
</div>
<div className="space-y-3">
{filteredCards.map(card => (
<div key={card.id} className="bg-white border rounded-lg p-4 hover:shadow-md transition-shadow">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<input
type="checkbox"
checked={selectedCards.includes(card.id)}
onChange={(e) => {
if (e.target.checked) {
setSelectedCards([...selectedCards, card.id])
} else {
setSelectedCards(selectedCards.filter(id => id !== card.id))
}
}}
className="h-4 w-4 text-primary focus:ring-primary border-gray-300 rounded"
/>
<div
onClick={() => {
setCheckingCard(card)
setCheckResults(null)
setShowCheckModal(true)
}}
className="cursor-pointer hover:text-primary transition-colors"
>
<div className="font-semibold">{card.word}</div>
<div className="text-sm text-gray-600">{card.translation}</div>
</div>
</div>
<div className="flex items-center space-x-4">
<div className="text-right">
<div className="text-sm text-gray-600"></div>
<div className="font-medium">{card.mastery}%</div>
</div>
<div className="text-right">
<div className="text-sm text-gray-600"></div>
<div className="text-sm font-medium text-primary">{card.nextReview}</div>
</div>
<button className="text-gray-400 hover:text-gray-600">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg>
</button>
</div>
</div>
</div>
))}
</div>
</div>
)}
{activeTab === 'favorites' && (
<div className="text-center py-12">
<div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg>
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2"></h3>
<p className="text-gray-600 mb-4"></p>
<Link
href="/learn"
className="inline-block bg-primary text-white px-6 py-2 rounded-lg hover:bg-primary-hover transition-colors"
>
</Link>
</div>
)}
{activeTab === 'error-reports' && (
<div>
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold">
({errorReports.filter(r => r.status === 'pending').length} )
</h3>
{errorReports.filter(r => r.status === 'pending').length > 0 && (
<button
onClick={() => {
// 一鍵檢測所有錯誤回報
const pendingReports = errorReports.filter(r => r.status === 'pending')
setCheckingCard({
isErrorList: true,
reports: pendingReports
})
setCheckResults(null)
setShowCheckModal(true)
}}
className="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600 transition-colors text-sm font-medium flex items-center gap-2"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</button>
)}
</div>
{errorReports.length === 0 ? (
<div className="text-center py-12">
<div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2"></h3>
<p className="text-gray-600"></p>
</div>
) : (
<div className="space-y-3">
{errorReports.map(report => (
<div
key={report.id}
className={`bg-white border rounded-lg p-4 hover:shadow-md transition-shadow ${
report.status === 'resolved' ? 'opacity-60' : ''
}`}
>
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h4 className="font-semibold text-lg">{report.word}</h4>
<span className={`px-2 py-1 rounded-full text-xs ${
report.status === 'pending'
? 'bg-yellow-100 text-yellow-700'
: 'bg-green-100 text-green-700'
}`}>
{report.status === 'pending' ? '待處理' : '已解決'}
</span>
<span className="px-2 py-1 bg-red-100 text-red-700 rounded-full text-xs">
{report.reportType}
</span>
</div>
<p className="text-gray-600 mb-2">{report.description}</p>
<div className="text-sm text-gray-500">
{report.reportedAt}
</div>
</div>
<div className="flex gap-2">
{report.status === 'pending' && (
<>
<button
onClick={() => {
// 檢測單個錯誤
const card = flashcards.find(c => c.id === report.cardId)
if (card) {
setCheckingCard({
...card,
errorReport: report
})
setCheckResults(null)
setShowCheckModal(true)
}
}}
className="text-green-600 hover:text-green-700 p-2"
title="檢測此錯誤"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</button>
<button
onClick={() => {
// 標記為已解決
setErrorReports(errorReports.map(r =>
r.id === report.id ? { ...r, status: 'resolved' } : r
))
}}
className="text-gray-400 hover:text-gray-600 p-2"
title="忽略此錯誤"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</>
)}
</div>
</div>
</div>
))}
</div>
)}
</div>
)}
</div>
</div>
{/* Stats Summary */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="bg-white rounded-lg shadow-sm p-4">
<div className="flex items-center justify-between">
<div>
<div className="text-2xl font-bold">234</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center">
<svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow-sm p-4">
<div className="flex items-center justify-between">
<div>
<div className="text-2xl font-bold">156</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center">
<svg className="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow-sm p-4">
<div className="flex items-center justify-between">
<div>
<div className="text-2xl font-bold">23</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="w-12 h-12 bg-yellow-100 rounded-full flex items-center justify-center">
<svg className="w-6 h-6 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow-sm p-4">
<div className="flex items-center justify-between">
<div>
<div className="text-2xl font-bold">67%</div>
<div className="text-sm text-gray-600"></div>
</div>
<div className="w-12 h-12 bg-purple-100 rounded-full flex items-center justify-center">
<svg className="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
</svg>
</div>
</div>
</div>
</div>
</div>
{/* Smart Check Modal */}
{showCheckModal && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50 p-4">
<div className="bg-white rounded-xl max-w-2xl w-full max-h-[80vh] overflow-y-auto">
<div className="p-6 border-b">
<div className="flex items-center justify-between">
<h2 className="text-xl font-bold">🤖 </h2>
<button
onClick={() => {
setShowCheckModal(false)
setCheckingCard(null)
setCheckResults(null)
}}
className="text-gray-400 hover:text-gray-600"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<div className="p-6">
{!checkResults ? (
// 檢測前
<div>
<div className="mb-4">
<p className="text-gray-600 mb-4">
{checkingCard?.isErrorList
? `即將檢測 ${checkingCard.reports.length} 個錯誤回報`
: checkingCard?.errorReport
? `正在檢測錯誤回報:${checkingCard.errorReport.reportType}`
: checkingCard
? `正在檢測詞卡「${checkingCard.word}」的內容...`
: `即將檢測 ${selectedCards.length} 個詞卡的內容正確性`}
</p>
</div>
{checkingCard && !checkingCard.isErrorList && (
<div className="bg-gray-50 rounded-lg p-4 mb-4">
{checkingCard.errorReport && (
<div className="mb-3 p-3 bg-yellow-50 border border-yellow-200 rounded">
<div className="text-sm font-medium text-yellow-800 mb-1">
{checkingCard.errorReport.reportType}
</div>
<div className="text-sm text-yellow-700">
{checkingCard.errorReport.description}
</div>
</div>
)}
<div className="space-y-2">
<div><strong></strong>{checkingCard.word}</div>
<div><strong></strong>{checkingCard.translation}</div>
<div><strong></strong>{checkingCard.definition}</div>
<div><strong></strong>{checkingCard.partOfSpeech}</div>
<div><strong></strong>{checkingCard.pronunciation}</div>
<div><strong></strong>{checkingCard.example}</div>
</div>
</div>
)}
{checkingCard?.isErrorList && (
<div className="bg-gray-50 rounded-lg p-4 mb-4 max-h-60 overflow-y-auto">
<div className="text-sm font-medium text-gray-700 mb-2"></div>
<div className="space-y-2">
{checkingCard.reports.map((report, idx) => (
<div key={idx} className="bg-white p-2 rounded border border-gray-200">
<div className="font-medium">{report.word}</div>
<div className="text-xs text-gray-600">
{report.reportType}: {report.description}
</div>
</div>
))}
</div>
</div>
)}
<button
onClick={() => {
// 模擬檢測過程
setTimeout(() => {
const isErrorList = checkingCard?.isErrorList
const totalCount = isErrorList
? checkingCard.reports.length
: checkingCard
? 1
: selectedCards.length
setCheckResults({
totalChecked: totalCount,
isErrorList: isErrorList,
errors: [
{
field: '發音',
original: '/nɪˈɡoʊʃieɪt/',
corrected: '/nɪˈɡəʊʃieɪt/',
reason: '英式發音標記錯誤'
},
{
field: '例句',
original: 'We need to negotiate a better deal with our suppliers.',
corrected: 'We need to negotiate a better deal with our suppliers.',
reason: '例句文法正確,但可加入更多上下文'
}
],
suggestions: [
'建議添加同義詞bargain, discuss',
'建議添加反義詞refuse, reject',
'可補充詞根詞綴說明'
]
})
}, 2000)
}}
className="w-full bg-green-500 text-white py-3 rounded-lg hover:bg-green-600 transition-colors font-medium"
>
</button>
</div>
) : (
// 檢測結果
<div>
<div className="mb-4">
<div className="flex items-center gap-2 mb-2">
<svg className="w-6 h-6 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<h3 className="text-lg font-semibold"></h3>
</div>
<p className="text-gray-600">
{checkResults.isErrorList
? `已檢測 ${checkResults.totalChecked} 個錯誤回報,發現 ${checkResults.errors.length} 個問題`
: `已檢測 ${checkResults.totalChecked} 個詞卡,發現 ${checkResults.errors.length} 個問題`}
</p>
</div>
{checkResults.errors.length > 0 && (
<div className="mb-4">
<h4 className="font-semibold mb-2">📝 </h4>
<div className="space-y-3">
{checkResults.errors.map((error, idx) => (
<div key={idx} className="bg-yellow-50 border border-yellow-200 rounded-lg p-3">
<div className="font-medium text-yellow-800 mb-1">{error.field}</div>
<div className="text-sm space-y-1">
<div><span className="text-gray-600"></span> {error.original}</div>
<div><span className="text-gray-600"></span> <span className="text-green-600">{error.corrected}</span></div>
<div className="text-xs text-gray-500">{error.reason}</div>
</div>
</div>
))}
</div>
</div>
)}
{checkResults.suggestions.length > 0 && (
<div className="mb-4">
<h4 className="font-semibold mb-2">💡 </h4>
<ul className="space-y-1">
{checkResults.suggestions.map((suggestion, idx) => (
<li key={idx} className="flex items-start gap-2 text-sm text-gray-600">
<span className="text-primary"></span>
{suggestion}
</li>
))}
</ul>
</div>
)}
<div className="flex gap-3">
<button
onClick={() => {
alert('已自動修正所有問題!')
setShowCheckModal(false)
setCheckResults(null)
setCheckingCard(null)
}}
className="flex-1 bg-primary text-white py-2 rounded-lg hover:bg-primary-hover transition-colors font-medium"
>
</button>
<button
onClick={() => {
setShowCheckModal(false)
setCheckResults(null)
setCheckingCard(null)
}}
className="flex-1 border border-gray-300 py-2 rounded-lg hover:bg-gray-50 transition-colors font-medium"
>
</button>
</div>
</div>
)}
</div>
</div>
</div>
)}
</div>
)
}