diff --git a/backend/DramaLing.Api/Controllers/SimplifiedFlashcardsController.cs b/backend/DramaLing.Api/Controllers/FlashcardsController.cs similarity index 93% rename from backend/DramaLing.Api/Controllers/SimplifiedFlashcardsController.cs rename to backend/DramaLing.Api/Controllers/FlashcardsController.cs index a243694..54b7098 100644 --- a/backend/DramaLing.Api/Controllers/SimplifiedFlashcardsController.cs +++ b/backend/DramaLing.Api/Controllers/FlashcardsController.cs @@ -8,14 +8,14 @@ using System.Security.Claims; namespace DramaLing.Api.Controllers; [ApiController] -[Route("api/flashcards-simple")] +[Route("api/flashcards")] [AllowAnonymous] // 暫時移除認證要求,修復網路錯誤 -public class SimplifiedFlashcardsController : ControllerBase +public class FlashcardsController : ControllerBase { private readonly DramaLingDbContext _context; - private readonly ILogger _logger; + private readonly ILogger _logger; - public SimplifiedFlashcardsController(DramaLingDbContext context, ILogger logger) + public FlashcardsController(DramaLingDbContext context, ILogger logger) { _context = context; _logger = logger; @@ -105,7 +105,7 @@ public class SimplifiedFlashcardsController : ControllerBase } [HttpPost] - public async Task CreateFlashcard([FromBody] CreateSimpleFlashcardRequest request) + public async Task CreateFlashcard([FromBody] CreateFlashcardRequest request) { try { @@ -250,7 +250,7 @@ public class SimplifiedFlashcardsController : ControllerBase } [HttpPut("{id}")] - public async Task UpdateFlashcard(Guid id, [FromBody] CreateSimpleFlashcardRequest request) + public async Task UpdateFlashcard(Guid id, [FromBody] CreateFlashcardRequest request) { try { @@ -359,8 +359,8 @@ public class SimplifiedFlashcardsController : ControllerBase } } -// 簡化的請求 DTO,移除 CardSetId -public class CreateSimpleFlashcardRequest +// 請求 DTO +public class CreateFlashcardRequest { public string Word { get; set; } = string.Empty; public string Translation { get; set; } = string.Empty; diff --git a/frontend/app/flashcards/[id]/page.tsx b/frontend/app/flashcards/[id]/page.tsx index 0791485..6378cce 100644 --- a/frontend/app/flashcards/[id]/page.tsx +++ b/frontend/app/flashcards/[id]/page.tsx @@ -4,7 +4,7 @@ import { useState, useEffect, use } from 'react' import { useRouter } from 'next/navigation' import { Navigation } from '@/components/Navigation' import { ProtectedRoute } from '@/components/ProtectedRoute' -import { flashcardsService, type Flashcard } from '@/lib/services/flashcards' +import { flashcardsService, type Flashcard } from '@/lib/services/simplifiedFlashcards' interface FlashcardDetailPageProps { params: Promise<{ diff --git a/frontend/app/flashcards/page.tsx b/frontend/app/flashcards/page.tsx index 323dc32..6496eca 100644 --- a/frontend/app/flashcards/page.tsx +++ b/frontend/app/flashcards/page.tsx @@ -6,7 +6,7 @@ import { ProtectedRoute } from '@/components/ProtectedRoute' import { Navigation } from '@/components/Navigation' import { FlashcardForm } from '@/components/FlashcardForm' // import { flashcardsService, type CardSet, type Flashcard } from '@/lib/services/flashcards' -import { simplifiedFlashcardsService, type SimpleFlashcard } from '@/lib/services/simplifiedFlashcards' +import { flashcardsService, type Flashcard } from '@/lib/services/flashcards' // 暫時為了兼容性定義 CardSet 類型 type CardSet = { @@ -16,7 +16,7 @@ type CardSet = { } // 使用簡化的 Flashcard 類型 -type Flashcard = SimpleFlashcard +type Flashcard = Flashcard import { useRouter } from 'next/navigation' function FlashcardsContent() { @@ -74,8 +74,8 @@ function FlashcardsContent() { const [showForm, setShowForm] = useState(false) const [editingCard, setEditingCard] = useState(null) - // 添加假資料用於展示CEFR效果 (更新為 SimpleFlashcard 格式) - const mockFlashcards: SimpleFlashcard[] = [ + // 添加假資料用於展示CEFR效果 (更新為 Flashcard 格式) + const mockFlashcards: Flashcard[] = [ { id: 'mock1', word: 'hello', translation: '你好', partOfSpeech: 'interjection', pronunciation: '/həˈloʊ/', masteryLevel: 95, timesReviewed: 15, isFavorite: true, nextReviewDate: '2025-09-21', difficultyLevel: 'A1', definition: 'A greeting word', example: 'Hello, how are you?', createdAt: '2025-09-17', updatedAt: '2025-09-17' }, { id: 'mock2', word: 'beautiful', translation: '美麗的', partOfSpeech: 'adjective', pronunciation: '/ˈbjuːtɪfəl/', masteryLevel: 78, timesReviewed: 8, isFavorite: false, nextReviewDate: '2025-09-22', difficultyLevel: 'A2', definition: 'Pleasing to look at', example: 'The beautiful sunset', createdAt: '2025-09-16', updatedAt: '2025-09-16' }, { id: 'mock3', word: 'understand', translation: '理解', partOfSpeech: 'verb', pronunciation: '/ˌʌndərˈstænd/', masteryLevel: 65, timesReviewed: 12, isFavorite: true, nextReviewDate: '2025-09-20', difficultyLevel: 'B1', definition: 'To comprehend', example: 'I understand the concept', createdAt: '2025-09-15', updatedAt: '2025-09-15' }, @@ -99,7 +99,7 @@ function FlashcardsContent() { try { setLoading(true) setError(null) // 清除之前的錯誤 - const result = await simplifiedFlashcardsService.getFlashcards() + const result = await flashcardsService.getFlashcards() if (result.success && result.data) { setFlashcards(result.data.flashcards) console.log('✅ 詞卡載入成功:', result.data.flashcards.length, '個詞卡') @@ -140,7 +140,7 @@ function FlashcardsContent() { } try { - const result = await simplifiedFlashcardsService.deleteFlashcard(card.id) + const result = await flashcardsService.deleteFlashcard(card.id) if (result.success) { loadFlashcards() alert(`詞卡「${card.word}」已刪除`) @@ -162,7 +162,7 @@ function FlashcardsContent() { } // 真實API調用 - const result = await simplifiedFlashcardsService.toggleFavorite(card.id) + const result = await flashcardsService.toggleFavorite(card.id) if (result.success) { loadFlashcards() alert(`${card.isFavorite ? '已取消收藏' : '已加入收藏'}「${card.word}」`) diff --git a/frontend/app/generate/page.tsx b/frontend/app/generate/page.tsx index 3d93d2a..86a36a4 100644 --- a/frontend/app/generate/page.tsx +++ b/frontend/app/generate/page.tsx @@ -4,7 +4,7 @@ import { useState, useMemo, useCallback } from 'react' import { ProtectedRoute } from '@/components/ProtectedRoute' import { Navigation } from '@/components/Navigation' import { ClickableTextV2 } from '@/components/ClickableTextV2' -import { simplifiedFlashcardsService } from '@/lib/services/simplifiedFlashcards' +import { flashcardsService } from '@/lib/services/flashcards' import { Play } from 'lucide-react' import Link from 'next/link' @@ -215,7 +215,7 @@ function GenerateContent() { example: `Example sentence with ${word}.` // 提供預設例句 } - const response = await simplifiedFlashcardsService.createFlashcard(cardData) + const response = await flashcardsService.createFlashcard(cardData) if (response.success) { console.log(`✅ 已將「${word}」保存到詞卡!`) diff --git a/frontend/components/FlashcardForm.tsx b/frontend/components/FlashcardForm.tsx index 6944b80..31cafc3 100644 --- a/frontend/components/FlashcardForm.tsx +++ b/frontend/components/FlashcardForm.tsx @@ -1,59 +1,34 @@ 'use client' -import React, { useState, useEffect } from 'react' -import { flashcardsService, type CreateFlashcardRequest, type CardSet } from '@/lib/services/flashcards' +import React, { useState } from 'react' +import { flashcardsService, type CreateFlashcardRequest, type Flashcard } from '@/lib/services/flashcards' import AudioPlayer from './AudioPlayer' interface FlashcardFormProps { - cardSets: CardSet[] - initialData?: Partial + initialData?: Partial isEdit?: boolean onSuccess: () => void onCancel: () => void } -export function FlashcardForm({ cardSets, initialData, isEdit = false, onSuccess, onCancel }: FlashcardFormProps) { - // 找到預設卡組或第一個卡組 - const getDefaultCardSetId = () => { - if (initialData?.cardSetId) return initialData.cardSetId - - // 優先選擇預設卡組 - const defaultCardSet = cardSets.find(set => set.isDefault) - if (defaultCardSet) return defaultCardSet.id - - // 如果沒有預設卡組,選擇第一個卡組 - if (cardSets.length > 0) return cardSets[0].id - - // 如果沒有任何卡組,返回空字串 - return '' - } - +export function FlashcardForm({ initialData, isEdit = false, onSuccess, onCancel }: FlashcardFormProps) { const [formData, setFormData] = useState({ - cardSetId: getDefaultCardSetId(), word: initialData?.word || '', translation: initialData?.translation || '', definition: initialData?.definition || '', pronunciation: initialData?.pronunciation || '', partOfSpeech: initialData?.partOfSpeech || '名詞', example: initialData?.example || '', + exampleTranslation: initialData?.exampleTranslation || '', }) - // 當 cardSets 改變時,重新設定 cardSetId(處理初始載入的情況) - React.useEffect(() => { - if (!formData.cardSetId && cardSets.length > 0) { - const defaultId = getDefaultCardSetId() - if (defaultId) { - setFormData(prev => ({ ...prev, cardSetId: defaultId })) - } - } - }, [cardSets]) - const [loading, setLoading] = useState(false) const [error, setError] = useState(null) - const partOfSpeechOptions = [ - '名詞', '動詞', '形容詞', '副詞', '介詞', '連詞', '感嘆詞', '代詞', '冠詞' - ] + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target + setFormData(prev => ({ ...prev, [name]: value })) + } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() @@ -71,196 +46,162 @@ export function FlashcardForm({ cardSets, initialData, isEdit = false, onSuccess if (result.success) { onSuccess() } else { - setError(result.error || '操作失敗') + setError(result.error || `Failed to ${isEdit ? 'update' : 'create'} flashcard`) } - } catch (err) { - setError('操作失敗,請重試') + } catch (error) { + setError(error instanceof Error ? error.message : 'An unexpected error occurred') } finally { setLoading(false) } } - const handleChange = (field: keyof CreateFlashcardRequest, value: string) => { - setFormData(prev => ({ ...prev, [field]: value })) - } - return ( -
-
-
-
-

- {isEdit ? '編輯詞卡' : '新增詞卡'} -

- -
+
+ {error && ( +
+ {error} +
+ )} - {error && ( -
-

{error}

-
+
+ + +
+ +
+ + +
+ +
+ +