All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 2m49s
184 lines
5.1 KiB
TypeScript
184 lines
5.1 KiB
TypeScript
"use client";
|
|
|
|
import { Card, Button, Avatar, Badge } from "@/components/ui";
|
|
|
|
interface ChallengeCardProps {
|
|
challenge: {
|
|
id: string;
|
|
challenger: {
|
|
id: string;
|
|
username: string;
|
|
avatar: string | null;
|
|
};
|
|
challenged: {
|
|
id: string;
|
|
username: string;
|
|
avatar: string | null;
|
|
};
|
|
title: string;
|
|
description: string;
|
|
pointsReward: number;
|
|
status: string;
|
|
adminComment: string | null;
|
|
winner?: {
|
|
id: string;
|
|
username: string;
|
|
} | null;
|
|
createdAt: string;
|
|
acceptedAt: string | null;
|
|
completedAt: string | null;
|
|
};
|
|
currentUserId?: string;
|
|
onAccept?: (challengeId: string) => void;
|
|
onCancel?: (challengeId: string) => void;
|
|
isPending?: boolean;
|
|
}
|
|
|
|
const getStatusLabel = (status: string) => {
|
|
switch (status) {
|
|
case "PENDING":
|
|
return "En attente d'acceptation";
|
|
case "ACCEPTED":
|
|
return "En cours - En attente de désignation du gagnant";
|
|
case "COMPLETED":
|
|
return "Complété";
|
|
case "REJECTED":
|
|
return "Rejeté";
|
|
case "CANCELLED":
|
|
return "Annulé";
|
|
default:
|
|
return status;
|
|
}
|
|
};
|
|
|
|
const getStatusVariant = (
|
|
status: string
|
|
): "default" | "success" | "warning" | "danger" | "info" => {
|
|
switch (status) {
|
|
case "PENDING":
|
|
return "warning";
|
|
case "ACCEPTED":
|
|
return "info";
|
|
case "COMPLETED":
|
|
return "success";
|
|
case "REJECTED":
|
|
return "danger";
|
|
case "CANCELLED":
|
|
return "default";
|
|
default:
|
|
return "default";
|
|
}
|
|
};
|
|
|
|
export default function ChallengeCard({
|
|
challenge,
|
|
currentUserId,
|
|
onAccept,
|
|
onCancel,
|
|
isPending = false,
|
|
}: ChallengeCardProps) {
|
|
const isChallenger = challenge.challenger.id === currentUserId;
|
|
const isChallenged = challenge.challenged.id === currentUserId;
|
|
const canAccept = challenge.status === "PENDING" && isChallenged;
|
|
const canCancel =
|
|
(challenge.status === "PENDING" || challenge.status === "ACCEPTED") &&
|
|
(isChallenger || isChallenged);
|
|
|
|
return (
|
|
<Card variant="dark" className="p-6">
|
|
<div className="flex items-start justify-between gap-4">
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-2 flex-wrap">
|
|
<h3 className="text-lg font-bold text-pixel-gold">
|
|
{challenge.title}
|
|
</h3>
|
|
<Badge variant={getStatusVariant(challenge.status)} size="xs">
|
|
{getStatusLabel(challenge.status)}
|
|
</Badge>
|
|
</div>
|
|
|
|
<p className="text-gray-300 mb-4">{challenge.description}</p>
|
|
|
|
<div className="flex items-center gap-4 mb-2 flex-wrap">
|
|
<div className="flex items-center gap-2">
|
|
<Avatar
|
|
src={challenge.challenger.avatar}
|
|
username={challenge.challenger.username}
|
|
size="sm"
|
|
/>
|
|
<span className="text-sm text-gray-300">
|
|
{challenge.challenger.username}
|
|
</span>
|
|
</div>
|
|
<span className="text-gray-500">VS</span>
|
|
<div className="flex items-center gap-2">
|
|
<Avatar
|
|
src={challenge.challenged.avatar}
|
|
username={challenge.challenged.username}
|
|
size="sm"
|
|
/>
|
|
<span className="text-sm text-gray-300">
|
|
{challenge.challenged.username}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="text-sm text-gray-400">
|
|
Récompense:{" "}
|
|
<span className="text-pixel-gold font-bold">
|
|
{challenge.pointsReward} points
|
|
</span>
|
|
</div>
|
|
|
|
{challenge.winner && (
|
|
<div className="text-sm text-green-400 mt-2">
|
|
🏆 Gagnant: {challenge.winner.username}
|
|
</div>
|
|
)}
|
|
|
|
{challenge.adminComment && (
|
|
<div className="text-xs text-gray-500 mt-2 italic">
|
|
Admin: {challenge.adminComment}
|
|
</div>
|
|
)}
|
|
|
|
<div className="text-xs text-gray-500 mt-2">
|
|
Créé le: {new Date(challenge.createdAt).toLocaleDateString("fr-FR")}
|
|
{challenge.acceptedAt &&
|
|
` • Accepté le: ${new Date(challenge.acceptedAt).toLocaleDateString("fr-FR")}`}
|
|
{challenge.completedAt &&
|
|
` • Complété le: ${new Date(challenge.completedAt).toLocaleDateString("fr-FR")}`}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-2">
|
|
{canAccept && onAccept && (
|
|
<Button
|
|
onClick={() => onAccept(challenge.id)}
|
|
variant="primary"
|
|
size="sm"
|
|
disabled={isPending}
|
|
>
|
|
Accepter
|
|
</Button>
|
|
)}
|
|
{canCancel && onCancel && (
|
|
<Button
|
|
onClick={() => {
|
|
if (confirm("Êtes-vous sûr de vouloir annuler ce défi ?")) {
|
|
onCancel(challenge.id);
|
|
}
|
|
}}
|
|
variant="secondary"
|
|
size="sm"
|
|
disabled={isPending}
|
|
>
|
|
Annuler
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|