diff --git a/app/api/creative-questions/route.ts b/app/api/creative-questions/route.ts new file mode 100644 index 0000000..7f87783 --- /dev/null +++ b/app/api/creative-questions/route.ts @@ -0,0 +1,30 @@ +import { NextRequest, NextResponse } from 'next/server' +import { getAllCreativeQuestions } from '@/lib/database/models/creative_question' +import { initializeDatabase } from '@/lib/database/init' + +export async function GET(request: NextRequest) { + try { + await initializeDatabase() + const questions = await getAllCreativeQuestions() + + // 可以根據需求添加隨機排序或限制數量 + const { searchParams } = new URL(request.url) + const random = searchParams.get('random') === 'true' + const limit = searchParams.get('limit') ? parseInt(searchParams.get('limit')!) : questions.length + + let filteredQuestions = questions + + if (random) { + // 隨機排序 + filteredQuestions = filteredQuestions.sort(() => Math.random() - 0.5) + } + + // 限制數量 + filteredQuestions = filteredQuestions.slice(0, limit) + + return NextResponse.json({ success: true, questions: filteredQuestions }) + } catch (error) { + console.error('獲取創意能力測試題目失敗:', error) + return NextResponse.json({ success: false, error: '無法獲取題目' }, { status: 500 }) + } +} diff --git a/app/tests/creative/page.tsx b/app/tests/creative/page.tsx index 52d1380..de20ec3 100644 --- a/app/tests/creative/page.tsx +++ b/app/tests/creative/page.tsx @@ -7,16 +7,49 @@ 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 { creativeQuestions } from "@/lib/questions/creative-questions" + +interface CreativeQuestion { + id: number + statement: string + category: 'innovation' | 'imagination' | 'flexibility' | 'originality' + is_reverse: boolean + created_at: string +} export default function CreativeTestPage() { const router = useRouter() + 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) + + // Load questions from database + useEffect(() => { + const loadQuestions = async () => { + try { + const response = await fetch('/api/creative-questions') + const data = await response.json() + + if (data.success) { + setQuestions(data.questions) + } else { + console.error('Failed to load questions:', data.error) + } + } catch (error) { + console.error('Error loading questions:', error) + } finally { + setIsLoading(false) + } + } + + loadQuestions() + }, []) // Timer effect useEffect(() => { + if (questions.length === 0) return + const timer = setInterval(() => { setTimeRemaining((prev) => { if (prev <= 1) { @@ -28,7 +61,7 @@ export default function CreativeTestPage() { }, 1000) return () => clearInterval(timer) - }, []) + }, [questions]) const formatTime = (seconds: number) => { const mins = Math.floor(seconds / 60) @@ -44,7 +77,7 @@ export default function CreativeTestPage() { } const handleNext = () => { - if (currentQuestion < creativeQuestions.length - 1) { + if (currentQuestion < questions.length - 1) { setCurrentQuestion((prev) => prev + 1) } } @@ -58,13 +91,14 @@ export default function CreativeTestPage() { const handleSubmit = () => { // Calculate score based on creativity scoring let totalScore = 0 - creativeQuestions.forEach((question, index) => { + questions.forEach((question, index) => { const answer = answers[index] || 1 // For creativity, higher scores indicate more creative thinking - totalScore += question.isReverse ? 6 - answer : answer + // 反向題:選擇 5 得 1 分,選擇 1 得 5 分 + totalScore += question.is_reverse ? 6 - answer : answer }) - const maxScore = creativeQuestions.length * 5 + const maxScore = questions.length * 5 const score = Math.round((totalScore / maxScore) * 100) // Store results in localStorage @@ -81,8 +115,41 @@ export default function CreativeTestPage() { router.push("/results/creative") } - const currentQ = creativeQuestions[currentQuestion] - const isLastQuestion = currentQuestion === creativeQuestions.length - 1 + if (isLoading) { + return ( + router.push("/")} + > +
+
+

載入題目中...

+
+
+ ) + } + + if (questions.length === 0) { + return ( + router.push("/")} + > +
+

無法載入題目,請稍後再試

+
+
+ ) + } + + const currentQ = questions[currentQuestion] + const isLastQuestion = currentQuestion === questions.length - 1 const hasAnswer = answers[currentQuestion] !== undefined const scaleOptions = [ @@ -97,7 +164,7 @@ export default function CreativeTestPage() { router.push("/")} > @@ -125,7 +192,6 @@ export default function CreativeTestPage() { > {option.label} -
{option.value}
))} @@ -139,7 +205,7 @@ export default function CreativeTestPage() {
- {creativeQuestions.map((_, index) => ( + {questions.map((_, index) => (
diff --git a/lib/database/init.ts b/lib/database/init.ts index 1db4ba1..d169205 100644 --- a/lib/database/init.ts +++ b/lib/database/init.ts @@ -1,6 +1,7 @@ import { testConnection } from './connection' import { createUsersTable } from './models/user' import { createLogicQuestionsTable } from './models/logic_question' +import { createCreativeQuestionsTable } from './models/creative_question' // 初始化資料庫 export async function initializeDatabase(): Promise { @@ -16,10 +17,13 @@ export async function initializeDatabase(): Promise { // 建立用戶表 await createUsersTable() - + // 建立邏輯思維題目表 await createLogicQuestionsTable() - + + // 建立創意能力測試題目表 + await createCreativeQuestionsTable() + console.log('✅ 資料庫初始化完成') return true } catch (error) { diff --git a/lib/database/models/creative_question.ts b/lib/database/models/creative_question.ts new file mode 100644 index 0000000..074caaf --- /dev/null +++ b/lib/database/models/creative_question.ts @@ -0,0 +1,82 @@ +import { executeQuery, executeQueryOne } from '../connection' + +export interface CreativeQuestion { + id: number + statement: string + category: 'innovation' | 'imagination' | 'flexibility' | 'originality' + is_reverse: boolean + created_at: string +} + +export interface CreateCreativeQuestionData { + statement: string + category: 'innovation' | 'imagination' | 'flexibility' | 'originality' + is_reverse: boolean +} + +// 建立創意能力測試題目表(如果不存在) +export async function createCreativeQuestionsTable(): Promise { + const createTableQuery = ` + CREATE TABLE IF NOT EXISTS creative_questions ( + id INT AUTO_INCREMENT PRIMARY KEY, + statement TEXT NOT NULL, + category ENUM('innovation', 'imagination', 'flexibility', 'originality') NOT NULL, + is_reverse BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ` + + await executeQuery(createTableQuery) + console.log('✅ 創意能力測試題目表建立成功') +} + +// 建立新題目 +export async function createCreativeQuestion(questionData: CreateCreativeQuestionData): Promise { + const query = ` + INSERT INTO creative_questions (statement, category, is_reverse) + VALUES (?, ?, ?) + ` + + const { statement, category, is_reverse } = questionData + + try { + const result = await executeQuery(query, [statement, category, is_reverse]) + + // 獲取插入的 ID + const insertId = (result as any).insertId + return await findCreativeQuestionById(insertId) + } catch (error) { + console.error('建立創意能力測試題目失敗:', error) + return null + } +} + +// 根據 ID 查找題目 +export async function findCreativeQuestionById(id: number): Promise { + const query = 'SELECT * FROM creative_questions WHERE id = ?' + return await executeQueryOne(query, [id]) +} + +// 獲取所有題目 +export async function getAllCreativeQuestions(): Promise { + const query = 'SELECT * FROM creative_questions ORDER BY created_at' + return await executeQuery(query) +} + +// 清空所有題目 +export async function clearCreativeQuestions(): Promise { + await executeQuery('DELETE FROM creative_questions') + console.log('✅ 已清空所有創意能力測試題目') +} + +// 建立多個題目 +export async function createMultipleCreativeQuestions(questionsData: CreateCreativeQuestionData[]): Promise { + const createdQuestions: CreativeQuestion[] = [] + for (const questionData of questionsData) { + const createdQuestion = await createCreativeQuestion(questionData) + if (createdQuestion) { + createdQuestions.push(createdQuestion) + } + } + return createdQuestions +} diff --git a/lib/database/seed-creative-questions.ts b/lib/database/seed-creative-questions.ts new file mode 100644 index 0000000..cb6ef73 --- /dev/null +++ b/lib/database/seed-creative-questions.ts @@ -0,0 +1,136 @@ +import { createMultipleCreativeQuestions, clearCreativeQuestions } from './models/creative_question' +import { initializeDatabase } from './init' + +// 創意能力測試題目數據 +const creativeQuestions = [ + { + statement: "我常能從不同角度看事情,接受多元觀點。", + category: "flexibility" as const, + is_reverse: false + }, + { + statement: "我有時會提出具挑戰性或爭議性的想法,促使他人表達不同觀點。", + category: "innovation" as const, + is_reverse: false + }, + { + statement: "我習慣一次只做一件事,不輕易嘗試新方法。", + category: "flexibility" as const, + is_reverse: true + }, + { + statement: "當靈感枯竭時,我仍能找到突破的方法。", + category: "imagination" as const, + is_reverse: false + }, + { + statement: "我喜歡與不同背景的人合作,從差異中獲得新想法。", + category: "innovation" as const, + is_reverse: false + }, + { + statement: "我通常笑得比別人多,並帶動正面氛圍。", + category: "originality" as const, + is_reverse: false + }, + { + statement: "我會追根究柢思考,直到找到事件背後的原因。", + category: "imagination" as const, + is_reverse: false + }, + { + statement: "我更喜歡看到整體格局,而不是專注在細節上。", + category: "originality" as const, + is_reverse: false + }, + { + statement: "我認為規定和框架在組織中絕對必要。", + category: "flexibility" as const, + is_reverse: true + }, + { + statement: "我通常會先做詳細規劃,然後按部就班執行。", + category: "flexibility" as const, + is_reverse: true + }, + { + statement: "我能找到更快的方法或捷徑完成任務。", + category: "innovation" as const, + is_reverse: false + }, + { + statement: "我喜歡解謎或挑戰看似難解的問題。", + category: "imagination" as const, + is_reverse: false + }, + { + statement: "我能接受頻繁的改變,並調整自己因應。", + category: "flexibility" as const, + is_reverse: false + }, + { + statement: "我通常不輕易說出心中想法,除非被問到。", + category: "originality" as const, + is_reverse: true + }, + { + statement: "我經常追求穩定感,避免風險。", + category: "flexibility" as const, + is_reverse: true + }, + { + statement: "當遇到一個陌生問題時,我會主動去探索,即使沒有明確指引。", + category: "innovation" as const, + is_reverse: false + }, + { + statement: "當既有方法行不通時,我會刻意嘗試完全相反的方向。", + category: "originality" as const, + is_reverse: false + }, + { + statement: "即使存在風險,我也願意嘗試新的解決方法。", + category: "innovation" as const, + is_reverse: false + } +] + +// 種子創意能力測試題目 +export async function seedCreativeQuestions(): Promise { + try { + await initializeDatabase() + console.log('🔄 正在種子創意能力測試題目...') + + // 清空現有題目數據 + await clearCreativeQuestions() + + // 建立題目 + const createdQuestions = await createMultipleCreativeQuestions(creativeQuestions) + + console.log(`✅ 成功建立 ${createdQuestions.length} 道創意能力測試題目`) + + // 顯示題目摘要 + console.log('\n📋 題目摘要:') + createdQuestions.forEach((question, index) => { + const reverseText = question.is_reverse ? ' (反向題)' : '' + console.log(`${index + 1}. ${question.statement.substring(0, 30)}...${reverseText}`) + }) + + console.log('\n📊 統計:') + const reverseCount = createdQuestions.filter(q => q.is_reverse).length + const normalCount = createdQuestions.length - reverseCount + console.log(`- 一般題目: ${normalCount} 題`) + console.log(`- 反向題目: ${reverseCount} 題`) + + } catch (error) { + console.error('❌ 種子創意能力測試題目失敗:', error) + throw error + } +} + +if (require.main === module) { + seedCreativeQuestions().catch(error => { + console.error('執行 seedCreativeQuestions 腳本失敗:', error) + process.exit(1) + }) +} diff --git a/package.json b/package.json index 5cf11ba..b100a7e 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,15 @@ "test-login": "node scripts/test-login.js", "check-passwords": "node scripts/check-passwords.js", "check-logic-questions": "node scripts/check-logic-questions.js", + "check-creative-questions": "node scripts/check-creative-questions.js", "test-score-levels": "node scripts/test-score-levels.js", "test-responsive-design": "node scripts/test-responsive-design.js", + "test-reverse-scoring": "node scripts/test-reverse-scoring.js", + "test-creative-flow": "node scripts/test-creative-flow.js", "update-logic-table": "node scripts/update-logic-table.js", "seed-db": "npx tsx lib/database/seed.ts", "seed-logic-questions": "npx tsx lib/database/seed-logic-questions.ts", + "seed-creative-questions": "npx tsx lib/database/seed-creative-questions.ts", "reset-users": "npx tsx lib/database/reset-users.ts" }, "dependencies": { diff --git a/scripts/check-creative-questions.js b/scripts/check-creative-questions.js new file mode 100644 index 0000000..d3843dc --- /dev/null +++ b/scripts/check-creative-questions.js @@ -0,0 +1,52 @@ +const mysql = require('mysql2/promise') + +async function checkCreativeQuestions() { + const config = { + host: process.env.DB_HOST || 'mysql.theaken.com', + port: parseInt(process.env.DB_PORT || '33306'), + user: process.env.DB_USER || 'hr_assessment', + password: process.env.DB_PASSWORD || 'QFOts8FlibiI', + database: process.env.DB_NAME || 'db_hr_assessment', + } + + console.log('🔄 正在檢查創意能力測試題目...') + + try { + const connection = await mysql.createConnection(config) + + const [rows] = await connection.execute('SELECT * FROM creative_questions ORDER BY id') + + console.log(`\n📋 共找到 ${rows.length} 道創意能力測試題目:`) + console.log('=' .repeat(80)) + + rows.forEach((question, index) => { + const reverseText = question.is_reverse ? ' (反向題)' : '' + console.log(`\n${index + 1}. ID: ${question.id}`) + console.log(` 題目: ${question.statement}`) + console.log(` 類別: ${question.category}`) + console.log(` 反向題: ${question.is_reverse ? '是' : '否'}${reverseText}`) + }) + + console.log('\n📊 統計:') + const reverseCount = rows.filter(q => q.is_reverse).length + const normalCount = rows.length - reverseCount + const categoryCount = {} + rows.forEach(q => { + categoryCount[q.category] = (categoryCount[q.category] || 0) + 1 + }) + + console.log(`- 一般題目: ${normalCount} 題`) + console.log(`- 反向題目: ${reverseCount} 題`) + console.log('- 類別分布:') + Object.entries(categoryCount).forEach(([category, count]) => { + console.log(` - ${category}: ${count} 題`) + }) + + await connection.end() + console.log('\n✅ 檢查完成') + } catch (error) { + console.error('❌ 檢查失敗:', error.message) + } +} + +checkCreativeQuestions() diff --git a/scripts/check-creative-table-structure.js b/scripts/check-creative-table-structure.js new file mode 100644 index 0000000..bb8e1ef --- /dev/null +++ b/scripts/check-creative-table-structure.js @@ -0,0 +1,40 @@ +const mysql = require('mysql2/promise') + +async function checkCreativeTableStructure() { + const config = { + host: process.env.DB_HOST || 'mysql.theaken.com', + port: parseInt(process.env.DB_PORT || '33306'), + user: process.env.DB_USER || 'hr_assessment', + password: process.env.DB_PASSWORD || 'QFOts8FlibiI', + database: process.env.DB_NAME || 'db_hr_assessment', + } + + console.log('🔄 正在檢查 creative_questions 表結構...') + + try { + const connection = await mysql.createConnection(config) + + // 檢查表是否存在 + const [tables] = await connection.execute("SHOW TABLES LIKE 'creative_questions'") + + if (tables.length === 0) { + console.log('❌ creative_questions 表不存在') + } else { + console.log('✅ creative_questions 表存在') + + // 檢查表結構 + const [columns] = await connection.execute("DESCRIBE creative_questions") + + console.log('\n📋 表結構:') + columns.forEach(col => { + console.log(`- ${col.Field}: ${col.Type} ${col.Null === 'NO' ? 'NOT NULL' : 'NULL'} ${col.Key ? `(${col.Key})` : ''} ${col.Default ? `DEFAULT ${col.Default}` : ''}`) + }) + } + + await connection.end() + } catch (error) { + console.error('❌ 檢查失敗:', error.message) + } +} + +checkCreativeTableStructure() diff --git a/scripts/test-creative-flow.js b/scripts/test-creative-flow.js new file mode 100644 index 0000000..bb8c597 --- /dev/null +++ b/scripts/test-creative-flow.js @@ -0,0 +1,71 @@ +// 測試創意測試流程 +const mysql = require('mysql2/promise') + +async function testCreativeFlow() { + const config = { + host: process.env.DB_HOST || 'mysql.theaken.com', + port: parseInt(process.env.DB_PORT || '33306'), + user: process.env.DB_USER || 'hr_assessment', + password: process.env.DB_PASSWORD || 'QFOts8FlibiI', + database: process.env.DB_NAME || 'db_hr_assessment', + } + + console.log('🧪 測試創意測試流程') + console.log('=' .repeat(50)) + + try { + const connection = await mysql.createConnection(config) + + // 1. 檢查資料庫中的題目 + const [questions] = await connection.execute('SELECT * FROM creative_questions ORDER BY id') + console.log(`✅ 資料庫中有 ${questions.length} 道題目`) + + // 2. 模擬 API 回應 + const apiResponse = { + success: true, + questions: questions + } + console.log('✅ API 回應格式正確') + + // 3. 測試分數計算邏輯 + const mockAnswers = { + 0: 5, // 一般題目,選擇 5 + 1: 5, // 反向題目,選擇 5 + 2: 1, // 一般題目,選擇 1 + 3: 1 // 反向題目,選擇 1 + } + + let totalScore = 0 + questions.slice(0, 4).forEach((question, index) => { + const answer = mockAnswers[index] || 1 + const score = question.is_reverse ? 6 - answer : answer + totalScore += score + + console.log(`題目 ${index + 1}: ${question.is_reverse ? '反向' : '一般'} - 選擇${answer} → 得分${score}`) + }) + + const maxScore = 4 * 5 + const percentage = Math.round((totalScore / maxScore) * 100) + + console.log(`\n📊 測試結果: ${totalScore}/${maxScore} (${percentage}%)`) + + // 4. 檢查題目類別分布 + const categoryCount = {} + questions.forEach(q => { + categoryCount[q.category] = (categoryCount[q.category] || 0) + 1 + }) + + console.log('\n📋 題目類別分布:') + Object.entries(categoryCount).forEach(([category, count]) => { + console.log(`- ${category}: ${count} 題`) + }) + + await connection.end() + console.log('\n✅ 創意測試流程測試完成') + + } catch (error) { + console.error('❌ 測試失敗:', error.message) + } +} + +testCreativeFlow() diff --git a/scripts/test-reverse-scoring.js b/scripts/test-reverse-scoring.js new file mode 100644 index 0000000..8af6140 --- /dev/null +++ b/scripts/test-reverse-scoring.js @@ -0,0 +1,63 @@ +// 測試反向題分數計算邏輯 +function testReverseScoring() { + console.log('🧮 測試反向題分數計算邏輯') + console.log('=' .repeat(50)) + + // 模擬題目數據 + const questions = [ + { id: 1, statement: "一般題目", is_reverse: false }, + { id: 2, statement: "反向題目", is_reverse: true }, + { id: 3, statement: "一般題目", is_reverse: false }, + { id: 4, statement: "反向題目", is_reverse: true } + ] + + // 模擬用戶答案 + const answers = { + 0: 5, // 一般題目,選擇 5 + 1: 5, // 反向題目,選擇 5 + 2: 1, // 一般題目,選擇 1 + 3: 1 // 反向題目,選擇 1 + } + + console.log('\n📋 題目和答案:') + questions.forEach((question, index) => { + const answer = answers[index] + const reverseText = question.is_reverse ? ' (反向題)' : '' + console.log(`${index + 1}. ${question.statement}${reverseText} - 用戶選擇: ${answer}`) + }) + + console.log('\n🧮 分數計算:') + let totalScore = 0 + + questions.forEach((question, index) => { + const answer = answers[index] || 1 + let score + + if (question.is_reverse) { + // 反向題:選擇 5 得 1 分,選擇 1 得 5 分 + score = 6 - answer + } else { + // 一般題:選擇多少得多少分 + score = answer + } + + totalScore += score + + console.log(`第${index + 1}題: ${question.is_reverse ? '反向' : '一般'} - 選擇${answer} → 得分${score}`) + }) + + const maxScore = questions.length * 5 + const percentage = Math.round((totalScore / maxScore) * 100) + + console.log('\n📊 結果:') + console.log(`總分: ${totalScore} / ${maxScore}`) + console.log(`百分比: ${percentage}%`) + + console.log('\n✅ 反向題分數計算邏輯測試完成') + console.log('\n📝 說明:') + console.log('- 一般題目:選擇 1-5 得 1-5 分') + console.log('- 反向題目:選擇 1-5 得 5-1 分(分數相反)') + console.log('- 這樣設計是為了確保高分代表高創意能力') +} + +testReverseScoring()