Add admin challenge acceptance functionality: Implement adminAcceptChallenge method in ChallengeService, allowing admins to accept pending challenges. Update ChallengeManagement component to include a button for accepting challenges, enhancing admin capabilities and user feedback handling.
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 4m48s
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 4m48s
This commit is contained in:
@@ -234,3 +234,37 @@ export async function reactivateChallenge(challengeId: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function adminAcceptChallenge(challengeId: string) {
|
||||||
|
try {
|
||||||
|
await checkAdminAccess();
|
||||||
|
|
||||||
|
const challenge = await challengeService.adminAcceptChallenge(challengeId);
|
||||||
|
|
||||||
|
revalidatePath("/admin");
|
||||||
|
revalidatePath("/challenges");
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Défi accepté avec succès",
|
||||||
|
data: challenge,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Admin accept challenge error:", error);
|
||||||
|
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
if (error instanceof NotFoundError) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
if (error instanceof Error && error.message.includes("Accès refusé")) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: "Une erreur est survenue lors de l'acceptation du défi",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
deleteChallenge,
|
deleteChallenge,
|
||||||
adminCancelChallenge,
|
adminCancelChallenge,
|
||||||
reactivateChallenge,
|
reactivateChallenge,
|
||||||
|
adminAcceptChallenge,
|
||||||
} from "@/actions/admin/challenges";
|
} from "@/actions/admin/challenges";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -226,6 +227,29 @@ export default function ChallengeManagement() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAdminAccept = async (challengeId: string) => {
|
||||||
|
if (
|
||||||
|
!confirm(
|
||||||
|
"Êtes-vous sûr de vouloir accepter ce défi à la place de l'utilisateur ?"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startTransition(async () => {
|
||||||
|
const result = await adminAcceptChallenge(challengeId);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setSuccessMessage("Défi accepté avec succès");
|
||||||
|
fetchChallenges();
|
||||||
|
setTimeout(() => setSuccessMessage(null), 5000);
|
||||||
|
} else {
|
||||||
|
setErrorMessage(result.error || "Erreur lors de l'acceptation");
|
||||||
|
setTimeout(() => setErrorMessage(null), 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="text-center text-pixel-gold py-8">Chargement...</div>
|
<div className="text-center text-pixel-gold py-8">Chargement...</div>
|
||||||
@@ -376,6 +400,16 @@ export default function ChallengeManagement() {
|
|||||||
>
|
>
|
||||||
Modifier
|
Modifier
|
||||||
</Button>
|
</Button>
|
||||||
|
{challenge.status === "PENDING" && (
|
||||||
|
<Button
|
||||||
|
onClick={() => handleAdminAccept(challenge.id)}
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
Accepter le défi
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
{challenge.status === "ACCEPTED" && (
|
{challenge.status === "ACCEPTED" && (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setSelectedChallenge(challenge)}
|
onClick={() => setSelectedChallenge(challenge)}
|
||||||
|
|||||||
@@ -120,6 +120,34 @@ export class ChallengeService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepte un défi en tant qu'admin (bypass les vérifications utilisateur)
|
||||||
|
*/
|
||||||
|
async adminAcceptChallenge(challengeId: string): Promise<Challenge> {
|
||||||
|
const challenge = await prisma.challenge.findUnique({
|
||||||
|
where: { id: challengeId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!challenge) {
|
||||||
|
throw new NotFoundError("Défi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que le défi est en attente
|
||||||
|
if (challenge.status !== "PENDING") {
|
||||||
|
throw new ValidationError(
|
||||||
|
"Ce défi ne peut plus être accepté (statut: " + challenge.status + ")"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return prisma.challenge.update({
|
||||||
|
where: { id: challengeId },
|
||||||
|
data: {
|
||||||
|
status: "ACCEPTED",
|
||||||
|
acceptedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Annule un défi (par le challenger ou le challenged)
|
* Annule un défi (par le challenger ou le challenged)
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user