132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
import React, { useState } from 'react'
|
|
import { Modal } from '@/components/ui/Modal'
|
|
import { ContentBlock } from '@/components/shared/ContentBlock'
|
|
import { BluePlayButton } from '@/components/shared/BluePlayButton'
|
|
|
|
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 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>
|
|
<BluePlayButton
|
|
text={analysis.idiom}
|
|
lang="en-US"
|
|
size="sm"
|
|
title="播放發音"
|
|
/>
|
|
</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>
|
|
)
|
|
} |