修改管理者測驗查看詳細畫面

This commit is contained in:
2025-10-04 20:46:44 +08:00
parent 3aba1451bd
commit 464ab0a83d
5 changed files with 548 additions and 258 deletions

View File

@@ -0,0 +1,543 @@
"use client"
import { useEffect, useState } from "react"
import { useParams, useRouter } from "next/navigation"
import { ProtectedRoute } from "@/components/protected-route"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Progress } from "@/components/ui/progress"
import { CheckCircle, XCircle, Brain, Lightbulb, BarChart3, ArrowLeft, Loader2 } from "lucide-react"
import Link from "next/link"
interface User {
id: string
name: string
email: string
department: string
role: string
}
interface TestResult {
id: string
userId: string
type: "logic" | "creative" | "combined"
score: number
completedAt: string
details?: {
logicScore?: number
creativeScore?: number
abilityBalance?: number
breakdown?: any
}
}
interface Question {
id: number
question?: string
statement?: string
option_a?: string
option_b?: string
option_c?: string
option_d?: string
option_e?: string
correct_answer?: 'A' | 'B' | 'C' | 'D' | 'E'
explanation?: string
type: 'logic' | 'creative'
userAnswer?: string | number
isCorrect?: boolean
score?: number
created_at: string
}
interface DetailData {
result: TestResult
user: User
questions: Question[]
}
export default function AdminResultDetailPage() {
return (
<ProtectedRoute adminOnly>
<AdminResultDetailContent />
</ProtectedRoute>
)
}
function AdminResultDetailContent() {
const params = useParams()
const router = useRouter()
const testResultId = params.testResultId as string
const [detailData, setDetailData] = useState<DetailData | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
const loadDetailData = async () => {
if (!testResultId) return
setIsLoading(true)
setError(null)
try {
// 從 URL 參數獲取測試類型,如果沒有則嘗試從結果中獲取
const urlParams = new URLSearchParams(window.location.search)
const testType = urlParams.get('testType') as "logic" | "creative" | "combined"
if (!testType) {
setError("缺少測試類型參數")
return
}
const response = await fetch(`/api/admin/test-results/detail?testResultId=${testResultId}&testType=${testType}`)
const data = await response.json()
if (data.success) {
setDetailData(data.data)
} else {
setError(data.message || "載入詳細結果失敗")
}
} catch (error) {
console.error("載入詳細結果錯誤:", error)
setError("載入詳細結果時發生錯誤")
} finally {
setIsLoading(false)
}
}
loadDetailData()
}, [testResultId])
if (isLoading) {
return (
<div className="min-h-screen bg-background flex items-center justify-center">
<Card className="w-full max-w-md">
<CardContent className="text-center py-8">
<Loader2 className="w-8 h-8 animate-spin mx-auto mb-4" />
<p className="text-muted-foreground">...</p>
</CardContent>
</Card>
</div>
)
}
if (error || !detailData) {
return (
<div className="min-h-screen bg-background flex items-center justify-center">
<Card className="w-full max-w-md">
<CardContent className="text-center py-8">
<p className="text-muted-foreground mb-4">{error || "未找到測試結果"}</p>
<Button asChild>
<Link href="/admin/results"></Link>
</Button>
</CardContent>
</Card>
</div>
)
}
const { result, user, questions } = detailData
const getTestTypeInfo = (type: string) => {
switch (type) {
case "logic":
return {
name: "邏輯思維",
icon: Brain,
color: "bg-primary",
textColor: "text-primary",
}
case "creative":
return {
name: "創意能力",
icon: Lightbulb,
color: "bg-accent",
textColor: "text-accent",
}
case "combined":
return {
name: "綜合能力",
icon: BarChart3,
color: "bg-gradient-to-r from-primary to-accent",
textColor: "text-primary",
}
default:
return {
name: "未知",
icon: BarChart3,
color: "bg-muted",
textColor: "text-muted-foreground",
}
}
}
const getScoreLevel = (score: number, type: string) => {
if (type === "logic") {
if (score === 100) return {
level: "邏輯巔峰者",
color: "bg-purple-600",
description: "近乎完美的邏輯典範!你像一台「推理引擎」,嚴謹又高效,幾乎不受陷阱干擾。",
suggestion: "多和他人分享你的思考路徑,能幫助團隊整體邏輯力提升。"
}
if (score >= 80) return {
level: "邏輯大師",
color: "bg-green-500",
description: "你的思維如同精密儀器,能快速抓住題目關鍵,並做出有效推理。常常是團隊中「冷靜的分析者」。",
suggestion: "挑戰更高層次的難題,讓你的邏輯力更加精進。"
}
if (score >= 60) return {
level: "邏輯高手",
color: "bg-blue-500",
description: "邏輯清晰穩定,大部分情境都能正確判斷。偶爾會因粗心錯過陷阱。",
suggestion: "在思維縝密之餘,更加留心細節,就能把錯誤率降到最低。"
}
if (score >= 30) return {
level: "邏輯學徒",
color: "bg-yellow-500",
description: "已經抓到一些邏輯規律,能解決中等難度的問題。遇到複雜情境時,仍可能卡關。",
suggestion: "嘗試將問題拆解成小步驟,就像組裝樂高,每一塊拼好,答案就自然浮現。"
}
return {
level: "邏輯探險新手",
color: "bg-red-500",
description: "還在邏輯森林的入口徘徊。思考時可能忽略細節,或被陷阱誤導。",
suggestion: "多練習經典邏輯題,像是在拼拼圖般,慢慢建立清晰的分析步驟。"
}
} else if (type === "creative") {
if (score >= 90) return {
level: "創意大師",
color: "bg-purple-600",
description: "你的創意如泉水般源源不絕,總能提出令人驚豔的解決方案!",
suggestion: "繼續保持這種創意精神,並嘗試將創意轉化為實際行動。"
}
if (score >= 80) return {
level: "創意高手",
color: "bg-green-500",
description: "創意思維活躍,能夠從不同角度思考問題,提出新穎的見解。",
suggestion: "多接觸不同領域的知識,豐富你的創意素材庫。"
}
if (score >= 60) return {
level: "創意探索者",
color: "bg-blue-500",
description: "有一定的創意思維,能夠在既有框架內提出改進建議。",
suggestion: "嘗試跳出舒適圈,挑戰更多創意性的任務。"
}
if (score >= 40) return {
level: "創意學習者",
color: "bg-yellow-500",
description: "正在學習如何發揮創意,需要更多練習和啟發。",
suggestion: "多觀察身邊的事物,培養對細節的敏感度。"
}
return {
level: "創意新手",
color: "bg-red-500",
description: "創意思維還需要培養,建議多接觸創意相關的活動。",
suggestion: "從簡單的創意練習開始,逐步提升創意思維能力。"
}
} else {
// combined
if (score >= 90) return {
level: "全能高手",
color: "bg-purple-600",
description: "邏輯與創意完美結合,是團隊中的全能型人才!",
suggestion: "繼續保持這種平衡,並嘗試帶領團隊解決複雜問題。"
}
if (score >= 80) return {
level: "綜合專家",
color: "bg-green-500",
description: "邏輯思維和創意能力都很出色,能夠勝任各種挑戰。",
suggestion: "繼續精進兩種能力,成為更全面的專業人才。"
}
if (score >= 60) return {
level: "平衡發展者",
color: "bg-blue-500",
description: "邏輯和創意能力都有一定水準,正在朝全面發展邁進。",
suggestion: "針對較弱的能力進行重點提升,達到更好的平衡。"
}
if (score >= 40) return {
level: "潛力新星",
color: "bg-yellow-500",
description: "有發展潛力,需要更多練習來提升綜合能力。",
suggestion: "制定學習計劃,系統性地提升邏輯和創意能力。"
}
return {
level: "成長中",
color: "bg-red-500",
description: "正在學習階段,需要更多時間和練習來提升能力。",
suggestion: "從基礎開始,逐步建立邏輯思維和創意思維。"
}
}
}
const testTypeInfo = getTestTypeInfo(result.type)
const scoreLevel = getScoreLevel(result.score, result.type)
const IconComponent = testTypeInfo.icon
// 計算統計數據
const logicQuestions = questions.filter(q => q.type === 'logic')
const creativeQuestions = questions.filter(q => q.type === 'creative')
const correctAnswers = logicQuestions.filter(q => q.isCorrect).length
const totalQuestions = questions.length
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="border-b bg-card/50 backdrop-blur-sm">
<div className="container mx-auto px-4 py-4">
<div className="flex items-center gap-3">
<Button variant="ghost" size="sm" asChild>
<Link href="/admin/results">
<ArrowLeft className="w-4 h-4 mr-2" />
<span className="hidden sm:inline"></span>
</Link>
</Button>
<div className={`w-10 h-10 ${testTypeInfo.color} rounded-lg flex items-center justify-center`}>
<IconComponent className="w-6 h-6 text-white" />
</div>
<div>
<h1 className="text-xl font-bold text-foreground">
{user.name} - {testTypeInfo.name}
</h1>
<p className="text-sm text-muted-foreground">
{new Date(result.completedAt).toLocaleString("zh-TW")}
</p>
</div>
</div>
</div>
</header>
<div className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto space-y-8">
{/* User Info */}
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-sm font-medium">{user.name}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-sm">{user.email}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-sm">{user.department}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<Badge variant="outline">{user.role}</Badge>
</div>
</div>
</CardContent>
</Card>
{/* Score Overview */}
<Card className="text-center">
<CardHeader>
<div
className={`w-24 h-24 ${scoreLevel.color} rounded-full flex items-center justify-center mx-auto mb-4`}
>
<span className="text-3xl font-bold text-white">{result.score}</span>
</div>
<CardTitle className="text-3xl mb-2"></CardTitle>
<div className="flex items-center justify-center gap-2 mb-4">
<Badge variant="secondary" className="text-lg px-4 py-1">
{scoreLevel.level}
</Badge>
</div>
<p className="text-lg text-muted-foreground mb-3">{scoreLevel.description}</p>
<div className="bg-muted/50 rounded-lg p-4 text-sm">
<p className="text-muted-foreground">
<span className="font-medium">👉 </span>
{scoreLevel.suggestion}
</p>
</div>
</CardHeader>
<CardContent>
<div className="grid grid-cols-3 gap-4 mb-6">
<div className="text-center">
<div className="text-2xl font-bold text-green-600 mb-1">{correctAnswers}</div>
<div className="text-xs text-muted-foreground"></div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-primary mb-1">{totalQuestions}</div>
<div className="text-xs text-muted-foreground"></div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-accent mb-1">
{totalQuestions > 0 ? Math.round((correctAnswers / totalQuestions) * 100) : 0}%
</div>
<div className="text-xs text-muted-foreground"></div>
</div>
</div>
<Progress value={result.score} className="h-3 mb-4" />
</CardContent>
</Card>
{/* Combined Test Details */}
{result.type === 'combined' && result.details && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-3 gap-4">
<div className="text-center p-4 bg-blue-50 rounded-lg">
<h4 className="font-medium text-blue-800"></h4>
<p className="text-2xl font-bold text-blue-600">{result.details.logicScore}</p>
</div>
<div className="text-center p-4 bg-green-50 rounded-lg">
<h4 className="font-medium text-green-800"></h4>
<p className="text-2xl font-bold text-green-600">{result.details.creativeScore}</p>
</div>
<div className="text-center p-4 bg-purple-50 rounded-lg">
<h4 className="font-medium text-purple-800"></h4>
<p className="text-2xl font-bold text-purple-600">{result.details.abilityBalance}</p>
</div>
</div>
</CardContent>
</Card>
)}
{/* Detailed Results */}
{questions.length > 0 && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-6">
{/* Logic Questions */}
{logicQuestions.length > 0 && (
<div>
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<Brain className="w-5 h-5 text-blue-600" />
</h3>
<div className="space-y-4">
{logicQuestions.map((question, index) => {
const userAnswer = question.userAnswer as string
const isCorrect = question.isCorrect
const getOptionText = (option: string) => {
switch (option) {
case 'A': return question.option_a
case 'B': return question.option_b
case 'C': return question.option_c
case 'D': return question.option_d
case 'E': return question.option_e
default: return "未作答"
}
}
const correctOptionText = getOptionText(question.correct_answer || '')
const userOptionText = userAnswer ? getOptionText(userAnswer) : "未作答"
return (
<div key={question.id} className="border rounded-lg p-3 sm:p-4 bg-blue-50/30">
<div className="flex items-start gap-3 mb-3">
<div className="flex-shrink-0 mt-1">
{isCorrect ? (
<CheckCircle className="w-4 h-4 sm:w-5 sm:h-5 text-green-500" />
) : (
<XCircle className="w-4 h-4 sm:w-5 sm:h-5 text-red-500" />
)}
</div>
<div className="flex-1 min-w-0">
<h4 className="font-medium mb-2 text-sm sm:text-base text-balance">
{index + 1}{question.question}
</h4>
<div className="space-y-2 text-xs sm:text-sm">
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2">
<span className="text-muted-foreground text-xs"></span>
<Badge variant={isCorrect ? "default" : "destructive"} className="text-xs w-fit">
{userAnswer ? `${userAnswer}. ${userOptionText}` : "未作答"}
</Badge>
</div>
{!isCorrect && (
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2">
<span className="text-muted-foreground text-xs"></span>
<Badge variant="outline" className="border-green-500 text-green-700 text-xs w-fit">
{question.correct_answer}. {correctOptionText}
</Badge>
</div>
)}
{question.explanation && (
<div className="mt-2 p-2 sm:p-3 bg-muted/50 rounded text-xs sm:text-sm">
<strong></strong>
{question.explanation}
</div>
)}
</div>
</div>
</div>
</div>
)
})}
</div>
</div>
)}
{/* Creative Questions */}
{creativeQuestions.length > 0 && (
<div>
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<Lightbulb className="w-5 h-5 text-green-600" />
</h3>
<div className="space-y-4">
{creativeQuestions.map((question, index) => (
<div key={question.id} className="border rounded-lg p-3 sm:p-4 bg-green-50/30">
<div className="flex items-start justify-between mb-2 sm:mb-3">
<h4 className="font-medium text-sm sm:text-base"> {index + 1} </h4>
<Badge variant="outline" className="text-green-600 border-green-600 text-xs">
{question.score}
</Badge>
</div>
<div className="space-y-2 sm:space-y-3">
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-xs sm:text-sm mt-1 break-words">{question.statement}</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-xs sm:text-sm mt-1">{question.userAnswer}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-xs sm:text-sm mt-1 font-bold">{question.score} </p>
</div>
</div>
</div>
</div>
))}
</div>
</div>
)}
</div>
</CardContent>
</Card>
)}
{/* Actions */}
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4 justify-center">
<Button asChild size="lg" className="w-full sm:w-auto">
<Link href="/admin/results">
<ArrowLeft className="w-4 h-4 mr-2" />
<span></span>
</Link>
</Button>
</div>
</div>
</div>
</div>
)
}

