diff --git a/app/api/creative-test-answers/route.ts b/app/api/creative-test-answers/route.ts new file mode 100644 index 0000000..161373c --- /dev/null +++ b/app/api/creative-test-answers/route.ts @@ -0,0 +1,31 @@ +import { NextRequest, NextResponse } from 'next/server' +import { getCreativeTestAnswersByTestResultId } from '@/lib/database/models/creative_test_answer' + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url) + const testResultId = searchParams.get('testResultId') + + if (!testResultId) { + return NextResponse.json( + { success: false, error: '缺少測試結果ID' }, + { status: 400 } + ) + } + + // 獲取創意測驗答案 + const answers = await getCreativeTestAnswersByTestResultId(testResultId) + + return NextResponse.json({ + success: true, + data: answers + }) + + } catch (error) { + console.error('獲取創意測驗答案失敗:', error) + return NextResponse.json( + { success: false, error: '伺服器錯誤' }, + { status: 500 } + ) + } +} diff --git a/app/api/test-results/creative/route.ts b/app/api/test-results/creative/route.ts new file mode 100644 index 0000000..133e206 --- /dev/null +++ b/app/api/test-results/creative/route.ts @@ -0,0 +1,155 @@ +import { NextRequest, NextResponse } from 'next/server' +import { createTestResult, getTestResultsByUserId } from '@/lib/database/models/test_result' +import { createCreativeTestAnswers } from '@/lib/database/models/creative_test_answer' +import { getAllCreativeQuestions } from '@/lib/database/models/creative_question' + +export async function POST(request: NextRequest) { + let body: any + try { + body = await request.json() + const { + userId, + answers, + completedAt + } = body + + // 驗證必要欄位 + if (!userId || !answers || !completedAt) { + return NextResponse.json( + { success: false, error: '缺少必要欄位' }, + { status: 400 } + ) + } + + // 獲取創意題目 + const questions = await getAllCreativeQuestions() + if (questions.length === 0) { + return NextResponse.json( + { success: false, error: '無法獲取題目' }, + { status: 500 } + ) + } + + // 計算分數(包含反向題處理) + let totalScore = 0 + const answerRecords = [] + + for (let i = 0; i < questions.length; i++) { + const question = questions[i] + const userAnswer = answers[i] || 1 // 預設為1 + + // 處理反向題:如果是反向題,分數要反轉 + let score = userAnswer + if (question.is_reverse) { + score = 6 - userAnswer // 5->1, 4->2, 3->3, 2->4, 1->5 + } + + totalScore += score + + answerRecords.push({ + test_result_id: '', // 稍後填入 + question_id: question.id, + user_answer: userAnswer, + score: score + }) + } + + // 計算百分比分數 + const maxPossibleScore = questions.length * 5 // 每題最高5分 + const scorePercentage = Math.round((totalScore / maxPossibleScore) * 100) + + // 建立測試結果 + console.log('🔄 開始建立創意測驗結果...') + console.log('測試結果數據:', { + user_id: userId, + test_type: 'creative', + score: scorePercentage, + total_questions: questions.length, + correct_answers: totalScore, // 創意測驗用總分數代替正確答案數 + completed_at: completedAt + }) + + const testResult = await createTestResult({ + user_id: userId, + test_type: 'creative', + score: scorePercentage, + total_questions: questions.length, + correct_answers: totalScore, + completed_at: completedAt + }) + + console.log('測試結果建立結果:', testResult) + + if (!testResult) { + console.error('❌ 建立測試結果失敗') + return NextResponse.json( + { success: false, error: '建立測試結果失敗' }, + { status: 500 } + ) + } + + console.log('✅ 測試結果建立成功:', testResult.id) + + // 更新答案記錄的 test_result_id + answerRecords.forEach(record => { + record.test_result_id = testResult.id + }) + + // 建立答案記錄 + const answerResults = await createCreativeTestAnswers(answerRecords) + + return NextResponse.json({ + success: true, + data: { + testResult, + answerCount: answerResults.length + } + }) + + } catch (error) { + console.error('上傳創意測驗結果失敗:', error) + console.error('錯誤詳情:', { + message: error instanceof Error ? error.message : '未知錯誤', + stack: error instanceof Error ? error.stack : undefined, + body: body + }) + return NextResponse.json( + { + success: false, + error: '伺服器錯誤', + details: error instanceof Error ? error.message : '未知錯誤' + }, + { status: 500 } + ) + } +} + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url) + const userId = searchParams.get('userId') + + if (!userId) { + return NextResponse.json( + { success: false, error: '缺少用戶ID' }, + { status: 400 } + ) + } + + // 獲取用戶的創意測驗結果 + const results = await getTestResultsByUserId(userId) + const creativeResults = results.filter(r => r.test_type === 'creative') + + return NextResponse.json({ + success: true, + data: creativeResults + }) + + } catch (error) { + console.error('獲取創意測驗結果失敗:', error) + return NextResponse.json( + { success: false, error: '伺服器錯誤' }, + { status: 500 } + ) + } +} diff --git a/app/results/creative/page.tsx b/app/results/creative/page.tsx index 2997f0a..8d9fbe3 100644 --- a/app/results/creative/page.tsx +++ b/app/results/creative/page.tsx @@ -8,6 +8,7 @@ import { Progress } from "@/components/ui/progress" import { Lightbulb, Home, RotateCcw, TrendingUp } from "lucide-react" import Link from "next/link" import { creativeQuestions } from "@/lib/questions/creative-questions" +import { useAuth } from "@/lib/hooks/use-auth" interface CreativeTestResults { type: string @@ -16,17 +17,139 @@ interface CreativeTestResults { maxScore: number answers: Record completedAt: string + dimensionScores?: { + innovation: { percentage: number, rawScore: number, maxScore: number } + imagination: { percentage: number, rawScore: number, maxScore: number } + flexibility: { percentage: number, rawScore: number, maxScore: number } + originality: { percentage: number, rawScore: number, maxScore: number } + } } export default function CreativeResultsPage() { + const { user } = useAuth() const [results, setResults] = useState(null) + const [questions, setQuestions] = useState([]) + const [isLoading, setIsLoading] = useState(true) useEffect(() => { + const loadData = async () => { + if (!user) return + + try { + // 從資料庫獲取最新的創意測驗結果 + const response = await fetch(`/api/test-results/creative?userId=${user.id}`) + const data = await response.json() + + if (data.success && data.data.length > 0) { + // 取最新的結果 + const latestResult = data.data[0] + + // 獲取題目資料來計算各維度分數 + const questionsResponse = await fetch('/api/creative-questions') + const questionsData = await questionsResponse.json() + + if (questionsData.success) { + setQuestions(questionsData.questions) + + // 計算各維度分數 + const dimensionScores = await calculateDimensionScores(latestResult, questionsData.questions) + + setResults({ + type: "creative", + score: latestResult.score, + totalScore: latestResult.correct_answers, + maxScore: latestResult.total_questions * 5, + answers: {}, // 從資料庫結果中獲取 + completedAt: latestResult.completed_at, + dimensionScores: dimensionScores + }) + } + } else { + // 如果沒有資料庫結果,回退到 localStorage + const savedResults = localStorage.getItem("creativeTestResults") + if (savedResults) { + setResults(JSON.parse(savedResults)) + } + } + } catch (error) { + console.error('Error loading creative test results:', error) + // 回退到 localStorage const savedResults = localStorage.getItem("creativeTestResults") if (savedResults) { setResults(JSON.parse(savedResults)) } - }, []) + } finally { + setIsLoading(false) + } + } + + loadData() + }, [user]) + + // 計算各維度分數 + const calculateDimensionScores = async (testResult: any, questions: any[]) => { + try { + // 獲取詳細答案 + const answersResponse = await fetch(`/api/creative-test-answers?testResultId=${testResult.id}`) + const answersData = await answersResponse.json() + + if (!answersData.success) { + return { + innovation: { percentage: 0, rawScore: 0, maxScore: 0 }, + imagination: { percentage: 0, rawScore: 0, maxScore: 0 }, + flexibility: { percentage: 0, rawScore: 0, maxScore: 0 }, + originality: { percentage: 0, rawScore: 0, maxScore: 0 } + } + } + + const answers = answersData.data + const dimensionScores: Record = { + innovation: { total: 0, count: 0 }, + imagination: { total: 0, count: 0 }, + flexibility: { total: 0, count: 0 }, + originality: { total: 0, count: 0 } + } + + // 計算各維度分數 + answers.forEach((answer: any) => { + const question = questions.find(q => q.id === answer.question_id) + if (question && dimensionScores[question.category]) { + dimensionScores[question.category].total += answer.score + dimensionScores[question.category].count += 1 + } + }) + + // 計算百分比分數和原始分數 + const result = { + innovation: { percentage: 0, rawScore: 0, maxScore: 0 }, + imagination: { percentage: 0, rawScore: 0, maxScore: 0 }, + flexibility: { percentage: 0, rawScore: 0, maxScore: 0 }, + originality: { percentage: 0, rawScore: 0, maxScore: 0 } + } + + Object.keys(dimensionScores).forEach(category => { + const { total, count } = dimensionScores[category] + const maxScore = count * 5 + const percentage = count > 0 ? Math.round((total / maxScore) * 100) : 0 + + result[category as keyof typeof result] = { + percentage: percentage, + rawScore: total, + maxScore: maxScore + } + }) + + return result + } catch (error) { + console.error('計算維度分數失敗:', error) + return { + innovation: { percentage: 0, rawScore: 0, maxScore: 0 }, + imagination: { percentage: 0, rawScore: 0, maxScore: 0 }, + flexibility: { percentage: 0, rawScore: 0, maxScore: 0 }, + originality: { percentage: 0, rawScore: 0, maxScore: 0 } + } + } + } if (!results) { return ( @@ -78,7 +201,33 @@ export default function CreativeResultsPage() { const creativityLevel = getCreativityLevel(results.score) - // Calculate category scores + // Calculate category scores - prioritize database data if available + let categoryResults: Array<{ + category: string + name: string + score: number + rawScore: number + maxRawScore: number + }> = [] + + if (results.dimensionScores) { + // Use database-calculated dimension scores + const dimensionNames = { + innovation: '創新能力', + imagination: '想像力', + flexibility: '靈活性', + originality: '原創性' + } + + categoryResults = Object.entries(results.dimensionScores).map(([key, data]) => ({ + category: key, + name: dimensionNames[key as keyof typeof dimensionNames], + score: data.percentage, + rawScore: data.rawScore, + maxRawScore: data.maxScore + })) + } else { + // Fallback to localStorage calculation const categoryScores = { innovation: { total: 0, count: 0, name: "創新能力" }, imagination: { total: 0, count: 0, name: "想像力" }, @@ -93,13 +242,14 @@ export default function CreativeResultsPage() { categoryScores[question.category].count += 1 }) - const categoryResults = Object.entries(categoryScores).map(([key, data]) => ({ + categoryResults = Object.entries(categoryScores).map(([key, data]) => ({ category: key, name: data.name, score: data.count > 0 ? Math.round((data.total / (data.count * 5)) * 100) : 0, rawScore: data.total, maxRawScore: data.count * 5, })) + } return (
@@ -338,11 +488,11 @@ export default function CreativeResultsPage() { {/* Legend */}
- {categoryResults.map((category) => ( + {categoryResults.map((category) => (
{category.name} -
+
))}
diff --git a/app/tests/creative/page.tsx b/app/tests/creative/page.tsx index 2b0ad14..f35d7a4 100644 --- a/app/tests/creative/page.tsx +++ b/app/tests/creative/page.tsx @@ -7,6 +7,7 @@ import { Button } from "@/components/ui/button" import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" import { Label } from "@/components/ui/label" import { useRouter } from "next/navigation" +import { useAuth } from "@/lib/hooks/use-auth" interface CreativeQuestion { id: number @@ -18,11 +19,13 @@ interface CreativeQuestion { export default function CreativeTestPage() { const router = useRouter() + const { user } = useAuth() const [questions, setQuestions] = useState([]) const [currentQuestion, setCurrentQuestion] = useState(0) const [answers, setAnswers] = useState>({}) const [timeRemaining, setTimeRemaining] = useState(30 * 60) // 30 minutes in seconds const [isLoading, setIsLoading] = useState(true) + const [isSubmitting, setIsSubmitting] = useState(false) // Load questions from database useEffect(() => { @@ -88,31 +91,83 @@ export default function CreativeTestPage() { } } - const handleSubmit = () => { - // Calculate score based on creativity scoring - let totalScore = 0 - questions.forEach((question, index) => { - const answer = answers[index] || 1 - // For creativity, higher scores indicate more creative thinking - // 反向題:選擇 5 得 1 分,選擇 1 得 5 分 - totalScore += question.is_reverse ? 6 - answer : answer - }) + const handleSubmit = async () => { + console.log('🔍 開始提交創意測驗...') + console.log('用戶狀態:', user) - const maxScore = questions.length * 5 - const score = Math.round((totalScore / maxScore) * 100) - - // Store results in localStorage - const results = { - type: "creative", - score, - totalScore, - maxScore, - answers, - completedAt: new Date().toISOString(), + if (!user) { + console.log('❌ 用戶未登入') + alert('請先登入') + return } - localStorage.setItem("creativeTestResults", JSON.stringify(results)) - router.push("/results/creative") + console.log('✅ 用戶已登入,用戶ID:', user.id) + setIsSubmitting(true) + + try { + // Calculate score based on creativity scoring + let totalScore = 0 + questions.forEach((question, index) => { + const answer = answers[index] || 1 + // For creativity, higher scores indicate more creative thinking + // 反向題:選擇 5 得 1 分,選擇 1 得 5 分 + totalScore += question.is_reverse ? 6 - answer : answer + }) + + const maxScore = questions.length * 5 + const score = Math.round((totalScore / maxScore) * 100) + + // Store results in localStorage (for backward compatibility) + const results = { + type: "creative", + score, + totalScore, + maxScore, + answers, + completedAt: new Date().toISOString(), + } + + localStorage.setItem("creativeTestResults", JSON.stringify(results)) + console.log('✅ 結果已儲存到 localStorage') + + // Upload to database + console.log('🔄 開始上傳到資料庫...') + const uploadData = { + userId: user.id, + answers: Object.values(answers), + completedAt: new Date().toISOString().replace('Z', '').replace('T', ' ') + } + console.log('上傳數據:', uploadData) + + const uploadResponse = await fetch('/api/test-results/creative', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(uploadData) + }) + + console.log('📡 API 響應狀態:', uploadResponse.status) + const uploadResult = await uploadResponse.json() + console.log('📡 API 響應內容:', uploadResult) + + if (uploadResult.success) { + console.log('✅ 創意測驗結果已上傳到資料庫') + console.log('測試結果ID:', uploadResult.data.testResult.id) + console.log('答案記錄數量:', uploadResult.data.answerCount) + } else { + console.error('❌ 上傳到資料庫失敗:', uploadResult.error) + // 即使上傳失敗,也繼續顯示結果 + } + + router.push("/results/creative") + + } catch (error) { + console.error('❌ 提交測驗失敗:', error) + alert('提交測驗失敗,請重試') + } finally { + setIsSubmitting(false) + } } if (isLoading) { @@ -223,8 +278,8 @@ export default function CreativeTestPage() { {isLastQuestion ? ( - ) : (