186 lines
7.5 KiB
TypeScript
186 lines
7.5 KiB
TypeScript
'use client'
|
||
|
||
import { useState } from 'react'
|
||
|
||
export default function TestApiPage() {
|
||
const [textInput, setTextInput] = useState('')
|
||
const [isLoading, setIsLoading] = useState(false)
|
||
const [result, setResult] = useState<any>(null)
|
||
const [error, setError] = useState<string | null>(null)
|
||
|
||
const handleTest = async () => {
|
||
if (!textInput.trim()) return
|
||
|
||
setIsLoading(true)
|
||
setError(null)
|
||
setResult(null)
|
||
|
||
try {
|
||
console.log('發送API請求到:', 'http://localhost:5000/api/ai/analyze-sentence')
|
||
console.log('請求數據:', { inputText: textInput, analysisMode: 'full' })
|
||
|
||
const response = await fetch('http://localhost:5000/api/ai/analyze-sentence', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
inputText: textInput,
|
||
analysisMode: 'full'
|
||
})
|
||
})
|
||
|
||
console.log('API響應狀態:', response.status, response.statusText)
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`API 錯誤: ${response.status} ${response.statusText}`)
|
||
}
|
||
|
||
const result = await response.json()
|
||
console.log('API響應數據:', result)
|
||
|
||
setResult(result)
|
||
|
||
if (result.success) {
|
||
console.log('✅ API調用成功')
|
||
} else {
|
||
console.log('❌ API返回失敗:', result.error)
|
||
setError(result.error)
|
||
}
|
||
} catch (error) {
|
||
console.error('❌ API調用錯誤:', error)
|
||
setError(error instanceof Error ? error.message : '未知錯誤')
|
||
} finally {
|
||
setIsLoading(false)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-gray-50 p-8">
|
||
<div className="max-w-4xl mx-auto">
|
||
<h1 className="text-3xl font-bold mb-8">API 連接測試</h1>
|
||
|
||
{/* 輸入區域 */}
|
||
<div className="bg-white rounded-lg shadow p-6 mb-6">
|
||
<h2 className="text-lg font-semibold mb-4">測試句子分析API</h2>
|
||
<div className="space-y-4">
|
||
<textarea
|
||
value={textInput}
|
||
onChange={(e) => setTextInput(e.target.value)}
|
||
placeholder="輸入英文句子進行測試..."
|
||
className="w-full h-32 px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-600 focus:border-transparent outline-none resize-none"
|
||
/>
|
||
|
||
<div className="flex gap-2">
|
||
<button
|
||
onClick={() => setTextInput("He brought this thing up during our meeting.")}
|
||
className="px-4 py-2 bg-green-100 text-green-700 rounded-lg text-sm hover:bg-green-200"
|
||
>
|
||
使用正確語法示例
|
||
</button>
|
||
<button
|
||
onClick={() => setTextInput("I go to school yesterday and meet my friends.")}
|
||
className="px-4 py-2 bg-red-100 text-red-700 rounded-lg text-sm hover:bg-red-200"
|
||
>
|
||
使用語法錯誤示例
|
||
</button>
|
||
</div>
|
||
|
||
<button
|
||
onClick={handleTest}
|
||
disabled={isLoading || !textInput.trim()}
|
||
className="w-full bg-blue-600 text-white py-3 rounded-lg font-semibold hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||
>
|
||
{isLoading ? (
|
||
<span className="flex items-center justify-center">
|
||
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||
</svg>
|
||
正在測試API連接...
|
||
</span>
|
||
) : (
|
||
'🔍 測試 API 連接'
|
||
)}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 錯誤顯示 */}
|
||
{error && (
|
||
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
|
||
<h3 className="text-red-800 font-semibold mb-2">❌ 錯誤</h3>
|
||
<p className="text-red-700">{error}</p>
|
||
</div>
|
||
)}
|
||
|
||
{/* 結果顯示 */}
|
||
{result && (
|
||
<div className="bg-white rounded-lg shadow p-6">
|
||
<h3 className="text-lg font-semibold mb-4">📋 API 響應結果</h3>
|
||
|
||
{result.success ? (
|
||
<div className="space-y-4">
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-green-600 text-lg">✅</span>
|
||
<span className="font-medium">API 調用成功</span>
|
||
{result.cached && <span className="text-blue-600 text-sm">(使用快取)</span>}
|
||
{result.cacheHit && <span className="text-purple-600 text-sm">(快取命中)</span>}
|
||
</div>
|
||
|
||
{/* 語法檢查結果 */}
|
||
{result.data?.grammarCorrection && (
|
||
<div className="p-3 bg-gray-50 rounded-lg">
|
||
<h4 className="font-medium mb-2">語法檢查</h4>
|
||
{result.data.grammarCorrection.hasErrors ? (
|
||
<div className="text-red-600">
|
||
❌ 發現語法錯誤:{result.data.grammarCorrection.corrections?.length || 0} 個
|
||
</div>
|
||
) : (
|
||
<div className="text-green-600">✅ 語法正確</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
{/* 句子意思 */}
|
||
{result.data?.sentenceMeaning && (
|
||
<div className="p-3 bg-blue-50 rounded-lg">
|
||
<h4 className="font-medium mb-2">句子翻譯</h4>
|
||
<p className="text-blue-800">{result.data.sentenceMeaning.translation}</p>
|
||
<p className="text-blue-600 text-sm mt-1">{result.data.sentenceMeaning.explanation}</p>
|
||
</div>
|
||
)}
|
||
|
||
{/* 高價值詞彙 */}
|
||
{result.data?.highValueWords && (
|
||
<div className="p-3 bg-green-50 rounded-lg">
|
||
<h4 className="font-medium mb-2">高價值詞彙</h4>
|
||
<div className="flex flex-wrap gap-2">
|
||
{result.data.highValueWords.map((word: string, idx: number) => (
|
||
<span key={idx} className="bg-green-200 text-green-800 px-2 py-1 rounded text-sm">
|
||
{word} ⭐
|
||
</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* 原始響應(調試用) */}
|
||
<details className="mt-4">
|
||
<summary className="cursor-pointer text-gray-600 text-sm">查看原始JSON響應</summary>
|
||
<pre className="mt-2 p-3 bg-gray-100 rounded text-xs overflow-auto">
|
||
{JSON.stringify(result, null, 2)}
|
||
</pre>
|
||
</details>
|
||
</div>
|
||
) : (
|
||
<div className="text-red-600">
|
||
❌ API 返回失敗: {result.error || '未知錯誤'}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
} |