View File

@@ -7,7 +7,6 @@ import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
import { Brain, Lightbulb, BarChart3, ArrowLeft, Search, Download, Filter, ChevronLeft, ChevronRight, Loader2, Eye } from "lucide-react" import { Brain, Lightbulb, BarChart3, ArrowLeft, Search, Download, Filter, ChevronLeft, ChevronRight, Loader2, Eye } from "lucide-react"
import Link from "next/link" import Link from "next/link"
@@ -82,10 +81,6 @@ function AdminResultsContent() {
const [testTypeFilter, setTestTypeFilter] = useState("all") const [testTypeFilter, setTestTypeFilter] = useState("all")
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
const [selectedResult, setSelectedResult] = useState<TestResult | null>(null)
const [showDetailModal, setShowDetailModal] = useState(false)
const [detailData, setDetailData] = useState<any>(null)
const [isLoadingDetail, setIsLoadingDetail] = useState(false)
useEffect(() => { useEffect(() => {
loadData() loadData()
@@ -251,29 +246,9 @@ function AdminResultsContent() {
} }
} }
const handleViewDetail = async (result: TestResult) => { const handleViewDetail = (result: TestResult) => {
setSelectedResult(result) // 跳轉到詳細頁面,而不是彈出視窗
setShowDetailModal(true) window.location.href = `/admin/results/detail/${result.id}?testType=${result.type}`
setIsLoadingDetail(true)
try {
const response = await fetch(`/api/admin/test-results/detail?testResultId=${result.id}&testType=${result.type}`)
const data = await response.json()
if (data.success) {
console.log('前端收到的詳細資料:', data.data)
console.log('題目數量:', data.data.questions?.length || 0)
setDetailData(data.data)
} else {
console.error('獲取詳細結果失敗:', data.message)
alert('獲取詳細結果失敗,請稍後再試')
}
} catch (error) {
console.error('獲取詳細結果錯誤:', error)
alert('獲取詳細結果時發生錯誤,請稍後再試')
} finally {
setIsLoadingDetail(false)
}
} }
return ( return (
@@ -642,204 +617,6 @@ function AdminResultsContent() {
</div> </div>
</div> </div>
{/* 詳細結果模態框 */}
<Dialog open={showDetailModal} onOpenChange={setShowDetailModal}>
<DialogContent className="max-w-4xl max-h-[80vh] overflow-y-auto w-[95vw] max-w-[95vw] sm:w-auto sm:max-w-4xl">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>
{selectedResult && `${selectedResult.userName} - ${getTestTypeInfo(selectedResult.type).name}`}
</DialogDescription>
</DialogHeader>
{isLoadingDetail ? (
<div className="flex items-center justify-center py-8">
<Loader2 className="w-6 h-6 animate-spin mr-2" />
<span>...</span>
</div>
) : detailData ? (
<div className="space-y-6">
{/* 基本資訊 */}
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-sm">{detailData.user.name}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-sm">{detailData.user.email}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-sm">{detailData.user.department}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-sm">{formatDate(detailData.result.completedAt)}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-sm font-bold text-lg">{detailData.result.score}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<Badge className={`${getScoreLevel(detailData.result.score).color} text-white`}>
{getScoreLevel(detailData.result.score).level}
</Badge>
</div>
</div>
</CardContent>
</Card>
{/* 題目詳情 */}
{detailData.questions && detailData.questions.length > 0 && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-6">
{/* 邏輯思維題目 */}
{detailData.questions.filter((q: any) => q.type === 'logic').length > 0 && (
<div>
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<Brain className="w-5 h-5 text-blue-600" />
</h3>
<div className="space-y-4">
{detailData.questions
.filter((q: any) => q.type === 'logic')
.map((question: any, index: number) => (
<div key={index} className="border rounded-lg p-3 sm:p-4 bg-blue-50/30">
<div className="flex items-start justify-between mb-2 sm:mb-3">
<h4 className="font-medium text-sm sm:text-base"> {index + 1} </h4>
<Badge variant={question.isCorrect ? "default" : "destructive"} className="text-xs">
{question.isCorrect ? "正確" : "錯誤"}
</Badge>
</div>
<div className="space-y-2 sm:space-y-3">
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-xs sm:text-sm mt-1 break-words">{question.question}</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<div className="space-y-1 mt-1">
{question.option_a && <p className="text-xs sm:text-sm break-words">A. {question.option_a}</p>}
{question.option_b && <p className="text-xs sm:text-sm break-words">B. {question.option_b}</p>}
{question.option_c && <p className="text-xs sm:text-sm break-words">C. {question.option_c}</p>}
{question.option_d && <p className="text-xs sm:text-sm break-words">D. {question.option_d}</p>}
{question.option_e && <p className="text-xs sm:text-sm break-words">E. {question.option_e}</p>}
</div>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<div className="space-y-1 mt-1">
<p className="text-xs sm:text-sm">: <span className="font-bold">{question.userAnswer}</span></p>
<p className="text-xs sm:text-sm">: <span className="font-bold text-green-600">{question.correctAnswer}</span></p>
</div>
</div>
</div>
{question.explanation && (
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-xs sm:text-sm mt-1 break-words">{question.explanation}</p>
</div>
)}
</div>
</div>
))}
</div>
</div>
)}
{/* 創意能力題目 */}
{detailData.questions.filter((q: any) => q.type === 'creative').length > 0 && (
<div>
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<Lightbulb className="w-5 h-5 text-green-600" />
</h3>
<div className="space-y-4">
{detailData.questions
.filter((q: any) => q.type === 'creative')
.map((question: any, index: number) => (
<div key={index} className="border rounded-lg p-3 sm:p-4 bg-green-50/30">
<div className="flex items-start justify-between mb-2 sm:mb-3">
<h4 className="font-medium text-sm sm:text-base"> {index + 1} </h4>
<Badge variant="outline" className="text-green-600 border-green-600 text-xs">
{question.score}
</Badge>
</div>
<div className="space-y-2 sm:space-y-3">
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-xs sm:text-sm mt-1 break-words">{question.statement}</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-xs sm:text-sm mt-1">{question.userAnswer}</p>
</div>
<div>
<label className="text-sm font-medium text-muted-foreground"></label>
<p className="text-xs sm:text-sm mt-1 font-bold">{question.score} </p>
</div>
</div>
</div>
</div>
))}
</div>
</div>
)}
</div>
</CardContent>
</Card>
)}
{/* 綜合測試詳細分析 */}
{detailData.result.type === 'combined' && detailData.result.details && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-3 gap-4">
<div className="text-center p-4 bg-blue-50 rounded-lg">
<h4 className="font-medium text-blue-800"></h4>
<p className="text-2xl font-bold text-blue-600">{detailData.result.details.logicScore}</p>
</div>
<div className="text-center p-4 bg-green-50 rounded-lg">
<h4 className="font-medium text-green-800"></h4>
<p className="text-2xl font-bold text-green-600">{detailData.result.details.creativeScore}</p>
</div>
<div className="text-center p-4 bg-purple-50 rounded-lg">
<h4 className="font-medium text-purple-800"></h4>
<p className="text-2xl font-bold text-purple-600">{detailData.result.details.abilityBalance}</p>
</div>
</div>
</CardContent>
</Card>
)}
</div>
) : (
<div className="text-center py-8 text-muted-foreground">
</div>
)}
</DialogContent>
</Dialog>
</div> </div>
) )
} }

