dramaling-vocab-learning/frontend/components/generate/IdiomDetailModal.tsx

140 lines
4.4 KiB
TypeScript

import React from 'react'
import { Play } from 'lucide-react'
import { Modal } from '@/components/ui/Modal'
import { ContentBlock } from '@/components/shared/ContentBlock'
interface IdiomAnalysis {
idiom: string
translation: string
definition: string
pronunciation?: string
cefr?: string
example?: string
exampleTranslation?: string
synonyms?: string[]
[key: string]: any
}
interface IdiomPopup {
idiom: string
analysis: IdiomAnalysis
position: { x: number; y: number }
}
interface IdiomDetailModalProps {
idiomPopup: IdiomPopup | null
onClose: () => void
onSaveIdiom?: (idiom: string, analysis: IdiomAnalysis) => Promise<{ success: boolean; error?: string }>
className?: string
}
export const IdiomDetailModal: React.FC<IdiomDetailModalProps> = ({
idiomPopup,
onClose,
onSaveIdiom,
className = ''
}) => {
if (!idiomPopup) {
return null
}
const { analysis } = idiomPopup
const handlePlayPronunciation = () => {
const utterance = new SpeechSynthesisUtterance(analysis.idiom)
utterance.lang = 'en-US'
utterance.rate = 0.8
speechSynthesis.speak(utterance)
}
const handleSave = async () => {
if (onSaveIdiom) {
await onSaveIdiom(idiomPopup.idiom, analysis)
}
}
return (
<Modal isOpen={!!idiomPopup} onClose={onClose} size="md">
{/* Header */}
<div className="bg-gradient-to-br from-blue-50 to-indigo-50 p-5 border-b border-blue-200">
<div className="mb-3">
<h3 className="text-2xl font-bold text-gray-900">{analysis.idiom}</h3>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="flex items-center gap-2">
<span className="text-base text-gray-600">{analysis.pronunciation}</span>
<button
onClick={handlePlayPronunciation}
className="flex items-center justify-center w-8 h-8 rounded-full bg-blue-600 hover:bg-blue-700 text-white transition-colors"
title="播放發音"
>
<Play size={16} />
</button>
</div>
</div>
<span className="px-3 py-1 rounded-full text-sm font-medium border bg-blue-100 text-blue-700 border-blue-200">
{analysis.cefr || 'A1'}
</span>
</div>
</div>
{/* Content */}
<div className="p-4 space-y-4 max-h-96 overflow-y-auto">
{/* Translation */}
<ContentBlock title="中文翻譯" variant="green">
<p className="text-green-800 font-medium text-left">{analysis.translation}</p>
</ContentBlock>
{/* Definition */}
<ContentBlock title="英文定義" variant="gray">
<p className="text-gray-700 text-left text-sm leading-relaxed">{analysis.definition}</p>
</ContentBlock>
{/* Example */}
{analysis.example && (
<ContentBlock title="例句" variant="blue">
<div className="space-y-2">
<p className="text-blue-800 text-left text-sm italic">
"{analysis.example}"
</p>
<p className="text-blue-700 text-left text-sm">
{analysis.exampleTranslation}
</p>
</div>
</ContentBlock>
)}
{/* Synonyms */}
{analysis.synonyms && Array.isArray(analysis.synonyms) && analysis.synonyms.length > 0 && (
<ContentBlock title="相關詞彙" variant="purple">
<div className="flex flex-wrap gap-2">
{analysis.synonyms.map((synonym: string, index: number) => (
<span
key={index}
className="bg-purple-100 text-purple-700 px-2 py-1 rounded-full text-xs font-medium"
>
{synonym}
</span>
))}
</div>
</ContentBlock>
)}
</div>
{/* Save Button */}
{onSaveIdiom && (
<div className="p-4 pt-2">
<button
onClick={handleSave}
className="w-full bg-primary text-white py-3 rounded-lg font-medium hover:bg-primary-hover transition-colors flex items-center justify-center gap-2"
>
<span className="font-medium"></span>
</button>
</div>
)}
</Modal>
)
}