Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/src/controllers/tent.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const toggleTentConfirmation = async (req: Request, res: Response) => {
to: [user1.email, user2.email],
subject: confirmed
? "🎉 Votre tente a été validée !"
: "⛺ Votre tente a été dévalidée",
: "⛺ Votre tente a été invalidée",
text: "", // optionnel
html: htmlEmail,
};
Expand All @@ -101,7 +101,7 @@ export const toggleTentConfirmation = async (req: Request, res: Response) => {
Ok(res, {
msg: confirmed
? "Tente validée et email envoyé."
: "Tente dévalidée et email envoyé.",
: "Tente invalidée et email envoyé.",
});
} catch (err: any) {
console.error(err);
Expand Down
26 changes: 24 additions & 2 deletions backend/src/services/challenge.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,29 @@ export const validateChallenge = async ({
throw new Error("Type de challenge non valide");
}

// 3. Créer l'objet de validation du challenge
// 3. Vérifier si ce challenge a déjà été validé pour cette cible
const targetColumn =
type === "user"
? challengeValidationSchema.target_user_id
: type === "team"
? challengeValidationSchema.target_team_id
: challengeValidationSchema.target_faction_id;

const existingValidation = await db
.select()
.from(challengeValidationSchema)
.where(
and(
eq(challengeValidationSchema.challenge_id, challengeId),
eq(targetColumn, targetId)
)
);

if (existingValidation.length > 0) {
throw new Error("Ce challenge a déjà été validé pour cette cible");
}

// 4. Créer l'objet de validation du challenge
const newChallengeValidationPoints = {
challenge_id: challengeId,
validated_by_admin_id: validatedBy,
Expand All @@ -94,7 +116,7 @@ export const validateChallenge = async ({
target_faction_id: target_faction_id,
};

// 4. Insérer la validation du challenge dans la base de données
// 5. Insérer la validation du challenge dans la base de données
const inserted = await db.insert(challengeValidationSchema).values(newChallengeValidationPoints).returning();

return inserted[0];
Expand Down
2 changes: 1 addition & 1 deletion backend/src/services/tent.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,5 @@ export const toggleTentConfirmation = async (
)
);

return { success: true, message: confirmed ? "Tente validée." : "Tente dévalidée." };
return { success: true, message: confirmed ? "Tente validée." : "Tente invalidée." };
};
2 changes: 1 addition & 1 deletion backend/src/utils/emailtemplates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ export const templateNotifyTentConfirmation = `
<p style="margin: 10px 0; font-size: 16px;">
La tente entre <strong>{{user1}}</strong> et <strong>{{user2}}</strong> a été
<strong style="color: {{#if confirmed}}green{{else}}red{{/if}};">
{{#if confirmed}}validée{{else}}dévalidée{{/if}}
{{#if confirmed}}validée{{else}}invalidée{{/if}}
</strong>.
</p>

Expand Down
48 changes: 23 additions & 25 deletions frontend/src/components/Admin/AdminChallenge/adminChalengeList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CheckCircle2, Edit, Search, Trash2 } from "lucide-react";
import { useMemo, useState } from "react";
import Select, { type SingleValue } from "react-select";
import Select from "react-select";
import Swal from "sweetalert2";

import { type Challenge } from "../../../interfaces/challenge.interface";
Expand All @@ -26,7 +26,7 @@ type ValidationTarget = "user" | "team" | "faction";
const AdminChallengeList = ({ challenges, refreshChallenges, onEdit, teams, factions, users }: Props) => {
const [showValidationFormForId, setShowValidationFormForId] = useState<number | null>(null);
const [validationType, setValidationType] = useState<ValidationTarget | null>(null);
const [selectedTargetId, setSelectedTargetId] = useState<number | null>(null);
const [selectedTargetIds, setSelectedTargetIds] = useState<number[]>([]);
const [searchTerm, setSearchTerm] = useState("");


Expand Down Expand Up @@ -62,14 +62,20 @@ const AdminChallengeList = ({ challenges, refreshChallenges, onEdit, teams, fact
}
};

const handleValidate = async () => {
if (!showValidationFormForId || !validationType || !selectedTargetId) return;
const handleValidationCLick = async (c: Challenge) => {
setShowValidationFormForId(c.id);
setValidationType(c.category.toLowerCase() as ValidationTarget);
}

const handleValidate = async () => {
if (!showValidationFormForId || !validationType || selectedTargetIds.length === 0) return;

for (const targetId of selectedTargetIds) {
try {
const res = await validateChallenge({
challengeId: showValidationFormForId,
type: validationType,
targetId: selectedTargetId,
targetId: targetId,
});

Swal.fire({
Expand All @@ -82,20 +88,21 @@ const AdminChallengeList = ({ challenges, refreshChallenges, onEdit, teams, fact

setShowValidationFormForId(null);
setValidationType(null);
setSelectedTargetId(null);
setSelectedTargetIds([]);
refreshChallenges();
} catch (err) {
console.error("Erreur lors de la validation du challenge", err);
Swal.fire({
icon: "error",
title: "Erreur ❌",
text: "Impossible de valider ce challenge. Réessaie plus tard.",
text: err as string,
});
}
}
};

return (
<Card className="w-full max-w-3xl mx-auto">
<Card className="w-full max-w-7xl mx-auto">
<CardHeader>
<CardTitle className="text-2xl font-semibold text-gray-800 text-center">
📜 Challenges
Expand Down Expand Up @@ -145,7 +152,7 @@ const AdminChallengeList = ({ challenges, refreshChallenges, onEdit, teams, fact
<Trash2 className="w-4 h-4" /> Supprimer
</Button>
<Button
onClick={() => setShowValidationFormForId(c.id)}
onClick={() => {handleValidationCLick(c)}}
className="bg-blue-600 hover:bg-blue-700 text-white flex items-center gap-2"
>
<CheckCircle2 className="w-4 h-4" /> Valider
Expand All @@ -157,23 +164,12 @@ const AdminChallengeList = ({ challenges, refreshChallenges, onEdit, teams, fact
<CardContent className="p-4 space-y-4">
<h4 className="font-bold text-lg">✅ Valider le challenge</h4>

<Select
placeholder="Choisir le type de cible"
onChange={(option: SingleValue<{ value: ValidationTarget; label: string }>) => {
setValidationType(option?.value ?? null);
setSelectedTargetId(null);
}}
options={[
{ value: "user", label: "Utilisateur" },
{ value: "team", label: "Équipe" },
{ value: "faction", label: "Faction" },
]}
/>

{validationType === "user" && (
<Select
isMulti
placeholder="Sélectionner un utilisateur"
onChange={(option) => setSelectedTargetId(Number(option?.value))}
onChange={(options) => setSelectedTargetIds(options.map((o) => Number(o.value)))}
options={users.map((u: User) => ({
value: u.userId,
label: `${u.firstName} ${u.lastName}`,
Expand All @@ -183,8 +179,9 @@ const AdminChallengeList = ({ challenges, refreshChallenges, onEdit, teams, fact

{validationType === "team" && (
<Select
isMulti
placeholder="Sélectionner une équipe"
onChange={(option) => setSelectedTargetId(Number(option?.value))}
onChange={(options) => setSelectedTargetIds(options.map((o) => Number(o.value)))}
options={teams.map((t: Team) => ({
value: t.teamId,
label: t.name,
Expand All @@ -194,8 +191,9 @@ const AdminChallengeList = ({ challenges, refreshChallenges, onEdit, teams, fact

{validationType === "faction" && (
<Select
isMulti
placeholder="Sélectionner une faction"
onChange={(option) => setSelectedTargetId(Number(option?.value))}
onChange={(options) => setSelectedTargetIds(options.map((o) => Number(o.value)))}
options={factions.map((f: Faction) => ({
value: f.factionId,
label: f.name,
Expand All @@ -214,7 +212,7 @@ const AdminChallengeList = ({ challenges, refreshChallenges, onEdit, teams, fact
onClick={() => {
setShowValidationFormForId(null);
setValidationType(null);
setSelectedTargetId(null);
setSelectedTargetIds([]);
}}
className="bg-gray-400 hover:bg-gray-500 text-white"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const AdminChallengeAddPointsForm = () => {

return (
<div>
<Card className="w-full max-w-3xl mx-auto">
<Card className="w-full max-w-7xl mx-auto">
<CardHeader>
<CardTitle className="text-2xl font-semibold text-gray-800 text-center">
🎯 Ajouter des points à une faction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,19 @@ const ChallengeEditor = ({ editingChallenge, setEditingChallenge, refreshChallen
setEditingChallenge(null);
};

const resetFormPostCreation = () => {
setForm({ title: "", description: "", category: form.category, points: 0 });
setEditingChallenge(null);
};


const handleSubmit = async () => {
setLoading(true);
try {
if (!form.title || !form.description || !form.category) {
Swal.fire({ icon: "error", title: "Tous les champs doivent être remplis" });
return;
}
if (editingChallenge) {
await updateChallenge({ id: editingChallenge.id, ...form });
Swal.fire({ icon: "success", title: "Challenge mis à jour !" });
Expand All @@ -53,7 +63,7 @@ const ChallengeEditor = ({ editingChallenge, setEditingChallenge, refreshChallen
Swal.fire({ icon: "success", title: "Challenge créé !" });
}
refreshChallenges();
resetForm();
resetFormPostCreation();
} catch {
Swal.fire({ icon: "error", title: "Erreur lors de l'enregistrement" });
} finally {
Expand All @@ -62,7 +72,7 @@ const ChallengeEditor = ({ editingChallenge, setEditingChallenge, refreshChallen
};

return (
<Card className="w-full max-w-3xl mx-auto">
<Card className="w-full max-w-7xl mx-auto">
<CardHeader>
<CardTitle className="text-2xl font-semibold text-gray-800 text-center">
{editingChallenge ? "✏️ Modifier Challenge" : "🛠️ Créer Challenge"}
Expand Down
Loading
Loading