View File

@@ -4,9 +4,9 @@
"private": true, "private": true,
"scripts": { "scripts": {
"build": "next build", "build": "next build",
"dev": "next dev", "dev": "next dev -p 12025",
"lint": "next lint", "lint": "next lint",
"start": "next start" "start": "next start -p 12025"
}, },
"dependencies": { "dependencies": {
"@hookform/resolvers": "^3.10.0", "@hookform/resolvers": "^3.10.0",

View File

@@ -1,19 +0,0 @@
"題目ID","陳述內容","類別","反向計分"
"1","我常能從不同角度看事情,接受多元觀點。","flexibility",""
"2","我有時會提出具挑戰性或爭議性的想法,促使他人表達不同觀點。","innovation",""
"3","我習慣一次只做一件事,不輕易嘗試新方法。","flexibility",""
"4","當靈感枯竭時,我仍能找到突破的方法。","imagination",""
"5","我喜歡與不同背景的人合作,從差異中獲得新想法。","innovation",""
"6","我通常笑得比別人多,並帶動正面氛圍。","originality",""
"7","我會追根究柢思考,直到找到事件背後的原因。","imagination",""
"8","我更喜歡看到整體格局,而不是專注在細節上。","originality",""
"9","我認為規定和框架在組織中絕對必要。","flexibility",""
"10","我通常會先做詳細規劃,然後按部就班執行。","flexibility",""
"11","我能找到更快的方法或捷徑完成任務。","innovation",""
"12","我喜歡解謎或挑戰看似難解的問題。","imagination",""
"13","我能接受頻繁的改變,並調整自己因應。","flexibility",""
"14","我通常不輕易說出心中想法,除非被問到。","originality",""
"15","我經常追求穩定感,避免風險。","flexibility",""
"16","當遇到一個陌生問題時,我會主動去探索,即使沒有明確指引。","innovation",""
"17","當既有方法行不通時,我會刻意嘗試完全相反的方向。","originality",""
"18","即使存在風險,我也願意嘗試新的解決方法。","innovation",""
1 題目ID 陳述內容 類別 反向計分
2 1 我常能從不同角度看事情,接受多元觀點。 flexibility
3 2 我有時會提出具挑戰性或爭議性的想法,促使他人表達不同觀點。 innovation
4 3 我習慣一次只做一件事,不輕易嘗試新方法。 flexibility
5 4 當靈感枯竭時,我仍能找到突破的方法。 imagination
6 5 我喜歡與不同背景的人合作,從差異中獲得新想法。 innovation
7 6 我通常笑得比別人多,並帶動正面氛圍。 originality
8 7 我會追根究柢思考,直到找到事件背後的原因。 imagination
9 8 我更喜歡看到整體格局,而不是專注在細節上。 originality
10 9 我認為規定和框架在組織中絕對必要。 flexibility
11 10 我通常會先做詳細規劃,然後按部就班執行。 flexibility
12 11 我能找到更快的方法或捷徑完成任務。 innovation
13 12 我喜歡解謎或挑戰看似難解的問題。 imagination
14 13 我能接受頻繁的改變,並調整自己因應。 flexibility
15 14 我通常不輕易說出心中想法,除非被問到。 originality
16 15 我經常追求穩定感,避免風險。 flexibility
17 16 當遇到一個陌生問題時,我會主動去探索,即使沒有明確指引。 innovation
18 17 當既有方法行不通時,我會刻意嘗試完全相反的方向。 originality
19 18 即使存在風險,我也願意嘗試新的解決方法。 innovation

View File

@@ -1,11 +0,0 @@
"題目ID","題目內容","選項A","選項B","選項C","選項D","選項E","正確答案","解釋"
"11","如果所有的玫瑰都是花,而有些花是紅色的,那麼我們可以確定:","所有玫瑰都是紅色的","有些玫瑰是紅色的","不能確定玫瑰的顏色","沒有玫瑰是紅色的","所有花都是玫瑰","C","根據題目條件1) 所有玫瑰都是花 2) 有些花是紅色的。我們只能確定有些花是紅色的,但無法確定這些紅色的花中是否包含玫瑰。因此不能確定玫瑰的顏色。"
"12","2, 4, 8, 16, ?, 64 中間的數字是:","24","28","32","36","40","C","這是一個等比數列公比為2。數列規律2×2=4, 4×2=8, 8×2=16, 16×2=32, 32×2=64。所以中間的數字是32。"
"13","在一個圓形跑道上強強和茂茂同時同地出發強強每分鐘跑400米茂茂每分鐘跑300米。如果跑道周長是1200米強強第一次追上茂茂需要多少分鐘","10分鐘","12分鐘","15分鐘","18分鐘","20分鐘","B","強強比茂茂每分鐘快100米400-300=100。要追上茂茂強強需要比茂茂多跑一圈1200米。所需時間 = 1200米 ÷ 100米/分鐘 = 12分鐘。"
"14","五個人坐成一排已知A不坐兩端B坐在C的左邊D坐在E的右邊。如果E坐在中間那麼從左到右的順序可能是","B-C-E-D-A","D-B-E-C-A","B-A-E-C-D","D-A-E-B-C","B-C-E-A-D","B","E在中間第3位。D在E的右邊所以D在第4或5位。B在C的左邊所以B在C前面。A不在兩端所以A在第2、3、4位。只有選項B符合D(1)-B(2)-E(3)-C(4)-A(5)。"
"15","如果今天是星期三那麼100天後是星期幾","星期一","星期二","星期三","星期四","星期五","E","一週有7天100÷7=14餘2。所以100天後相當於14週又2天。從星期三開始往後數2天星期三→星期四→星期五。"
"16","一個班級有30個學生其中20個會游泳25個會騎車那麼既會游泳又會騎車的學生至少有多少人","10人","15人","20人","25人","30人","B","使用容斥原理:會游泳或會騎車的人數 = 會游泳人數 + 會騎車人數 - 既會游泳又會騎車的人數。30 = 20 + 25 - 既會游泳又會騎車的人數。所以既會游泳又會騎車的人數 = 45 - 30 = 15人。"
"17","四個朋友分別戴紅、藍、綠、黃四種顏色的帽子。已知:小王不戴紅帽,小李不戴藍帽,小陳不戴綠帽,小趙不戴黃帽。如果小王戴藍帽,那麼小趙戴什麼顏色的帽子?","紅帽","藍帽","綠帽","黃帽","無法確定","E","小王戴藍帽。小李不戴藍帽,所以小李只能戴紅、綠、黃帽。小陳不戴綠帽,所以小陳只能戴紅、藍、黃帽(但藍帽已被小王戴走)。小趙不戴黃帽,所以小趙只能戴紅、藍、綠帽(但藍帽已被小王戴走)。由於信息不足,無法確定小趙的帽子顏色。"
"18","在一個密碼中A=1, B=2, C=3...Z=26。如果「CAT」的數值和是24那麼「DOG」的數值和是","26","27","28","29","30","A","C=3, A=1, T=20所以CAT=3+1+20=24。D=4, O=15, G=7所以DOG=4+15+7=26。"
"19","一隻青蛙掉進了一口18米深的井裡。每天白天它向上爬6米晚上向下滑落3米。按這一速度問青蛙多少天能爬出井口","3","4","5","6","7","C","每天淨爬升6-3=3米。前4天共爬升4×3=12米還剩18-12=6米。第5天白天爬6米就能到達井口不需要再滑落。所以需要5天。"
"20","有兄妹倆1993年的時候哥哥21歲妹妹的年齡當時是7歲請問到什麼時候哥哥的年齡才會是妹妹年齡的兩倍","1997年","1998年","1999年","2000年","2001年","D","1993年時哥哥21歲妹妹7歲年齡差是14歲。設x年後哥哥年齡是妹妹的2倍21+x = 2(7+x)解得x=7。所以是1993+7=2000年。驗證2000年哥哥28歲妹妹14歲28=2×14。"
1 題目ID 題目內容 選項A 選項B 選項C 選項D 選項E 正確答案 解釋
2 11 如果所有的玫瑰都是花,而有些花是紅色的,那麼我們可以確定: 所有玫瑰都是紅色的 有些玫瑰是紅色的 不能確定玫瑰的顏色 沒有玫瑰是紅色的 所有花都是玫瑰 C 根據題目條件:1) 所有玫瑰都是花 2) 有些花是紅色的。我們只能確定有些花是紅色的,但無法確定這些紅色的花中是否包含玫瑰。因此不能確定玫瑰的顏色。
3 12 2, 4, 8, 16, ?, 64 中間的數字是: 24 28 32 36 40 C 這是一個等比數列,公比為2。數列規律:2×2=4, 4×2=8, 8×2=16, 16×2=32, 32×2=64。所以中間的數字是32。
4 13 在一個圓形跑道上,強強和茂茂同時同地出發,強強每分鐘跑400米,茂茂每分鐘跑300米。如果跑道周長是1200米,強強第一次追上茂茂需要多少分鐘? 10分鐘 12分鐘 15分鐘 18分鐘 20分鐘 B 強強比茂茂每分鐘快100米(400-300=100)。要追上茂茂,強強需要比茂茂多跑一圈(1200米)。所需時間 = 1200米 ÷ 100米/分鐘 = 12分鐘。
5 14 五個人坐成一排,已知:A不坐兩端,B坐在C的左邊,D坐在E的右邊。如果E坐在中間,那麼從左到右的順序可能是: B-C-E-D-A D-B-E-C-A B-A-E-C-D D-A-E-B-C B-C-E-A-D B E在中間(第3位)。D在E的右邊,所以D在第4或5位。B在C的左邊,所以B在C前面。A不在兩端,所以A在第2、3、4位。只有選項B符合:D(1)-B(2)-E(3)-C(4)-A(5)。
6 15 如果今天是星期三,那麼100天後是星期幾? 星期一 星期二 星期三 星期四 星期五 E 一週有7天,100÷7=14餘2。所以100天後相當於14週又2天。從星期三開始,往後數2天:星期三→星期四→星期五。
7 16 一個班級有30個學生,其中20個會游泳,25個會騎車,那麼既會游泳又會騎車的學生至少有多少人? 10人 15人 20人 25人 30人 B 使用容斥原理:會游泳或會騎車的人數 = 會游泳人數 + 會騎車人數 - 既會游泳又會騎車的人數。30 = 20 + 25 - 既會游泳又會騎車的人數。所以既會游泳又會騎車的人數 = 45 - 30 = 15人。
8 17 四個朋友分別戴紅、藍、綠、黃四種顏色的帽子。已知:小王不戴紅帽,小李不戴藍帽,小陳不戴綠帽,小趙不戴黃帽。如果小王戴藍帽,那麼小趙戴什麼顏色的帽子? 紅帽 藍帽 綠帽 黃帽 無法確定 E 小王戴藍帽。小李不戴藍帽,所以小李只能戴紅、綠、黃帽。小陳不戴綠帽,所以小陳只能戴紅、藍、黃帽(但藍帽已被小王戴走)。小趙不戴黃帽,所以小趙只能戴紅、藍、綠帽(但藍帽已被小王戴走)。由於信息不足,無法確定小趙的帽子顏色。
9 18 在一個密碼中,A=1, B=2, C=3...Z=26。如果「CAT」的數值和是24,那麼「DOG」的數值和是: 26 27 28 29 30 A C=3, A=1, T=20,所以CAT=3+1+20=24。D=4, O=15, G=7,所以DOG=4+15+7=26。
10 19 一隻青蛙掉進了一口18米深的井裡。每天白天它向上爬6米,晚上向下滑落3米。按這一速度,問青蛙多少天能爬出井口? 3 4 5 6 7 C 每天淨爬升:6-3=3米。前4天共爬升:4×3=12米,還剩18-12=6米。第5天白天爬6米就能到達井口,不需要再滑落。所以需要5天。
11 20 有兄妹倆,1993年的時候,哥哥21歲,妹妹的年齡當時是7歲,請問到什麼時候,哥哥的年齡才會是妹妹年齡的兩倍? 1997年 1998年 1999年 2000年 2001年 D 1993年時哥哥21歲,妹妹7歲,年齡差是14歲。設x年後哥哥年齡是妹妹的2倍:21+x = 2(7+x),解得x=7。所以是1993+7=2000年。驗證:2000年哥哥28歲,妹妹14歲,28=2×14。