126 lines
5.4 KiB
TypeScript
126 lines
5.4 KiB
TypeScript
import { db } from "@/lib/db";
|
||
import { requireAdmin } from "@/lib/session";
|
||
import { notFound } from "next/navigation";
|
||
import Image from "next/image";
|
||
import { formatPersianDate, formatPersianDateTime } from "@/lib/persianDate";
|
||
import { CARD_TIER_LABELS, getCardTierBadgeClass, resolveQuizRewardTier } from "@/lib/cardTier";
|
||
|
||
export default async function QuizResultsPage({ params }: { params: Promise<{ id: string }> }) {
|
||
await requireAdmin();
|
||
const { id } = await params;
|
||
|
||
const quiz = await db.dailyQuiz.findUnique({
|
||
where: { id },
|
||
include: {
|
||
questions: { orderBy: { order: "asc" } },
|
||
submissions: {
|
||
include: { user: { select: { id: true, name: true, email: true } } },
|
||
orderBy: [{ score: "desc" }, { submittedAt: "asc" }],
|
||
},
|
||
},
|
||
});
|
||
|
||
if (!quiz) notFound();
|
||
|
||
const awardedCards = await db.goldenCard.findMany({
|
||
where: { quizId: id },
|
||
include: {
|
||
user: { select: { id: true, name: true, email: true } },
|
||
player: { include: { country: true } },
|
||
},
|
||
orderBy: { acquiredDate: "desc" },
|
||
});
|
||
|
||
return (
|
||
<div className="flex flex-col gap-6">
|
||
<div className="flex justify-between items-center">
|
||
<h1 className="text-2xl font-bold">نتایج کوییز - {formatPersianDate(new Date(quiz.date))}</h1>
|
||
<span
|
||
className={`text-sm px-3 py-1 rounded-full ${
|
||
quiz.isProcessed ? "bg-green-100 text-green-700" : "bg-yellow-100 text-yellow-700"
|
||
}`}
|
||
>
|
||
{quiz.isProcessed ? "تخصیص کارت انجام شده" : "در انتظار تخصیص کارت"}
|
||
</span>
|
||
</div>
|
||
|
||
{awardedCards.length > 0 && (
|
||
<div className="bg-white rounded-2xl shadow p-6">
|
||
<h2 className="font-bold text-lg mb-4">کارت های تخصیص داده شده</h2>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
{awardedCards.map((card) => (
|
||
<div key={card.id} className="border rounded-xl p-4 flex items-center gap-3 bg-slate-50">
|
||
{card.player.image ? (
|
||
<div className="relative w-12 h-12 rounded-full overflow-hidden border-2 border-slate-200">
|
||
<Image src={`/uploads/players/${card.player.image}`} alt={card.player.name} fill className="object-cover" />
|
||
</div>
|
||
) : (
|
||
<div className="w-12 h-12 rounded-full bg-slate-200 flex items-center justify-center text-xl">*</div>
|
||
)}
|
||
<div>
|
||
<p className="font-bold text-sm">{card.user.name ?? card.user.email}</p>
|
||
<p className="text-xs text-gray-500">{card.player.name} - {card.player.country.name}</p>
|
||
<div className="mt-1 flex items-center gap-2">
|
||
<span className={`rounded-full px-2 py-0.5 text-xs font-bold ${getCardTierBadgeClass(card.cardTier)}`}>
|
||
{CARD_TIER_LABELS[card.cardTier]}
|
||
</span>
|
||
<span
|
||
className={`text-xs px-2 py-0.5 rounded-full ${
|
||
card.status === "OPENED" ? "bg-green-100 text-green-700" : "bg-gray-100 text-gray-600"
|
||
}`}
|
||
>
|
||
{card.status === "OPENED" ? "باز شده" : "مهر شده"}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="bg-white rounded-2xl shadow overflow-hidden">
|
||
<div className="px-5 py-4 border-b">
|
||
<h2 className="font-bold">همه شرکت کنندگان ({quiz.submissions.length})</h2>
|
||
</div>
|
||
<table className="w-full text-sm">
|
||
<thead className="bg-gray-50 text-gray-600">
|
||
<tr>
|
||
<th className="text-right px-5 py-3">کاربر</th>
|
||
<th className="text-right px-5 py-3">نتیجه</th>
|
||
<th className="text-right px-5 py-3">زمان ارسال</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{quiz.submissions.map((submission) => {
|
||
const rewardTier = resolveQuizRewardTier(quiz, submission.correctAnswers);
|
||
|
||
return (
|
||
<tr key={submission.id} className={`border-t ${rewardTier ? "bg-green-50" : ""}`}>
|
||
<td className="px-5 py-3">{submission.user.name ?? submission.user.email}</td>
|
||
<td className="px-5 py-3">
|
||
<div className="flex flex-col gap-1">
|
||
<span className={`font-bold ${rewardTier ? "text-green-600" : "text-gray-700"}`}>
|
||
{submission.score}%
|
||
</span>
|
||
<span className="text-xs text-gray-500">{submission.correctAnswers} جواب صحیح</span>
|
||
{rewardTier && (
|
||
<span className={`inline-flex w-fit rounded-full px-2 py-0.5 text-xs font-bold ${getCardTierBadgeClass(rewardTier)}`}>
|
||
{CARD_TIER_LABELS[rewardTier]}
|
||
</span>
|
||
)}
|
||
</div>
|
||
</td>
|
||
<td className="px-5 py-3 text-gray-500 text-xs">
|
||
{formatPersianDateTime(new Date(submission.submittedAt))}
|
||
</td>
|
||
</tr>
|
||
);
|
||
})}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|