Compare commits
6 Commits
ffbf3cd42f
...
5eddf36121
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5eddf36121 | ||
|
|
ec965cd59d | ||
|
|
79c21955e0 | ||
|
|
16e4b63ffd | ||
|
|
3dd82c2bd4 | ||
|
|
f45cc1839e |
127
actions/admin/feedback.ts
Normal file
127
actions/admin/feedback.ts
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { revalidatePath } from "next/cache";
|
||||||
|
import { auth } from "@/lib/auth";
|
||||||
|
import { prisma } from "@/services/database";
|
||||||
|
import { Role } from "@/prisma/generated/prisma/client";
|
||||||
|
import { NotFoundError } from "@/services/errors";
|
||||||
|
|
||||||
|
function checkAdminAccess() {
|
||||||
|
return async () => {
|
||||||
|
const session = await auth();
|
||||||
|
if (!session?.user || session.user.role !== Role.ADMIN) {
|
||||||
|
throw new Error("Accès refusé");
|
||||||
|
}
|
||||||
|
return session;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addFeedbackBonusPoints(
|
||||||
|
userId: string,
|
||||||
|
points: number
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
await checkAdminAccess()();
|
||||||
|
|
||||||
|
// Vérifier que l'utilisateur existe
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: userId },
|
||||||
|
select: { id: true, score: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundError("Utilisateur");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ajouter les points
|
||||||
|
const updatedUser = await prisma.user.update({
|
||||||
|
where: { id: userId },
|
||||||
|
data: {
|
||||||
|
score: {
|
||||||
|
increment: points,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
score: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
revalidatePath("/admin");
|
||||||
|
revalidatePath("/leaderboard");
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `${points} points ajoutés avec succès`,
|
||||||
|
data: updatedUser,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error adding bonus points:", error);
|
||||||
|
|
||||||
|
if (error instanceof NotFoundError) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
if (error instanceof Error && error.message === "Accès refusé") {
|
||||||
|
return { success: false, error: "Accès refusé" };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: "Erreur lors de l'ajout des points",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function markFeedbackAsRead(feedbackId: string, isRead: boolean) {
|
||||||
|
try {
|
||||||
|
await checkAdminAccess()();
|
||||||
|
|
||||||
|
// Vérifier que le feedback existe
|
||||||
|
const feedback = await prisma.eventFeedback.findUnique({
|
||||||
|
where: { id: feedbackId },
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!feedback) {
|
||||||
|
throw new NotFoundError("Feedback");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour le statut
|
||||||
|
const updatedFeedback = await prisma.eventFeedback.update({
|
||||||
|
where: { id: feedbackId },
|
||||||
|
data: {
|
||||||
|
isRead,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
isRead: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
revalidatePath("/admin");
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: isRead
|
||||||
|
? "Feedback marqué comme lu"
|
||||||
|
: "Feedback marqué comme non lu",
|
||||||
|
data: updatedFeedback,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error marking feedback as read:", error);
|
||||||
|
|
||||||
|
if (error instanceof NotFoundError) {
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
if (error instanceof Error && error.message === "Accès refusé") {
|
||||||
|
return { success: false, error: "Accès refusé" };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: "Erreur lors de la mise à jour du feedback",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -20,6 +20,8 @@ export async function updateSitePreferences(data: {
|
|||||||
eventsBackground?: string | null;
|
eventsBackground?: string | null;
|
||||||
leaderboardBackground?: string | null;
|
leaderboardBackground?: string | null;
|
||||||
challengesBackground?: string | null;
|
challengesBackground?: string | null;
|
||||||
|
eventRegistrationPoints?: number;
|
||||||
|
eventFeedbackPoints?: number;
|
||||||
}) {
|
}) {
|
||||||
try {
|
try {
|
||||||
await checkAdminAccess()();
|
await checkAdminAccess()();
|
||||||
@@ -29,6 +31,8 @@ export async function updateSitePreferences(data: {
|
|||||||
eventsBackground: data.eventsBackground,
|
eventsBackground: data.eventsBackground,
|
||||||
leaderboardBackground: data.leaderboardBackground,
|
leaderboardBackground: data.leaderboardBackground,
|
||||||
challengesBackground: data.challengesBackground,
|
challengesBackground: data.challengesBackground,
|
||||||
|
eventRegistrationPoints: data.eventRegistrationPoints,
|
||||||
|
eventFeedbackPoints: data.eventFeedbackPoints,
|
||||||
});
|
});
|
||||||
|
|
||||||
revalidatePath("/admin");
|
revalidatePath("/admin");
|
||||||
|
|||||||
31
app/api/admin/events/[id]/registrations/route.ts
Normal file
31
app/api/admin/events/[id]/registrations/route.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { auth } from "@/lib/auth";
|
||||||
|
import { eventRegistrationService } from "@/services/events/event-registration.service";
|
||||||
|
import { Role } from "@/prisma/generated/prisma/client";
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
request: Request,
|
||||||
|
{ params }: { params: Promise<{ id: string }> }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const session = await auth();
|
||||||
|
|
||||||
|
if (!session?.user || session.user.role !== Role.ADMIN) {
|
||||||
|
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id: eventId } = await params;
|
||||||
|
const registrations = await eventRegistrationService.getEventRegistrations(
|
||||||
|
eventId
|
||||||
|
);
|
||||||
|
|
||||||
|
return NextResponse.json(registrations);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching event registrations:", error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Erreur lors de la récupération des inscrits" },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -128,6 +128,9 @@ export default function FeedbackPageClient({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rafraîchir le score dans le header
|
||||||
|
window.dispatchEvent(new Event("refreshUserScore"));
|
||||||
|
|
||||||
// Rediriger après 2 secondes
|
// Rediriger après 2 secondes
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
router.push("/events");
|
router.push("/events");
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import EventManagement from "@/components/admin/EventManagement";
|
|||||||
import FeedbackManagement from "@/components/admin/FeedbackManagement";
|
import FeedbackManagement from "@/components/admin/FeedbackManagement";
|
||||||
import ChallengeManagement from "@/components/admin/ChallengeManagement";
|
import ChallengeManagement from "@/components/admin/ChallengeManagement";
|
||||||
import BackgroundPreferences from "@/components/admin/BackgroundPreferences";
|
import BackgroundPreferences from "@/components/admin/BackgroundPreferences";
|
||||||
|
import EventPointsPreferences from "@/components/admin/EventPointsPreferences";
|
||||||
|
import EventFeedbackPointsPreferences from "@/components/admin/EventFeedbackPointsPreferences";
|
||||||
import { Button, Card, SectionTitle } from "@/components/ui";
|
import { Button, Card, SectionTitle } from "@/components/ui";
|
||||||
|
|
||||||
interface SitePreferences {
|
interface SitePreferences {
|
||||||
@@ -14,6 +16,8 @@ interface SitePreferences {
|
|||||||
eventsBackground: string | null;
|
eventsBackground: string | null;
|
||||||
leaderboardBackground: string | null;
|
leaderboardBackground: string | null;
|
||||||
challengesBackground: string | null;
|
challengesBackground: string | null;
|
||||||
|
eventRegistrationPoints: number;
|
||||||
|
eventFeedbackPoints: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AdminPanelProps {
|
interface AdminPanelProps {
|
||||||
@@ -93,6 +97,8 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<BackgroundPreferences initialPreferences={initialPreferences} />
|
<BackgroundPreferences initialPreferences={initialPreferences} />
|
||||||
|
<EventPointsPreferences initialPreferences={initialPreferences} />
|
||||||
|
<EventFeedbackPointsPreferences initialPreferences={initialPreferences} />
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ interface SitePreferences {
|
|||||||
eventsBackground: string | null;
|
eventsBackground: string | null;
|
||||||
leaderboardBackground: string | null;
|
leaderboardBackground: string | null;
|
||||||
challengesBackground: string | null;
|
challengesBackground: string | null;
|
||||||
|
eventRegistrationPoints?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BackgroundPreferencesProps {
|
interface BackgroundPreferencesProps {
|
||||||
|
|||||||
@@ -9,7 +9,15 @@ import {
|
|||||||
adminCancelChallenge,
|
adminCancelChallenge,
|
||||||
reactivateChallenge,
|
reactivateChallenge,
|
||||||
} from "@/actions/admin/challenges";
|
} from "@/actions/admin/challenges";
|
||||||
import { Button, Card, Input, Textarea, Alert } from "@/components/ui";
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Input,
|
||||||
|
Textarea,
|
||||||
|
Alert,
|
||||||
|
Modal,
|
||||||
|
CloseButton,
|
||||||
|
} from "@/components/ui";
|
||||||
import { Avatar } from "@/components/ui";
|
import { Avatar } from "@/components/ui";
|
||||||
|
|
||||||
interface Challenge {
|
interface Challenge {
|
||||||
@@ -417,23 +425,29 @@ export default function ChallengeManagement() {
|
|||||||
|
|
||||||
{/* Modal de validation */}
|
{/* Modal de validation */}
|
||||||
{selectedChallenge && (
|
{selectedChallenge && (
|
||||||
<div
|
<Modal
|
||||||
className="fixed inset-0 z-[200] flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm"
|
isOpen={!!selectedChallenge}
|
||||||
|
onClose={() => {
|
||||||
|
setSelectedChallenge(null);
|
||||||
|
setWinnerId("");
|
||||||
|
setAdminComment("");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
>
|
||||||
|
<div className="p-6">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h2 className="text-2xl font-bold text-pixel-gold">
|
||||||
|
Désigner le gagnant
|
||||||
|
</h2>
|
||||||
|
<CloseButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedChallenge(null);
|
setSelectedChallenge(null);
|
||||||
setWinnerId("");
|
setWinnerId("");
|
||||||
setAdminComment("");
|
setAdminComment("");
|
||||||
}}
|
}}
|
||||||
>
|
size="lg"
|
||||||
<Card
|
/>
|
||||||
variant="dark"
|
</div>
|
||||||
className="max-w-2xl w-full max-h-[90vh] overflow-y-auto"
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
<div className="p-6">
|
|
||||||
<h2 className="text-2xl font-bold text-pixel-gold mb-4">
|
|
||||||
Désigner le gagnant
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h3 className="text-lg font-bold text-gray-300 mb-2">
|
<h3 className="text-lg font-bold text-gray-300 mb-2">
|
||||||
@@ -545,30 +559,36 @@ export default function ChallengeManagement() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Modal>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Modal d'édition */}
|
{/* Modal d'édition */}
|
||||||
{editingChallenge && (
|
{editingChallenge && (
|
||||||
<div
|
<Modal
|
||||||
className="fixed inset-0 z-[200] flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm"
|
isOpen={!!editingChallenge}
|
||||||
|
onClose={() => {
|
||||||
|
setEditingChallenge(null);
|
||||||
|
setEditTitle("");
|
||||||
|
setEditDescription("");
|
||||||
|
setEditPointsReward(0);
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
>
|
||||||
|
<div className="p-6">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h2 className="text-2xl font-bold text-pixel-gold">
|
||||||
|
Modifier le défi
|
||||||
|
</h2>
|
||||||
|
<CloseButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditingChallenge(null);
|
setEditingChallenge(null);
|
||||||
setEditTitle("");
|
setEditTitle("");
|
||||||
setEditDescription("");
|
setEditDescription("");
|
||||||
setEditPointsReward(0);
|
setEditPointsReward(0);
|
||||||
}}
|
}}
|
||||||
>
|
size="lg"
|
||||||
<Card
|
/>
|
||||||
variant="dark"
|
</div>
|
||||||
className="max-w-2xl w-full max-h-[90vh] overflow-y-auto"
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
<div className="p-6">
|
|
||||||
<h2 className="text-2xl font-bold text-pixel-gold mb-4">
|
|
||||||
Modifier le défi
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Input
|
<Input
|
||||||
@@ -632,8 +652,7 @@ export default function ChallengeManagement() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Modal>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
166
components/admin/EventFeedbackPointsPreferences.tsx
Normal file
166
components/admin/EventFeedbackPointsPreferences.tsx
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { updateSitePreferences } from "@/actions/admin/preferences";
|
||||||
|
import { Button, Card, Input } from "@/components/ui";
|
||||||
|
|
||||||
|
interface SitePreferences {
|
||||||
|
id: string;
|
||||||
|
eventFeedbackPoints: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventFeedbackPointsPreferencesProps {
|
||||||
|
initialPreferences: SitePreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EventFeedbackPointsPreferences({
|
||||||
|
initialPreferences,
|
||||||
|
}: EventFeedbackPointsPreferencesProps) {
|
||||||
|
const [preferences, setPreferences] = useState<SitePreferences | null>(
|
||||||
|
initialPreferences
|
||||||
|
);
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
eventFeedbackPoints: initialPreferences.eventFeedbackPoints.toString(),
|
||||||
|
});
|
||||||
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
|
||||||
|
// Synchroniser les préférences quand initialPreferences change
|
||||||
|
useEffect(() => {
|
||||||
|
setPreferences(initialPreferences);
|
||||||
|
setFormData({
|
||||||
|
eventFeedbackPoints: initialPreferences.eventFeedbackPoints.toString(),
|
||||||
|
});
|
||||||
|
}, [initialPreferences]);
|
||||||
|
|
||||||
|
const handleEdit = () => {
|
||||||
|
setIsEditing(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = async () => {
|
||||||
|
const points = parseInt(formData.eventFeedbackPoints, 10);
|
||||||
|
|
||||||
|
if (isNaN(points) || points < 0) {
|
||||||
|
alert("Le nombre de points doit être un nombre positif");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSaving(true);
|
||||||
|
try {
|
||||||
|
const result = await updateSitePreferences({
|
||||||
|
eventFeedbackPoints: points,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.success && result.data) {
|
||||||
|
setPreferences(result.data);
|
||||||
|
setFormData({
|
||||||
|
eventFeedbackPoints: result.data.eventFeedbackPoints.toString(),
|
||||||
|
});
|
||||||
|
setIsEditing(false);
|
||||||
|
} else {
|
||||||
|
console.error("Error updating preferences:", result.error);
|
||||||
|
alert(result.error || "Erreur lors de la mise à jour");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating preferences:", error);
|
||||||
|
alert("Erreur lors de la mise à jour");
|
||||||
|
} finally {
|
||||||
|
setIsSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setIsEditing(false);
|
||||||
|
if (preferences) {
|
||||||
|
setFormData({
|
||||||
|
eventFeedbackPoints: preferences.eventFeedbackPoints.toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card variant="default" className="p-3 sm:p-4">
|
||||||
|
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-3 mb-4">
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<h3 className="text-pixel-gold font-bold text-base sm:text-lg break-words">
|
||||||
|
Points de feedback sur les événements
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-400 text-xs sm:text-sm">
|
||||||
|
Nombre de points attribués lorsqu'un utilisateur donne un feedback à un événement (première fois uniquement)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{!isEditing && (
|
||||||
|
<Button
|
||||||
|
onClick={handleEdit}
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
className="whitespace-nowrap flex-shrink-0"
|
||||||
|
>
|
||||||
|
Modifier
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isEditing ? (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
htmlFor="eventFeedbackPoints"
|
||||||
|
className="block text-sm font-medium text-pixel-gold mb-2"
|
||||||
|
>
|
||||||
|
Points de feedback
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
id="eventFeedbackPoints"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
value={formData.eventFeedbackPoints}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
eventFeedbackPoints: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="100"
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-gray-400 mt-1">
|
||||||
|
Les utilisateurs gagneront ce nombre de points lors de leur premier feedback sur un événement
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col sm:flex-row gap-2 pt-4">
|
||||||
|
<Button
|
||||||
|
onClick={handleSave}
|
||||||
|
variant="success"
|
||||||
|
size="md"
|
||||||
|
disabled={isSaving}
|
||||||
|
>
|
||||||
|
{isSaving ? "Enregistrement..." : "Enregistrer"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleCancel}
|
||||||
|
variant="secondary"
|
||||||
|
size="md"
|
||||||
|
disabled={isSaving}
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
||||||
|
<span className="text-pixel-gold font-bold text-sm sm:text-base min-w-0 sm:min-w-[200px] flex-shrink-0">
|
||||||
|
Points actuels:
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-lg sm:text-xl font-bold text-white">
|
||||||
|
{preferences?.eventFeedbackPoints ?? 100}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs sm:text-sm text-gray-400">points</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3,7 +3,17 @@
|
|||||||
import { useState, useEffect, useTransition } from "react";
|
import { useState, useEffect, useTransition } from "react";
|
||||||
import { calculateEventStatus } from "@/lib/eventStatus";
|
import { calculateEventStatus } from "@/lib/eventStatus";
|
||||||
import { createEvent, updateEvent, deleteEvent } from "@/actions/admin/events";
|
import { createEvent, updateEvent, deleteEvent } from "@/actions/admin/events";
|
||||||
import { Input, Textarea, Button, Card, Badge } from "@/components/ui";
|
import {
|
||||||
|
Input,
|
||||||
|
Textarea,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Badge,
|
||||||
|
Modal,
|
||||||
|
CloseButton,
|
||||||
|
Avatar,
|
||||||
|
} from "@/components/ui";
|
||||||
|
import { updateUser } from "@/actions/admin/users";
|
||||||
|
|
||||||
interface Event {
|
interface Event {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -20,6 +30,24 @@ interface Event {
|
|||||||
registrationsCount?: number;
|
registrationsCount?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface EventRegistration {
|
||||||
|
id: string;
|
||||||
|
userId: string;
|
||||||
|
eventId: string;
|
||||||
|
createdAt: string;
|
||||||
|
user: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
avatar: string | null;
|
||||||
|
score: number;
|
||||||
|
level: number;
|
||||||
|
hp: number;
|
||||||
|
maxHp: number;
|
||||||
|
xp: number;
|
||||||
|
maxXp: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface EventFormData {
|
interface EventFormData {
|
||||||
date: string;
|
date: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -70,6 +98,14 @@ export default function EventManagement() {
|
|||||||
const [editingEvent, setEditingEvent] = useState<Event | null>(null);
|
const [editingEvent, setEditingEvent] = useState<Event | null>(null);
|
||||||
const [isCreating, setIsCreating] = useState(false);
|
const [isCreating, setIsCreating] = useState(false);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
const [viewingRegistrations, setViewingRegistrations] =
|
||||||
|
useState<Event | null>(null);
|
||||||
|
const [registrations, setRegistrations] = useState<EventRegistration[]>([]);
|
||||||
|
const [loadingRegistrations, setLoadingRegistrations] = useState(false);
|
||||||
|
const [editingScores, setEditingScores] = useState<Record<string, number>>(
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
const [savingScore, setSavingScore] = useState<string | null>(null);
|
||||||
const [formData, setFormData] = useState<EventFormData>({
|
const [formData, setFormData] = useState<EventFormData>({
|
||||||
date: "",
|
date: "",
|
||||||
name: "",
|
name: "",
|
||||||
@@ -199,6 +235,75 @@ export default function EventManagement() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleViewRegistrations = async (event: Event) => {
|
||||||
|
setViewingRegistrations(event);
|
||||||
|
setLoadingRegistrations(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/admin/events/${event.id}/registrations`
|
||||||
|
);
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
setRegistrations(data);
|
||||||
|
// Initialiser les scores d'édition avec les scores actuels
|
||||||
|
const scoresMap: Record<string, number> = {};
|
||||||
|
data.forEach((reg: EventRegistration) => {
|
||||||
|
scoresMap[reg.user.id] = reg.user.score;
|
||||||
|
});
|
||||||
|
setEditingScores(scoresMap);
|
||||||
|
} else {
|
||||||
|
alert("Erreur lors de la récupération des inscrits");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching registrations:", error);
|
||||||
|
alert("Erreur lors de la récupération des inscrits");
|
||||||
|
} finally {
|
||||||
|
setLoadingRegistrations(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseRegistrations = () => {
|
||||||
|
setViewingRegistrations(null);
|
||||||
|
setRegistrations([]);
|
||||||
|
setEditingScores({});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleScoreChange = (userId: string, newScore: number) => {
|
||||||
|
setEditingScores({
|
||||||
|
...editingScores,
|
||||||
|
[userId]: newScore,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveScore = async (userId: string) => {
|
||||||
|
const newScore = editingScores[userId];
|
||||||
|
if (newScore === undefined) return;
|
||||||
|
|
||||||
|
setSavingScore(userId);
|
||||||
|
startTransition(async () => {
|
||||||
|
try {
|
||||||
|
const result = await updateUser(userId, { score: newScore });
|
||||||
|
if (result.success) {
|
||||||
|
// Mettre à jour le score dans la liste locale
|
||||||
|
setRegistrations((prev) =>
|
||||||
|
prev.map((reg) =>
|
||||||
|
reg.user.id === userId
|
||||||
|
? { ...reg, user: { ...reg.user, score: newScore } }
|
||||||
|
: reg
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
alert(result.error || "Erreur lors de la mise à jour du score");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating score:", error);
|
||||||
|
alert("Erreur lors de la mise à jour du score");
|
||||||
|
} finally {
|
||||||
|
setSavingScore(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <div className="text-center text-gray-400 py-8">Chargement...</div>;
|
return <div className="text-center text-gray-400 py-8">Chargement...</div>;
|
||||||
}
|
}
|
||||||
@@ -221,11 +326,20 @@ export default function EventManagement() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Modal de création/édition */}
|
||||||
{(isCreating || editingEvent) && (
|
{(isCreating || editingEvent) && (
|
||||||
<Card variant="default" className="p-3 sm:p-4 mb-4">
|
<Modal
|
||||||
<h4 className="text-pixel-gold font-bold mb-4 text-base sm:text-lg break-words">
|
isOpen={isCreating || !!editingEvent}
|
||||||
|
onClose={handleCancel}
|
||||||
|
size="lg"
|
||||||
|
>
|
||||||
|
<div className="p-6">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h4 className="text-pixel-gold font-bold text-base sm:text-lg break-words">
|
||||||
{isCreating ? "Créer un événement" : "Modifier l'événement"}
|
{isCreating ? "Créer un événement" : "Modifier l'événement"}
|
||||||
</h4>
|
</h4>
|
||||||
|
<CloseButton onClick={handleCancel} size="lg" />
|
||||||
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Input
|
<Input
|
||||||
type="date"
|
type="date"
|
||||||
@@ -330,7 +444,8 @@ export default function EventManagement() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
</Modal>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{events.length === 0 ? (
|
{events.length === 0 ? (
|
||||||
@@ -392,7 +507,15 @@ export default function EventManagement() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!isCreating && !editingEvent && (
|
{!isCreating && !editingEvent && (
|
||||||
<div className="flex gap-2 sm:ml-4 flex-shrink-0">
|
<div className="flex gap-2 sm:ml-4 flex-shrink-0 flex-wrap">
|
||||||
|
<Button
|
||||||
|
onClick={() => handleViewRegistrations(event)}
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
className="whitespace-nowrap"
|
||||||
|
>
|
||||||
|
Inscrits ({event.registrationsCount || 0})
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleEdit(event)}
|
onClick={() => handleEdit(event)}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@@ -417,6 +540,116 @@ export default function EventManagement() {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Modal des inscrits */}
|
||||||
|
{viewingRegistrations && (
|
||||||
|
<Modal
|
||||||
|
isOpen={!!viewingRegistrations}
|
||||||
|
onClose={handleCloseRegistrations}
|
||||||
|
size="lg"
|
||||||
|
>
|
||||||
|
<div className="p-6">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h4 className="text-pixel-gold font-bold text-base sm:text-lg break-words">
|
||||||
|
Inscrits à "{viewingRegistrations.name}"
|
||||||
|
</h4>
|
||||||
|
<CloseButton onClick={handleCloseRegistrations} size="lg" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{loadingRegistrations ? (
|
||||||
|
<div className="text-center text-gray-400 py-8">
|
||||||
|
Chargement...
|
||||||
|
</div>
|
||||||
|
) : registrations.length === 0 ? (
|
||||||
|
<div className="text-center text-gray-400 py-8">
|
||||||
|
Aucun inscrit pour cet événement
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-3 max-h-[60vh] overflow-y-auto">
|
||||||
|
{registrations.map((registration) => {
|
||||||
|
const user = registration.user;
|
||||||
|
const currentScore = editingScores[user.id] ?? user.score;
|
||||||
|
const isSaving = savingScore === user.id;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
key={registration.id}
|
||||||
|
variant="default"
|
||||||
|
className="p-3 sm:p-4"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col sm:flex-row sm:items-center gap-3">
|
||||||
|
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||||
|
<Avatar
|
||||||
|
src={user.avatar}
|
||||||
|
username={user.username}
|
||||||
|
size="md"
|
||||||
|
borderClassName="border-2 border-pixel-gold/50"
|
||||||
|
/>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h5 className="text-pixel-gold font-bold text-sm sm:text-base break-words">
|
||||||
|
{user.username}
|
||||||
|
</h5>
|
||||||
|
<p className="text-gray-400 text-xs sm:text-sm">
|
||||||
|
Niveau {user.level} • HP: {user.hp}/{user.maxHp} •
|
||||||
|
XP: {user.xp}/{user.maxXp}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-2 sm:gap-3 flex-shrink-0">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<label className="text-xs sm:text-sm text-gray-300 whitespace-nowrap">
|
||||||
|
Score:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={currentScore}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleScoreChange(
|
||||||
|
user.id,
|
||||||
|
parseInt(e.target.value) || 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
disabled={isSaving}
|
||||||
|
className="w-24 px-2 sm:px-3 py-1 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm text-center disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-1 sm:gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
handleScoreChange(user.id, currentScore - 100)
|
||||||
|
}
|
||||||
|
disabled={isSaving}
|
||||||
|
className="px-2 sm:px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-[10px] sm:text-xs rounded hover:bg-red-900/30 transition flex-shrink-0 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
-100
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
handleScoreChange(user.id, currentScore + 100)
|
||||||
|
}
|
||||||
|
disabled={isSaving}
|
||||||
|
className="px-2 sm:px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-[10px] sm:text-xs rounded hover:bg-green-900/30 transition flex-shrink-0 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
+100
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleSaveScore(user.id)}
|
||||||
|
disabled={isSaving || currentScore === user.score}
|
||||||
|
className="px-2 sm:px-3 py-1 border border-pixel-gold/50 bg-pixel-gold/20 text-pixel-gold text-[10px] sm:text-xs rounded hover:bg-pixel-gold/30 transition flex-shrink-0 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
{isSaving ? "..." : "Sauver"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
166
components/admin/EventPointsPreferences.tsx
Normal file
166
components/admin/EventPointsPreferences.tsx
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { updateSitePreferences } from "@/actions/admin/preferences";
|
||||||
|
import { Button, Card, Input } from "@/components/ui";
|
||||||
|
|
||||||
|
interface SitePreferences {
|
||||||
|
id: string;
|
||||||
|
eventRegistrationPoints: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventPointsPreferencesProps {
|
||||||
|
initialPreferences: SitePreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EventPointsPreferences({
|
||||||
|
initialPreferences,
|
||||||
|
}: EventPointsPreferencesProps) {
|
||||||
|
const [preferences, setPreferences] = useState<SitePreferences | null>(
|
||||||
|
initialPreferences
|
||||||
|
);
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
eventRegistrationPoints: initialPreferences.eventRegistrationPoints.toString(),
|
||||||
|
});
|
||||||
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
|
||||||
|
// Synchroniser les préférences quand initialPreferences change
|
||||||
|
useEffect(() => {
|
||||||
|
setPreferences(initialPreferences);
|
||||||
|
setFormData({
|
||||||
|
eventRegistrationPoints: initialPreferences.eventRegistrationPoints.toString(),
|
||||||
|
});
|
||||||
|
}, [initialPreferences]);
|
||||||
|
|
||||||
|
const handleEdit = () => {
|
||||||
|
setIsEditing(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = async () => {
|
||||||
|
const points = parseInt(formData.eventRegistrationPoints, 10);
|
||||||
|
|
||||||
|
if (isNaN(points) || points < 0) {
|
||||||
|
alert("Le nombre de points doit être un nombre positif");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSaving(true);
|
||||||
|
try {
|
||||||
|
const result = await updateSitePreferences({
|
||||||
|
eventRegistrationPoints: points,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.success && result.data) {
|
||||||
|
setPreferences(result.data);
|
||||||
|
setFormData({
|
||||||
|
eventRegistrationPoints: result.data.eventRegistrationPoints.toString(),
|
||||||
|
});
|
||||||
|
setIsEditing(false);
|
||||||
|
} else {
|
||||||
|
console.error("Error updating preferences:", result.error);
|
||||||
|
alert(result.error || "Erreur lors de la mise à jour");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating preferences:", error);
|
||||||
|
alert("Erreur lors de la mise à jour");
|
||||||
|
} finally {
|
||||||
|
setIsSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setIsEditing(false);
|
||||||
|
if (preferences) {
|
||||||
|
setFormData({
|
||||||
|
eventRegistrationPoints: preferences.eventRegistrationPoints.toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card variant="default" className="p-3 sm:p-4">
|
||||||
|
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-3 mb-4">
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<h3 className="text-pixel-gold font-bold text-base sm:text-lg break-words">
|
||||||
|
Points d'inscription aux événements
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-400 text-xs sm:text-sm">
|
||||||
|
Nombre de points attribués lorsqu'un utilisateur s'inscrit à un événement
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{!isEditing && (
|
||||||
|
<Button
|
||||||
|
onClick={handleEdit}
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
className="whitespace-nowrap flex-shrink-0"
|
||||||
|
>
|
||||||
|
Modifier
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isEditing ? (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
htmlFor="eventRegistrationPoints"
|
||||||
|
className="block text-sm font-medium text-pixel-gold mb-2"
|
||||||
|
>
|
||||||
|
Points d'inscription
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
id="eventRegistrationPoints"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
value={formData.eventRegistrationPoints}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
eventRegistrationPoints: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder="100"
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-gray-400 mt-1">
|
||||||
|
Les utilisateurs gagneront ce nombre de points lors de leur inscription à un événement
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col sm:flex-row gap-2 pt-4">
|
||||||
|
<Button
|
||||||
|
onClick={handleSave}
|
||||||
|
variant="success"
|
||||||
|
size="md"
|
||||||
|
disabled={isSaving}
|
||||||
|
>
|
||||||
|
{isSaving ? "Enregistrement..." : "Enregistrer"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleCancel}
|
||||||
|
variant="secondary"
|
||||||
|
size="md"
|
||||||
|
disabled={isSaving}
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
||||||
|
<span className="text-pixel-gold font-bold text-sm sm:text-base min-w-0 sm:min-w-[200px] flex-shrink-0">
|
||||||
|
Points actuels:
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-lg sm:text-xl font-bold text-white">
|
||||||
|
{preferences?.eventRegistrationPoints ?? 100}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs sm:text-sm text-gray-400">points</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,11 +1,18 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
import {
|
||||||
|
addFeedbackBonusPoints,
|
||||||
|
markFeedbackAsRead,
|
||||||
|
} from "@/actions/admin/feedback";
|
||||||
|
import { Button } from "@/components/ui";
|
||||||
|
import Avatar from "@/components/ui/Avatar";
|
||||||
|
|
||||||
interface Feedback {
|
interface Feedback {
|
||||||
id: string;
|
id: string;
|
||||||
rating: number;
|
rating: number;
|
||||||
comment: string | null;
|
comment: string | null;
|
||||||
|
isRead: boolean;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
event: {
|
event: {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -17,6 +24,8 @@ interface Feedback {
|
|||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
avatar: string | null;
|
||||||
|
score: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,6 +44,10 @@ export default function FeedbackManagement() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [selectedEvent, setSelectedEvent] = useState<string | null>(null);
|
const [selectedEvent, setSelectedEvent] = useState<string | null>(null);
|
||||||
|
const [addingPoints, setAddingPoints] = useState<Record<string, boolean>>(
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
const [markingRead, setMarkingRead] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchFeedbacks();
|
fetchFeedbacks();
|
||||||
@@ -92,9 +105,59 @@ export default function FeedbackManagement() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredFeedbacks = selectedEvent
|
const handleAddPoints = async (userId: string, points: number) => {
|
||||||
|
const key = `${userId}-${points}`;
|
||||||
|
setAddingPoints((prev) => ({ ...prev, [key]: true }));
|
||||||
|
setError("");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await addFeedbackBonusPoints(userId, points);
|
||||||
|
if (result.success) {
|
||||||
|
// Rafraîchir les données pour voir les nouveaux scores
|
||||||
|
await fetchFeedbacks();
|
||||||
|
// Rafraîchir le score dans le header si l'utilisateur est connecté
|
||||||
|
window.dispatchEvent(new Event("refreshUserScore"));
|
||||||
|
} else {
|
||||||
|
setError(result.error || "Erreur lors de l'ajout des points");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setError("Erreur lors de l'ajout des points");
|
||||||
|
} finally {
|
||||||
|
setAddingPoints((prev) => ({ ...prev, [key]: false }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMarkAsRead = async (feedbackId: string, isRead: boolean) => {
|
||||||
|
setMarkingRead((prev) => ({ ...prev, [feedbackId]: true }));
|
||||||
|
setError("");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await markFeedbackAsRead(feedbackId, isRead);
|
||||||
|
if (result.success) {
|
||||||
|
// Rafraîchir les données pour voir le nouveau statut
|
||||||
|
await fetchFeedbacks();
|
||||||
|
} else {
|
||||||
|
setError(result.error || "Erreur lors de la mise à jour");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setError("Erreur lors de la mise à jour");
|
||||||
|
} finally {
|
||||||
|
setMarkingRead((prev) => ({ ...prev, [feedbackId]: false }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredFeedbacks = (selectedEvent
|
||||||
? feedbacks.filter((f) => f.event.id === selectedEvent)
|
? feedbacks.filter((f) => f.event.id === selectedEvent)
|
||||||
: feedbacks;
|
: feedbacks
|
||||||
|
).sort((a, b) => {
|
||||||
|
// Trier : non lus en premier, puis par date décroissante
|
||||||
|
if (a.isRead !== b.isRead) {
|
||||||
|
return a.isRead ? 1 : -1;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
@@ -184,21 +247,46 @@ export default function FeedbackManagement() {
|
|||||||
{filteredFeedbacks.map((feedback) => (
|
{filteredFeedbacks.map((feedback) => (
|
||||||
<div
|
<div
|
||||||
key={feedback.id}
|
key={feedback.id}
|
||||||
className="bg-black/40 border border-pixel-gold/20 rounded p-3 sm:p-4"
|
className={`bg-black/40 border rounded p-3 sm:p-4 ${
|
||||||
|
feedback.isRead
|
||||||
|
? "border-pixel-gold/20 opacity-75"
|
||||||
|
: "border-pixel-gold/50 bg-pixel-gold/5"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3 mb-3">
|
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3 mb-3">
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-3 mb-2">
|
{/* En-tête utilisateur avec avatar */}
|
||||||
|
<div className="flex items-center gap-2 sm:gap-3 mb-3">
|
||||||
|
<Avatar
|
||||||
|
src={feedback.user.avatar}
|
||||||
|
username={feedback.user.username}
|
||||||
|
size="md"
|
||||||
|
borderClassName="border-pixel-gold/30"
|
||||||
|
/>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 mb-1">
|
||||||
<h4 className="text-white font-semibold text-sm sm:text-base break-words">
|
<h4 className="text-white font-semibold text-sm sm:text-base break-words">
|
||||||
{feedback.user.username}
|
{feedback.user.username}
|
||||||
</h4>
|
</h4>
|
||||||
|
<span className="text-pixel-gold font-bold text-xs sm:text-sm">
|
||||||
|
{feedback.user.score.toLocaleString("fr-FR")} pts
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<span className="text-gray-500 text-[10px] sm:text-xs break-all">
|
<span className="text-gray-500 text-[10px] sm:text-xs break-all">
|
||||||
{feedback.user.email}
|
{feedback.user.email}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-pixel-gold text-xs sm:text-sm font-semibold mb-2 break-words">
|
</div>
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<div className="text-pixel-gold text-xs sm:text-sm font-semibold break-words">
|
||||||
{feedback.event.name}
|
{feedback.event.name}
|
||||||
</div>
|
</div>
|
||||||
|
{!feedback.isRead && (
|
||||||
|
<span className="bg-pixel-gold/20 text-pixel-gold text-[10px] px-1.5 py-0.5 rounded uppercase font-semibold">
|
||||||
|
Non lu
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="text-gray-500 text-[10px] sm:text-xs mb-2">
|
<div className="text-gray-500 text-[10px] sm:text-xs mb-2">
|
||||||
{new Date(feedback.createdAt).toLocaleDateString(
|
{new Date(feedback.createdAt).toLocaleDateString(
|
||||||
"fr-FR",
|
"fr-FR",
|
||||||
@@ -212,8 +300,23 @@ export default function FeedbackManagement() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-shrink-0">
|
<div className="flex flex-col items-end gap-2 flex-shrink-0">
|
||||||
{renderStars(feedback.rating)}
|
{renderStars(feedback.rating)}
|
||||||
|
<Button
|
||||||
|
variant={feedback.isRead ? "secondary" : "success"}
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
handleMarkAsRead(feedback.id, !feedback.isRead)
|
||||||
|
}
|
||||||
|
disabled={markingRead[feedback.id]}
|
||||||
|
className="text-xs whitespace-nowrap"
|
||||||
|
>
|
||||||
|
{markingRead[feedback.id]
|
||||||
|
? "..."
|
||||||
|
: feedback.isRead
|
||||||
|
? "Marquer non lu"
|
||||||
|
: "Marquer lu"}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{feedback.comment && (
|
{feedback.comment && (
|
||||||
@@ -223,6 +326,39 @@ export default function FeedbackManagement() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{/* Boutons pour ajouter des points bonus */}
|
||||||
|
<div className="mt-3 pt-3 border-t border-pixel-gold/20 flex flex-wrap gap-2">
|
||||||
|
<span className="text-gray-400 text-xs sm:text-sm mr-2">
|
||||||
|
Points bonus:
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleAddPoints(feedback.user.id, 10)}
|
||||||
|
disabled={addingPoints[`${feedback.user.id}-10`]}
|
||||||
|
className="text-xs"
|
||||||
|
>
|
||||||
|
{addingPoints[`${feedback.user.id}-10`] ? "..." : "+10"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleAddPoints(feedback.user.id, 100)}
|
||||||
|
disabled={addingPoints[`${feedback.user.id}-100`]}
|
||||||
|
className="text-xs"
|
||||||
|
>
|
||||||
|
{addingPoints[`${feedback.user.id}-100`] ? "..." : "+100"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleAddPoints(feedback.user.id, 1000)}
|
||||||
|
disabled={addingPoints[`${feedback.user.id}-1000`]}
|
||||||
|
className="text-xs"
|
||||||
|
>
|
||||||
|
{addingPoints[`${feedback.user.id}-1000`] ? "..." : "+1000"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, useTransition } from "react";
|
import { useState, useEffect, useTransition } from "react";
|
||||||
import { Avatar, Input, Button, Card } from "@/components/ui";
|
import {
|
||||||
|
Avatar,
|
||||||
|
Input,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Modal,
|
||||||
|
CloseButton,
|
||||||
|
} from "@/components/ui";
|
||||||
import { updateUser, deleteUser } from "@/actions/admin/users";
|
import { updateUser, deleteUser } from "@/actions/admin/users";
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
@@ -159,6 +166,25 @@ export default function UserManagement() {
|
|||||||
return num.toLocaleString("en-US");
|
return num.toLocaleString("en-US");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Trouver l'utilisateur en cours d'édition pour les previews
|
||||||
|
const currentEditingUserData = editingUser
|
||||||
|
? users.find((u) => u.id === editingUser.userId)
|
||||||
|
: null;
|
||||||
|
const previewHp =
|
||||||
|
currentEditingUserData && editingUser
|
||||||
|
? Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(
|
||||||
|
currentEditingUserData.maxHp,
|
||||||
|
currentEditingUserData.hp + editingUser.hpDelta
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: 0;
|
||||||
|
const previewXp =
|
||||||
|
currentEditingUserData && editingUser
|
||||||
|
? Math.max(0, currentEditingUserData.xp + editingUser.xpDelta)
|
||||||
|
: 0;
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <div className="text-center text-gray-400 py-8">Chargement...</div>;
|
return <div className="text-center text-gray-400 py-8">Chargement...</div>;
|
||||||
}
|
}
|
||||||
@@ -170,79 +196,129 @@ export default function UserManagement() {
|
|||||||
Aucun utilisateur trouvé
|
Aucun utilisateur trouvé
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
users.map((user) => {
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 sm:gap-4">
|
||||||
const isEditing = editingUser?.userId === user.id;
|
{users.map((user) => {
|
||||||
const previewHp = isEditing
|
|
||||||
? Math.max(0, Math.min(user.maxHp, user.hp + editingUser.hpDelta))
|
|
||||||
: user.hp;
|
|
||||||
const previewXp = isEditing
|
|
||||||
? Math.max(0, user.xp + editingUser.xpDelta)
|
|
||||||
: user.xp;
|
|
||||||
const displayAvatar = isEditing ? editingUser.avatar : user.avatar;
|
|
||||||
const displayUsername = isEditing
|
|
||||||
? editingUser.username || user.username
|
|
||||||
: user.username;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card key={user.id} variant="default" className="p-3 sm:p-4">
|
<Card key={user.id} variant="default" className="p-3">
|
||||||
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 mb-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex gap-2 sm:gap-3 items-center flex-1 min-w-0">
|
{/* Header avec avatar et nom */}
|
||||||
{/* Avatar */}
|
<div className="flex items-center gap-2">
|
||||||
<Avatar
|
<Avatar
|
||||||
src={displayAvatar}
|
src={user.avatar}
|
||||||
username={displayUsername}
|
username={user.username}
|
||||||
size="sm"
|
size="sm"
|
||||||
className="flex-shrink-0"
|
className="flex-shrink-0"
|
||||||
borderClassName="border-2 border-pixel-gold/50"
|
borderClassName="border-2 border-pixel-gold/50"
|
||||||
/>
|
/>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-1.5 sm:gap-2 flex-wrap">
|
<h3 className="text-pixel-gold font-bold text-sm truncate">
|
||||||
<h3 className="text-pixel-gold font-bold text-sm sm:text-base break-words">
|
{user.username}
|
||||||
{displayUsername}
|
|
||||||
</h3>
|
</h3>
|
||||||
<span className="text-[10px] sm:text-xs text-gray-500 whitespace-nowrap">
|
<div className="flex items-center gap-1.5 mt-0.5">
|
||||||
Niveau {user.level}
|
<span className="text-[10px] text-gray-500">
|
||||||
</span>
|
Niv. {user.level}
|
||||||
<span className="text-[10px] sm:text-xs text-gray-500 whitespace-nowrap">
|
|
||||||
Score: {formatNumber(user.score)}
|
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`text-[10px] sm:text-xs whitespace-nowrap ${
|
className={`text-[10px] font-bold px-1.5 py-0.5 rounded border ${
|
||||||
user.role === "ADMIN"
|
user.role === "ADMIN"
|
||||||
? "text-pixel-gold"
|
? "text-pixel-gold border-pixel-gold/50 bg-pixel-gold/10"
|
||||||
: "text-gray-500"
|
: "text-gray-500 border-gray-500/30 bg-gray-500/10"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{user.role}
|
{user.role}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-400 text-[10px] sm:text-xs truncate">
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Score en évidence */}
|
||||||
|
<div className="flex items-baseline gap-1.5 px-1">
|
||||||
|
<span className="text-[10px] text-gray-400">Score:</span>
|
||||||
|
<span className="text-lg font-bold text-pixel-gold">
|
||||||
|
{formatNumber(user.score)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Stats HP et XP */}
|
||||||
|
<div className="space-y-1.5 text-[10px]">
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between items-center mb-0.5">
|
||||||
|
<span className="text-gray-400">HP</span>
|
||||||
|
<span className="text-gray-400">
|
||||||
|
{user.hp}/{user.maxHp}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-1 bg-black/60 rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full bg-gradient-to-r from-red-600 to-green-500"
|
||||||
|
style={{
|
||||||
|
width: `${Math.min(
|
||||||
|
100,
|
||||||
|
(user.hp / user.maxHp) * 100
|
||||||
|
)}%`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between items-center mb-0.5">
|
||||||
|
<span className="text-gray-400">XP</span>
|
||||||
|
<span className="text-gray-400">
|
||||||
|
{formatNumber(user.xp)}/{formatNumber(user.maxXp)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-1 bg-black/60 rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full bg-gradient-to-r from-blue-600 to-purple-500"
|
||||||
|
style={{
|
||||||
|
width: `${Math.min(
|
||||||
|
100,
|
||||||
|
(user.xp / user.maxXp) * 100
|
||||||
|
)}%`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Email */}
|
||||||
|
<p className="text-gray-400 text-[10px] truncate px-1">
|
||||||
{user.email}
|
{user.email}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
</div>
|
{/* Boutons d'action */}
|
||||||
{!isEditing && (
|
<div className="flex gap-2 pt-1">
|
||||||
<div className="flex gap-2 flex-shrink-0 sm:ml-2">
|
|
||||||
<button
|
<button
|
||||||
onClick={() => handleEdit(user)}
|
onClick={() => handleEdit(user)}
|
||||||
className="px-2 sm:px-3 py-1.5 border border-pixel-gold/50 bg-black/60 text-white uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition whitespace-nowrap"
|
className="flex-1 px-2 py-1 border border-pixel-gold/50 bg-black/60 text-white uppercase text-[10px] tracking-widest rounded hover:bg-pixel-gold/10 transition"
|
||||||
>
|
>
|
||||||
Modifier
|
Modifier
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete(user.id)}
|
onClick={() => handleDelete(user.id)}
|
||||||
disabled={deletingUserId === user.id}
|
disabled={deletingUserId === user.id}
|
||||||
className="px-2 sm:px-3 py-1.5 border border-red-500/50 bg-red-900/20 text-red-400 uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-red-900/30 transition disabled:opacity-50 whitespace-nowrap"
|
className="flex-1 px-2 py-1 border border-red-500/50 bg-red-900/20 text-red-400 uppercase text-[10px] tracking-widest rounded hover:bg-red-900/30 transition disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{deletingUserId === user.id
|
{deletingUserId === user.id ? "..." : "Suppr."}
|
||||||
? "Suppression..."
|
|
||||||
: "Supprimer"}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{isEditing ? (
|
{/* Modal d'édition */}
|
||||||
|
{editingUser && currentEditingUserData && (
|
||||||
|
<Modal isOpen={!!editingUser} onClose={handleCancel} size="lg">
|
||||||
|
<div className="p-6">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h4 className="text-pixel-gold font-bold text-base sm:text-lg break-words">
|
||||||
|
Modifier l'utilisateur
|
||||||
|
</h4>
|
||||||
|
<CloseButton onClick={handleCancel} size="lg" />
|
||||||
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Username Section */}
|
{/* Username Section */}
|
||||||
<Input
|
<Input
|
||||||
@@ -269,15 +345,15 @@ export default function UserManagement() {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Avatar
|
<Avatar
|
||||||
src={editingUser.avatar}
|
src={editingUser.avatar}
|
||||||
username={editingUser.username || user.username}
|
username={
|
||||||
|
editingUser.username || currentEditingUserData.username
|
||||||
|
}
|
||||||
size="lg"
|
size="lg"
|
||||||
borderClassName="border-2 border-pixel-gold/50"
|
borderClassName="border-2 border-pixel-gold/50"
|
||||||
/>
|
/>
|
||||||
{uploadingAvatar === user.id && (
|
{uploadingAvatar === editingUser.userId && (
|
||||||
<div className="absolute inset-0 bg-black/60 flex items-center justify-center rounded-full">
|
<div className="absolute inset-0 bg-black/60 flex items-center justify-center rounded-full">
|
||||||
<div className="text-pixel-gold text-xs">
|
<div className="text-pixel-gold text-xs">Upload...</div>
|
||||||
Upload...
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -330,7 +406,7 @@ export default function UserManagement() {
|
|||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
setUploadingAvatar(user.id);
|
setUploadingAvatar(editingUser.userId);
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file", file);
|
formData.append("file", file);
|
||||||
@@ -363,16 +439,16 @@ export default function UserManagement() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
id={`avatar-upload-${user.id}`}
|
id={`avatar-upload-${editingUser.userId}`}
|
||||||
/>
|
/>
|
||||||
<label htmlFor={`avatar-upload-${user.id}`}>
|
<label htmlFor={`avatar-upload-${editingUser.userId}`}>
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
as="span"
|
as="span"
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
>
|
>
|
||||||
{uploadingAvatar === user.id
|
{uploadingAvatar === editingUser.userId
|
||||||
? "Upload en cours..."
|
? "Upload en cours..."
|
||||||
: "Upload un avatar custom"}
|
: "Upload un avatar custom"}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -387,7 +463,7 @@ export default function UserManagement() {
|
|||||||
Points de Vie (HP)
|
Points de Vie (HP)
|
||||||
</label>
|
</label>
|
||||||
<span className="text-[10px] sm:text-xs text-gray-400">
|
<span className="text-[10px] sm:text-xs text-gray-400">
|
||||||
{previewHp} / {user.maxHp}
|
{previewHp} / {currentEditingUserData.maxHp}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1 sm:gap-2 flex-wrap">
|
<div className="flex gap-1 sm:gap-2 flex-wrap">
|
||||||
@@ -453,7 +529,7 @@ export default function UserManagement() {
|
|||||||
style={{
|
style={{
|
||||||
width: `${Math.min(
|
width: `${Math.min(
|
||||||
100,
|
100,
|
||||||
(previewHp / user.maxHp) * 100
|
(previewHp / currentEditingUserData.maxHp) * 100
|
||||||
)}%`,
|
)}%`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -467,7 +543,8 @@ export default function UserManagement() {
|
|||||||
Expérience (XP)
|
Expérience (XP)
|
||||||
</label>
|
</label>
|
||||||
<span className="text-[10px] sm:text-xs text-gray-400">
|
<span className="text-[10px] sm:text-xs text-gray-400">
|
||||||
{formatNumber(previewXp)} / {formatNumber(user.maxXp)}
|
{formatNumber(previewXp)} /{" "}
|
||||||
|
{formatNumber(currentEditingUserData.maxXp)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1 sm:gap-2 flex-wrap">
|
<div className="flex gap-1 sm:gap-2 flex-wrap">
|
||||||
@@ -533,7 +610,7 @@ export default function UserManagement() {
|
|||||||
style={{
|
style={{
|
||||||
width: `${Math.min(
|
width: `${Math.min(
|
||||||
100,
|
100,
|
||||||
(previewXp / user.maxXp) * 100
|
(previewXp / currentEditingUserData.maxXp) * 100
|
||||||
)}%`,
|
)}%`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -695,60 +772,13 @@ export default function UserManagement() {
|
|||||||
>
|
>
|
||||||
{saving ? "Enregistrement..." : "Enregistrer"}
|
{saving ? "Enregistrement..." : "Enregistrer"}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button onClick={handleCancel} variant="secondary" size="md">
|
||||||
onClick={handleCancel}
|
|
||||||
variant="secondary"
|
|
||||||
size="md"
|
|
||||||
>
|
|
||||||
Annuler
|
Annuler
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4 text-[10px] sm:text-xs">
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="flex justify-between items-center mb-0.5">
|
|
||||||
<span className="text-gray-400">HP</span>
|
|
||||||
<span className="text-gray-400">
|
|
||||||
{user.hp}/{user.maxHp}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="h-1.5 bg-black/60 rounded-full overflow-hidden">
|
</Modal>
|
||||||
<div
|
|
||||||
className="h-full bg-gradient-to-r from-red-600 to-green-500"
|
|
||||||
style={{
|
|
||||||
width: `${Math.min(
|
|
||||||
100,
|
|
||||||
(user.hp / user.maxHp) * 100
|
|
||||||
)}%`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="flex justify-between items-center mb-0.5">
|
|
||||||
<span className="text-gray-400">XP</span>
|
|
||||||
<span className="text-gray-400">
|
|
||||||
{formatNumber(user.xp)}/{formatNumber(user.maxXp)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="h-1.5 bg-black/60 rounded-full overflow-hidden">
|
|
||||||
<div
|
|
||||||
className="h-full bg-gradient-to-r from-blue-600 to-purple-500"
|
|
||||||
style={{
|
|
||||||
width: `${Math.min(
|
|
||||||
100,
|
|
||||||
(user.xp / user.maxXp) * 100
|
|
||||||
)}%`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -564,6 +564,8 @@ export default function EventsPageSection({
|
|||||||
...prev,
|
...prev,
|
||||||
[eventId]: true,
|
[eventId]: true,
|
||||||
}));
|
}));
|
||||||
|
// Rafraîchir le score dans le header
|
||||||
|
window.dispatchEvent(new Event("refreshUserScore"));
|
||||||
} else {
|
} else {
|
||||||
setError(result.error || "Une erreur est survenue");
|
setError(result.error || "Une erreur est survenue");
|
||||||
}
|
}
|
||||||
@@ -583,6 +585,8 @@ export default function EventsPageSection({
|
|||||||
...prev,
|
...prev,
|
||||||
[eventId]: false,
|
[eventId]: false,
|
||||||
}));
|
}));
|
||||||
|
// Rafraîchir le score dans le header
|
||||||
|
window.dispatchEvent(new Event("refreshUserScore"));
|
||||||
} else {
|
} else {
|
||||||
setError(result.error || "Une erreur est survenue");
|
setError(result.error || "Une erreur est survenue");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,6 +155,9 @@ export default function FeedbackModal({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rafraîchir le score dans le header
|
||||||
|
window.dispatchEvent(new Event("refreshUserScore"));
|
||||||
|
|
||||||
// Fermer la modale après 1.5 secondes
|
// Fermer la modale après 1.5 secondes
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
onClose();
|
onClose();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ interface UserData {
|
|||||||
xp: number;
|
xp: number;
|
||||||
maxXp: number;
|
maxXp: number;
|
||||||
level: number;
|
level: number;
|
||||||
|
score: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NavigationProps {
|
interface NavigationProps {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ interface UserData {
|
|||||||
xp: number;
|
xp: number;
|
||||||
maxXp: number;
|
maxXp: number;
|
||||||
level: number;
|
level: number;
|
||||||
|
score: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function NavigationWrapper() {
|
export default async function NavigationWrapper() {
|
||||||
@@ -31,6 +32,7 @@ export default async function NavigationWrapper() {
|
|||||||
xp: true,
|
xp: true,
|
||||||
maxXp: true,
|
maxXp: true,
|
||||||
level: true,
|
level: true,
|
||||||
|
score: true,
|
||||||
}),
|
}),
|
||||||
challengeService.getActiveChallengesCount(session.user.id),
|
challengeService.getActiveChallengesCount(session.user.id),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Avatar } from "@/components/ui";
|
import { Avatar } from "@/components/ui";
|
||||||
@@ -13,6 +13,7 @@ interface UserData {
|
|||||||
xp: number;
|
xp: number;
|
||||||
maxXp: number;
|
maxXp: number;
|
||||||
level: number;
|
level: number;
|
||||||
|
score: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PlayerStatsProps {
|
interface PlayerStatsProps {
|
||||||
@@ -32,6 +33,7 @@ const defaultUserData: UserData = {
|
|||||||
xp: 0,
|
xp: 0,
|
||||||
maxXp: 5000,
|
maxXp: 5000,
|
||||||
level: 1,
|
level: 1,
|
||||||
|
score: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PlayerStats({ initialUserData }: PlayerStatsProps) {
|
export default function PlayerStats({ initialUserData }: PlayerStatsProps) {
|
||||||
@@ -40,6 +42,31 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) {
|
|||||||
initialUserData || defaultUserData
|
initialUserData || defaultUserData
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const refreshUserData = useCallback(async () => {
|
||||||
|
if (!session?.user?.id) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/users/${session.user.id}`);
|
||||||
|
const data = await res.json();
|
||||||
|
if (data) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
setUserData({
|
||||||
|
username: data.username || "Guest",
|
||||||
|
avatar: data.avatar,
|
||||||
|
hp: data.hp || 1000,
|
||||||
|
maxHp: data.maxHp || 1000,
|
||||||
|
xp: data.xp || 0,
|
||||||
|
maxXp: data.maxXp || 5000,
|
||||||
|
level: data.level || 1,
|
||||||
|
score: data.score || 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error refreshing user data:", error);
|
||||||
|
}
|
||||||
|
}, [session]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Si on a déjà des données initiales, ne rien faire (déjà initialisé dans useState)
|
// Si on a déjà des données initiales, ne rien faire (déjà initialisé dans useState)
|
||||||
if (initialUserData) {
|
if (initialUserData) {
|
||||||
@@ -62,6 +89,7 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) {
|
|||||||
xp: data.xp || 0,
|
xp: data.xp || 0,
|
||||||
maxXp: data.maxXp || 5000,
|
maxXp: data.maxXp || 5000,
|
||||||
level: data.level || 1,
|
level: data.level || 1,
|
||||||
|
score: data.score || 0,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -77,6 +105,7 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) {
|
|||||||
xp: 0,
|
xp: 0,
|
||||||
maxXp: 5000,
|
maxXp: 5000,
|
||||||
level: 1,
|
level: 1,
|
||||||
|
score: 0,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -88,51 +117,19 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) {
|
|||||||
}
|
}
|
||||||
}, [session, initialUserData]);
|
}, [session, initialUserData]);
|
||||||
|
|
||||||
const { username, avatar, hp, maxHp, xp, maxXp, level } = userData;
|
// Écouter les événements de refresh du score
|
||||||
|
|
||||||
// Calculer les pourcentages cibles
|
|
||||||
const targetHpPercentage = (hp / maxHp) * 100;
|
|
||||||
const targetXpPercentage = (xp / maxXp) * 100;
|
|
||||||
|
|
||||||
// Initialiser les pourcentages à 0 si on a des données initiales (pour l'animation)
|
|
||||||
// Sinon utiliser directement les valeurs calculées
|
|
||||||
const [hpPercentage, setHpPercentage] = useState(
|
|
||||||
initialUserData ? 0 : targetHpPercentage
|
|
||||||
);
|
|
||||||
const [xpPercentage, setXpPercentage] = useState(
|
|
||||||
initialUserData ? 0 : targetXpPercentage
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Si on a des données initiales, animer depuis 0 vers la valeur cible
|
const handleRefreshScore = () => {
|
||||||
if (initialUserData) {
|
refreshUserData();
|
||||||
const hpTimer = setTimeout(() => {
|
|
||||||
setHpPercentage(targetHpPercentage);
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
const xpTimer = setTimeout(() => {
|
|
||||||
setXpPercentage(targetXpPercentage);
|
|
||||||
}, 200);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearTimeout(hpTimer);
|
|
||||||
clearTimeout(xpTimer);
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
// Sinon, mettre à jour directement (pour les pages Client Components)
|
|
||||||
// Utiliser requestAnimationFrame pour éviter les cascades de rendu
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
setHpPercentage(targetHpPercentage);
|
|
||||||
setXpPercentage(targetXpPercentage);
|
|
||||||
});
|
|
||||||
}, [targetHpPercentage, targetXpPercentage, initialUserData]);
|
|
||||||
|
|
||||||
const hpColor =
|
window.addEventListener("refreshUserScore", handleRefreshScore);
|
||||||
hpPercentage > 60
|
return () => {
|
||||||
? "from-green-600 to-green-700"
|
window.removeEventListener("refreshUserScore", handleRefreshScore);
|
||||||
: hpPercentage > 30
|
};
|
||||||
? "from-yellow-600 to-orange-700"
|
}, [refreshUserData]);
|
||||||
: "from-red-700 to-red-900";
|
|
||||||
|
const { username, avatar, level, score } = userData;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@@ -150,7 +147,7 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) {
|
|||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
<div className="flex flex-col gap-1.5 min-w-[180px] sm:min-w-[200px]">
|
<div className="flex flex-col gap-1.5 min-w-[140px] sm:min-w-[160px]">
|
||||||
{/* Username & Level */}
|
{/* Username & Level */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Link
|
<Link
|
||||||
@@ -166,57 +163,16 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bars side by side */}
|
{/* Score Display */}
|
||||||
<div className="flex flex-col gap-1">
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{/* HP Bar */}
|
<div className="text-gray-400 font-pixel text-xs uppercase">
|
||||||
<div className="relative h-2 flex-1 bg-gray-900 border border-gray-700 rounded overflow-hidden">
|
Score
|
||||||
<div
|
|
||||||
className={`absolute inset-0 bg-gradient-to-r ${hpColor} transition-all duration-1000 ease-out`}
|
|
||||||
style={{ width: `${hpPercentage}%` }}
|
|
||||||
>
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/10 to-transparent animate-shimmer"></div>
|
|
||||||
</div>
|
</div>
|
||||||
{hpPercentage < 30 && (
|
<div className="text-pixel-gold font-gaming font-bold text-sm">
|
||||||
<div className="absolute inset-0 border border-red-500 rounded animate-pulse"></div>
|
{formatNumber(score)}
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* XP Bar */}
|
|
||||||
<div className="relative h-2 flex-1 bg-gray-900 border border-pixel-gold/30 rounded overflow-hidden">
|
|
||||||
<div
|
|
||||||
className="absolute inset-0 bg-gradient-to-r from-pixel-gold/80 via-pixel-gold/70 to-pixel-gold/80 transition-all duration-1000 ease-out"
|
|
||||||
style={{ width: `${xpPercentage}%` }}
|
|
||||||
>
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/10 to-transparent animate-shimmer"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Labels */}
|
|
||||||
<div className="flex items-center gap-2 text-[8px] font-pixel text-gray-400">
|
|
||||||
<div className="flex-1 text-left">
|
|
||||||
HP {hp} / {maxHp}
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 text-right">
|
|
||||||
XP {formatNumber(xp)} / {formatNumber(maxXp)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style jsx>{`
|
|
||||||
@keyframes shimmer {
|
|
||||||
0% {
|
|
||||||
transform: translateX(-100%);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateX(100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.animate-shimmer {
|
|
||||||
animation: shimmer 2s infinite;
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ReactNode, useEffect } from "react";
|
import { ReactNode, useEffect } from "react";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
|
||||||
interface ModalProps {
|
interface ModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -37,7 +38,7 @@ export default function Modal({
|
|||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
return (
|
const modalContent = (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-[200] flex items-center justify-center p-4 backdrop-blur-sm"
|
className="fixed inset-0 z-[200] flex items-center justify-center p-4 backdrop-blur-sm"
|
||||||
style={{
|
style={{
|
||||||
@@ -59,4 +60,11 @@ export default function Modal({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Utiliser un portal pour rendre le modal directement dans le body
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
return createPortal(modalContent, document.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,6 +211,19 @@ export type IntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
_max?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BoolFilter<$PrismaModel = never> = {
|
||||||
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type EnumChallengeStatusFilter<$PrismaModel = never> = {
|
export type EnumChallengeStatusFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.ChallengeStatus | Prisma.EnumChallengeStatusFieldRefInput<$PrismaModel>
|
equals?: $Enums.ChallengeStatus | Prisma.EnumChallengeStatusFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.ChallengeStatus[]
|
in?: $Enums.ChallengeStatus[]
|
||||||
@@ -467,6 +480,19 @@ export type NestedFloatNullableFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedFloatNullableFilter<$PrismaModel> | number | null
|
not?: Prisma.NestedFloatNullableFilter<$PrismaModel> | number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NestedBoolFilter<$PrismaModel = never> = {
|
||||||
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedEnumChallengeStatusFilter<$PrismaModel = never> = {
|
export type NestedEnumChallengeStatusFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.ChallengeStatus | Prisma.EnumChallengeStatusFieldRefInput<$PrismaModel>
|
equals?: $Enums.ChallengeStatus | Prisma.EnumChallengeStatusFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.ChallengeStatus[]
|
in?: $Enums.ChallengeStatus[]
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1032,6 +1032,7 @@ export const EventFeedbackScalarFieldEnum = {
|
|||||||
eventId: 'eventId',
|
eventId: 'eventId',
|
||||||
rating: 'rating',
|
rating: 'rating',
|
||||||
comment: 'comment',
|
comment: 'comment',
|
||||||
|
isRead: 'isRead',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
@@ -1045,6 +1046,8 @@ export const SitePreferencesScalarFieldEnum = {
|
|||||||
eventsBackground: 'eventsBackground',
|
eventsBackground: 'eventsBackground',
|
||||||
leaderboardBackground: 'leaderboardBackground',
|
leaderboardBackground: 'leaderboardBackground',
|
||||||
challengesBackground: 'challengesBackground',
|
challengesBackground: 'challengesBackground',
|
||||||
|
eventRegistrationPoints: 'eventRegistrationPoints',
|
||||||
|
eventFeedbackPoints: 'eventFeedbackPoints',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
@@ -1136,6 +1139,13 @@ export type EnumEventTypeFieldRefInput<$PrismaModel> = FieldRefInputType<$Prisma
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'Boolean'
|
||||||
|
*/
|
||||||
|
export type BooleanFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Boolean'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to a field of type 'ChallengeStatus'
|
* Reference to a field of type 'ChallengeStatus'
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ export const EventFeedbackScalarFieldEnum = {
|
|||||||
eventId: 'eventId',
|
eventId: 'eventId',
|
||||||
rating: 'rating',
|
rating: 'rating',
|
||||||
comment: 'comment',
|
comment: 'comment',
|
||||||
|
isRead: 'isRead',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
@@ -154,6 +155,8 @@ export const SitePreferencesScalarFieldEnum = {
|
|||||||
eventsBackground: 'eventsBackground',
|
eventsBackground: 'eventsBackground',
|
||||||
leaderboardBackground: 'leaderboardBackground',
|
leaderboardBackground: 'leaderboardBackground',
|
||||||
challengesBackground: 'challengesBackground',
|
challengesBackground: 'challengesBackground',
|
||||||
|
eventRegistrationPoints: 'eventRegistrationPoints',
|
||||||
|
eventFeedbackPoints: 'eventFeedbackPoints',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export type EventFeedbackMinAggregateOutputType = {
|
|||||||
eventId: string | null
|
eventId: string | null
|
||||||
rating: number | null
|
rating: number | null
|
||||||
comment: string | null
|
comment: string | null
|
||||||
|
isRead: boolean | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
}
|
}
|
||||||
@@ -50,6 +51,7 @@ export type EventFeedbackMaxAggregateOutputType = {
|
|||||||
eventId: string | null
|
eventId: string | null
|
||||||
rating: number | null
|
rating: number | null
|
||||||
comment: string | null
|
comment: string | null
|
||||||
|
isRead: boolean | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
}
|
}
|
||||||
@@ -60,6 +62,7 @@ export type EventFeedbackCountAggregateOutputType = {
|
|||||||
eventId: number
|
eventId: number
|
||||||
rating: number
|
rating: number
|
||||||
comment: number
|
comment: number
|
||||||
|
isRead: number
|
||||||
createdAt: number
|
createdAt: number
|
||||||
updatedAt: number
|
updatedAt: number
|
||||||
_all: number
|
_all: number
|
||||||
@@ -80,6 +83,7 @@ export type EventFeedbackMinAggregateInputType = {
|
|||||||
eventId?: true
|
eventId?: true
|
||||||
rating?: true
|
rating?: true
|
||||||
comment?: true
|
comment?: true
|
||||||
|
isRead?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
}
|
}
|
||||||
@@ -90,6 +94,7 @@ export type EventFeedbackMaxAggregateInputType = {
|
|||||||
eventId?: true
|
eventId?: true
|
||||||
rating?: true
|
rating?: true
|
||||||
comment?: true
|
comment?: true
|
||||||
|
isRead?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
}
|
}
|
||||||
@@ -100,6 +105,7 @@ export type EventFeedbackCountAggregateInputType = {
|
|||||||
eventId?: true
|
eventId?: true
|
||||||
rating?: true
|
rating?: true
|
||||||
comment?: true
|
comment?: true
|
||||||
|
isRead?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
_all?: true
|
_all?: true
|
||||||
@@ -197,6 +203,7 @@ export type EventFeedbackGroupByOutputType = {
|
|||||||
eventId: string
|
eventId: string
|
||||||
rating: number
|
rating: number
|
||||||
comment: string | null
|
comment: string | null
|
||||||
|
isRead: boolean
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
_count: EventFeedbackCountAggregateOutputType | null
|
_count: EventFeedbackCountAggregateOutputType | null
|
||||||
@@ -230,6 +237,7 @@ export type EventFeedbackWhereInput = {
|
|||||||
eventId?: Prisma.StringFilter<"EventFeedback"> | string
|
eventId?: Prisma.StringFilter<"EventFeedback"> | string
|
||||||
rating?: Prisma.IntFilter<"EventFeedback"> | number
|
rating?: Prisma.IntFilter<"EventFeedback"> | number
|
||||||
comment?: Prisma.StringNullableFilter<"EventFeedback"> | string | null
|
comment?: Prisma.StringNullableFilter<"EventFeedback"> | string | null
|
||||||
|
isRead?: Prisma.BoolFilter<"EventFeedback"> | boolean
|
||||||
createdAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
||||||
event?: Prisma.XOR<Prisma.EventScalarRelationFilter, Prisma.EventWhereInput>
|
event?: Prisma.XOR<Prisma.EventScalarRelationFilter, Prisma.EventWhereInput>
|
||||||
@@ -242,6 +250,7 @@ export type EventFeedbackOrderByWithRelationInput = {
|
|||||||
eventId?: Prisma.SortOrder
|
eventId?: Prisma.SortOrder
|
||||||
rating?: Prisma.SortOrder
|
rating?: Prisma.SortOrder
|
||||||
comment?: Prisma.SortOrderInput | Prisma.SortOrder
|
comment?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
isRead?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
event?: Prisma.EventOrderByWithRelationInput
|
event?: Prisma.EventOrderByWithRelationInput
|
||||||
@@ -258,6 +267,7 @@ export type EventFeedbackWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
eventId?: Prisma.StringFilter<"EventFeedback"> | string
|
eventId?: Prisma.StringFilter<"EventFeedback"> | string
|
||||||
rating?: Prisma.IntFilter<"EventFeedback"> | number
|
rating?: Prisma.IntFilter<"EventFeedback"> | number
|
||||||
comment?: Prisma.StringNullableFilter<"EventFeedback"> | string | null
|
comment?: Prisma.StringNullableFilter<"EventFeedback"> | string | null
|
||||||
|
isRead?: Prisma.BoolFilter<"EventFeedback"> | boolean
|
||||||
createdAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
||||||
event?: Prisma.XOR<Prisma.EventScalarRelationFilter, Prisma.EventWhereInput>
|
event?: Prisma.XOR<Prisma.EventScalarRelationFilter, Prisma.EventWhereInput>
|
||||||
@@ -270,6 +280,7 @@ export type EventFeedbackOrderByWithAggregationInput = {
|
|||||||
eventId?: Prisma.SortOrder
|
eventId?: Prisma.SortOrder
|
||||||
rating?: Prisma.SortOrder
|
rating?: Prisma.SortOrder
|
||||||
comment?: Prisma.SortOrderInput | Prisma.SortOrder
|
comment?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
isRead?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
_count?: Prisma.EventFeedbackCountOrderByAggregateInput
|
_count?: Prisma.EventFeedbackCountOrderByAggregateInput
|
||||||
@@ -288,6 +299,7 @@ export type EventFeedbackScalarWhereWithAggregatesInput = {
|
|||||||
eventId?: Prisma.StringWithAggregatesFilter<"EventFeedback"> | string
|
eventId?: Prisma.StringWithAggregatesFilter<"EventFeedback"> | string
|
||||||
rating?: Prisma.IntWithAggregatesFilter<"EventFeedback"> | number
|
rating?: Prisma.IntWithAggregatesFilter<"EventFeedback"> | number
|
||||||
comment?: Prisma.StringNullableWithAggregatesFilter<"EventFeedback"> | string | null
|
comment?: Prisma.StringNullableWithAggregatesFilter<"EventFeedback"> | string | null
|
||||||
|
isRead?: Prisma.BoolWithAggregatesFilter<"EventFeedback"> | boolean
|
||||||
createdAt?: Prisma.DateTimeWithAggregatesFilter<"EventFeedback"> | Date | string
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"EventFeedback"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"EventFeedback"> | Date | string
|
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"EventFeedback"> | Date | string
|
||||||
}
|
}
|
||||||
@@ -296,6 +308,7 @@ export type EventFeedbackCreateInput = {
|
|||||||
id?: string
|
id?: string
|
||||||
rating: number
|
rating: number
|
||||||
comment?: string | null
|
comment?: string | null
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
event: Prisma.EventCreateNestedOneWithoutFeedbacksInput
|
event: Prisma.EventCreateNestedOneWithoutFeedbacksInput
|
||||||
@@ -308,6 +321,7 @@ export type EventFeedbackUncheckedCreateInput = {
|
|||||||
eventId: string
|
eventId: string
|
||||||
rating: number
|
rating: number
|
||||||
comment?: string | null
|
comment?: string | null
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
@@ -316,6 +330,7 @@ export type EventFeedbackUpdateInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
isRead?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
event?: Prisma.EventUpdateOneRequiredWithoutFeedbacksNestedInput
|
event?: Prisma.EventUpdateOneRequiredWithoutFeedbacksNestedInput
|
||||||
@@ -328,6 +343,7 @@ export type EventFeedbackUncheckedUpdateInput = {
|
|||||||
eventId?: Prisma.StringFieldUpdateOperationsInput | string
|
eventId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
isRead?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -338,6 +354,7 @@ export type EventFeedbackCreateManyInput = {
|
|||||||
eventId: string
|
eventId: string
|
||||||
rating: number
|
rating: number
|
||||||
comment?: string | null
|
comment?: string | null
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
@@ -346,6 +363,7 @@ export type EventFeedbackUpdateManyMutationInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
isRead?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -356,6 +374,7 @@ export type EventFeedbackUncheckedUpdateManyInput = {
|
|||||||
eventId?: Prisma.StringFieldUpdateOperationsInput | string
|
eventId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
isRead?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -381,6 +400,7 @@ export type EventFeedbackCountOrderByAggregateInput = {
|
|||||||
eventId?: Prisma.SortOrder
|
eventId?: Prisma.SortOrder
|
||||||
rating?: Prisma.SortOrder
|
rating?: Prisma.SortOrder
|
||||||
comment?: Prisma.SortOrder
|
comment?: Prisma.SortOrder
|
||||||
|
isRead?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
@@ -395,6 +415,7 @@ export type EventFeedbackMaxOrderByAggregateInput = {
|
|||||||
eventId?: Prisma.SortOrder
|
eventId?: Prisma.SortOrder
|
||||||
rating?: Prisma.SortOrder
|
rating?: Prisma.SortOrder
|
||||||
comment?: Prisma.SortOrder
|
comment?: Prisma.SortOrder
|
||||||
|
isRead?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
@@ -405,6 +426,7 @@ export type EventFeedbackMinOrderByAggregateInput = {
|
|||||||
eventId?: Prisma.SortOrder
|
eventId?: Prisma.SortOrder
|
||||||
rating?: Prisma.SortOrder
|
rating?: Prisma.SortOrder
|
||||||
comment?: Prisma.SortOrder
|
comment?: Prisma.SortOrder
|
||||||
|
isRead?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
@@ -497,10 +519,15 @@ export type EventFeedbackUncheckedUpdateManyWithoutEventNestedInput = {
|
|||||||
deleteMany?: Prisma.EventFeedbackScalarWhereInput | Prisma.EventFeedbackScalarWhereInput[]
|
deleteMany?: Prisma.EventFeedbackScalarWhereInput | Prisma.EventFeedbackScalarWhereInput[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BoolFieldUpdateOperationsInput = {
|
||||||
|
set?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export type EventFeedbackCreateWithoutUserInput = {
|
export type EventFeedbackCreateWithoutUserInput = {
|
||||||
id?: string
|
id?: string
|
||||||
rating: number
|
rating: number
|
||||||
comment?: string | null
|
comment?: string | null
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
event: Prisma.EventCreateNestedOneWithoutFeedbacksInput
|
event: Prisma.EventCreateNestedOneWithoutFeedbacksInput
|
||||||
@@ -511,6 +538,7 @@ export type EventFeedbackUncheckedCreateWithoutUserInput = {
|
|||||||
eventId: string
|
eventId: string
|
||||||
rating: number
|
rating: number
|
||||||
comment?: string | null
|
comment?: string | null
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
@@ -549,6 +577,7 @@ export type EventFeedbackScalarWhereInput = {
|
|||||||
eventId?: Prisma.StringFilter<"EventFeedback"> | string
|
eventId?: Prisma.StringFilter<"EventFeedback"> | string
|
||||||
rating?: Prisma.IntFilter<"EventFeedback"> | number
|
rating?: Prisma.IntFilter<"EventFeedback"> | number
|
||||||
comment?: Prisma.StringNullableFilter<"EventFeedback"> | string | null
|
comment?: Prisma.StringNullableFilter<"EventFeedback"> | string | null
|
||||||
|
isRead?: Prisma.BoolFilter<"EventFeedback"> | boolean
|
||||||
createdAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"EventFeedback"> | Date | string
|
||||||
}
|
}
|
||||||
@@ -557,6 +586,7 @@ export type EventFeedbackCreateWithoutEventInput = {
|
|||||||
id?: string
|
id?: string
|
||||||
rating: number
|
rating: number
|
||||||
comment?: string | null
|
comment?: string | null
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
user: Prisma.UserCreateNestedOneWithoutEventFeedbacksInput
|
user: Prisma.UserCreateNestedOneWithoutEventFeedbacksInput
|
||||||
@@ -567,6 +597,7 @@ export type EventFeedbackUncheckedCreateWithoutEventInput = {
|
|||||||
userId: string
|
userId: string
|
||||||
rating: number
|
rating: number
|
||||||
comment?: string | null
|
comment?: string | null
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
@@ -601,6 +632,7 @@ export type EventFeedbackCreateManyUserInput = {
|
|||||||
eventId: string
|
eventId: string
|
||||||
rating: number
|
rating: number
|
||||||
comment?: string | null
|
comment?: string | null
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
@@ -609,6 +641,7 @@ export type EventFeedbackUpdateWithoutUserInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
isRead?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
event?: Prisma.EventUpdateOneRequiredWithoutFeedbacksNestedInput
|
event?: Prisma.EventUpdateOneRequiredWithoutFeedbacksNestedInput
|
||||||
@@ -619,6 +652,7 @@ export type EventFeedbackUncheckedUpdateWithoutUserInput = {
|
|||||||
eventId?: Prisma.StringFieldUpdateOperationsInput | string
|
eventId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
isRead?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -628,6 +662,7 @@ export type EventFeedbackUncheckedUpdateManyWithoutUserInput = {
|
|||||||
eventId?: Prisma.StringFieldUpdateOperationsInput | string
|
eventId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
isRead?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -637,6 +672,7 @@ export type EventFeedbackCreateManyEventInput = {
|
|||||||
userId: string
|
userId: string
|
||||||
rating: number
|
rating: number
|
||||||
comment?: string | null
|
comment?: string | null
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
@@ -645,6 +681,7 @@ export type EventFeedbackUpdateWithoutEventInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
isRead?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
user?: Prisma.UserUpdateOneRequiredWithoutEventFeedbacksNestedInput
|
user?: Prisma.UserUpdateOneRequiredWithoutEventFeedbacksNestedInput
|
||||||
@@ -655,6 +692,7 @@ export type EventFeedbackUncheckedUpdateWithoutEventInput = {
|
|||||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
isRead?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -664,6 +702,7 @@ export type EventFeedbackUncheckedUpdateManyWithoutEventInput = {
|
|||||||
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
userId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
rating?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
comment?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
isRead?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -676,6 +715,7 @@ export type EventFeedbackSelect<ExtArgs extends runtime.Types.Extensions.Interna
|
|||||||
eventId?: boolean
|
eventId?: boolean
|
||||||
rating?: boolean
|
rating?: boolean
|
||||||
comment?: boolean
|
comment?: boolean
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
event?: boolean | Prisma.EventDefaultArgs<ExtArgs>
|
event?: boolean | Prisma.EventDefaultArgs<ExtArgs>
|
||||||
@@ -688,6 +728,7 @@ export type EventFeedbackSelectCreateManyAndReturn<ExtArgs extends runtime.Types
|
|||||||
eventId?: boolean
|
eventId?: boolean
|
||||||
rating?: boolean
|
rating?: boolean
|
||||||
comment?: boolean
|
comment?: boolean
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
event?: boolean | Prisma.EventDefaultArgs<ExtArgs>
|
event?: boolean | Prisma.EventDefaultArgs<ExtArgs>
|
||||||
@@ -700,6 +741,7 @@ export type EventFeedbackSelectUpdateManyAndReturn<ExtArgs extends runtime.Types
|
|||||||
eventId?: boolean
|
eventId?: boolean
|
||||||
rating?: boolean
|
rating?: boolean
|
||||||
comment?: boolean
|
comment?: boolean
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
event?: boolean | Prisma.EventDefaultArgs<ExtArgs>
|
event?: boolean | Prisma.EventDefaultArgs<ExtArgs>
|
||||||
@@ -712,11 +754,12 @@ export type EventFeedbackSelectScalar = {
|
|||||||
eventId?: boolean
|
eventId?: boolean
|
||||||
rating?: boolean
|
rating?: boolean
|
||||||
comment?: boolean
|
comment?: boolean
|
||||||
|
isRead?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventFeedbackOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "userId" | "eventId" | "rating" | "comment" | "createdAt" | "updatedAt", ExtArgs["result"]["eventFeedback"]>
|
export type EventFeedbackOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "userId" | "eventId" | "rating" | "comment" | "isRead" | "createdAt" | "updatedAt", ExtArgs["result"]["eventFeedback"]>
|
||||||
export type EventFeedbackInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type EventFeedbackInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
event?: boolean | Prisma.EventDefaultArgs<ExtArgs>
|
event?: boolean | Prisma.EventDefaultArgs<ExtArgs>
|
||||||
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
user?: boolean | Prisma.UserDefaultArgs<ExtArgs>
|
||||||
@@ -742,6 +785,7 @@ export type $EventFeedbackPayload<ExtArgs extends runtime.Types.Extensions.Inter
|
|||||||
eventId: string
|
eventId: string
|
||||||
rating: number
|
rating: number
|
||||||
comment: string | null
|
comment: string | null
|
||||||
|
isRead: boolean
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
}, ExtArgs["result"]["eventFeedback"]>
|
}, ExtArgs["result"]["eventFeedback"]>
|
||||||
@@ -1174,6 +1218,7 @@ export interface EventFeedbackFieldRefs {
|
|||||||
readonly eventId: Prisma.FieldRef<"EventFeedback", 'String'>
|
readonly eventId: Prisma.FieldRef<"EventFeedback", 'String'>
|
||||||
readonly rating: Prisma.FieldRef<"EventFeedback", 'Int'>
|
readonly rating: Prisma.FieldRef<"EventFeedback", 'Int'>
|
||||||
readonly comment: Prisma.FieldRef<"EventFeedback", 'String'>
|
readonly comment: Prisma.FieldRef<"EventFeedback", 'String'>
|
||||||
|
readonly isRead: Prisma.FieldRef<"EventFeedback", 'Boolean'>
|
||||||
readonly createdAt: Prisma.FieldRef<"EventFeedback", 'DateTime'>
|
readonly createdAt: Prisma.FieldRef<"EventFeedback", 'DateTime'>
|
||||||
readonly updatedAt: Prisma.FieldRef<"EventFeedback", 'DateTime'>
|
readonly updatedAt: Prisma.FieldRef<"EventFeedback", 'DateTime'>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,16 +20,30 @@ export type SitePreferencesModel = runtime.Types.Result.DefaultSelection<Prisma.
|
|||||||
|
|
||||||
export type AggregateSitePreferences = {
|
export type AggregateSitePreferences = {
|
||||||
_count: SitePreferencesCountAggregateOutputType | null
|
_count: SitePreferencesCountAggregateOutputType | null
|
||||||
|
_avg: SitePreferencesAvgAggregateOutputType | null
|
||||||
|
_sum: SitePreferencesSumAggregateOutputType | null
|
||||||
_min: SitePreferencesMinAggregateOutputType | null
|
_min: SitePreferencesMinAggregateOutputType | null
|
||||||
_max: SitePreferencesMaxAggregateOutputType | null
|
_max: SitePreferencesMaxAggregateOutputType | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SitePreferencesAvgAggregateOutputType = {
|
||||||
|
eventRegistrationPoints: number | null
|
||||||
|
eventFeedbackPoints: number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SitePreferencesSumAggregateOutputType = {
|
||||||
|
eventRegistrationPoints: number | null
|
||||||
|
eventFeedbackPoints: number | null
|
||||||
|
}
|
||||||
|
|
||||||
export type SitePreferencesMinAggregateOutputType = {
|
export type SitePreferencesMinAggregateOutputType = {
|
||||||
id: string | null
|
id: string | null
|
||||||
homeBackground: string | null
|
homeBackground: string | null
|
||||||
eventsBackground: string | null
|
eventsBackground: string | null
|
||||||
leaderboardBackground: string | null
|
leaderboardBackground: string | null
|
||||||
challengesBackground: string | null
|
challengesBackground: string | null
|
||||||
|
eventRegistrationPoints: number | null
|
||||||
|
eventFeedbackPoints: number | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
}
|
}
|
||||||
@@ -40,6 +54,8 @@ export type SitePreferencesMaxAggregateOutputType = {
|
|||||||
eventsBackground: string | null
|
eventsBackground: string | null
|
||||||
leaderboardBackground: string | null
|
leaderboardBackground: string | null
|
||||||
challengesBackground: string | null
|
challengesBackground: string | null
|
||||||
|
eventRegistrationPoints: number | null
|
||||||
|
eventFeedbackPoints: number | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
}
|
}
|
||||||
@@ -50,18 +66,32 @@ export type SitePreferencesCountAggregateOutputType = {
|
|||||||
eventsBackground: number
|
eventsBackground: number
|
||||||
leaderboardBackground: number
|
leaderboardBackground: number
|
||||||
challengesBackground: number
|
challengesBackground: number
|
||||||
|
eventRegistrationPoints: number
|
||||||
|
eventFeedbackPoints: number
|
||||||
createdAt: number
|
createdAt: number
|
||||||
updatedAt: number
|
updatedAt: number
|
||||||
_all: number
|
_all: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type SitePreferencesAvgAggregateInputType = {
|
||||||
|
eventRegistrationPoints?: true
|
||||||
|
eventFeedbackPoints?: true
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SitePreferencesSumAggregateInputType = {
|
||||||
|
eventRegistrationPoints?: true
|
||||||
|
eventFeedbackPoints?: true
|
||||||
|
}
|
||||||
|
|
||||||
export type SitePreferencesMinAggregateInputType = {
|
export type SitePreferencesMinAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
homeBackground?: true
|
homeBackground?: true
|
||||||
eventsBackground?: true
|
eventsBackground?: true
|
||||||
leaderboardBackground?: true
|
leaderboardBackground?: true
|
||||||
challengesBackground?: true
|
challengesBackground?: true
|
||||||
|
eventRegistrationPoints?: true
|
||||||
|
eventFeedbackPoints?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
}
|
}
|
||||||
@@ -72,6 +102,8 @@ export type SitePreferencesMaxAggregateInputType = {
|
|||||||
eventsBackground?: true
|
eventsBackground?: true
|
||||||
leaderboardBackground?: true
|
leaderboardBackground?: true
|
||||||
challengesBackground?: true
|
challengesBackground?: true
|
||||||
|
eventRegistrationPoints?: true
|
||||||
|
eventFeedbackPoints?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
}
|
}
|
||||||
@@ -82,6 +114,8 @@ export type SitePreferencesCountAggregateInputType = {
|
|||||||
eventsBackground?: true
|
eventsBackground?: true
|
||||||
leaderboardBackground?: true
|
leaderboardBackground?: true
|
||||||
challengesBackground?: true
|
challengesBackground?: true
|
||||||
|
eventRegistrationPoints?: true
|
||||||
|
eventFeedbackPoints?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
_all?: true
|
_all?: true
|
||||||
@@ -122,6 +156,18 @@ export type SitePreferencesAggregateArgs<ExtArgs extends runtime.Types.Extension
|
|||||||
* Count returned SitePreferences
|
* Count returned SitePreferences
|
||||||
**/
|
**/
|
||||||
_count?: true | SitePreferencesCountAggregateInputType
|
_count?: true | SitePreferencesCountAggregateInputType
|
||||||
|
/**
|
||||||
|
* {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs}
|
||||||
|
*
|
||||||
|
* Select which fields to average
|
||||||
|
**/
|
||||||
|
_avg?: SitePreferencesAvgAggregateInputType
|
||||||
|
/**
|
||||||
|
* {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs}
|
||||||
|
*
|
||||||
|
* Select which fields to sum
|
||||||
|
**/
|
||||||
|
_sum?: SitePreferencesSumAggregateInputType
|
||||||
/**
|
/**
|
||||||
* {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs}
|
* {@link https://www.prisma.io/docs/concepts/components/prisma-client/aggregations Aggregation Docs}
|
||||||
*
|
*
|
||||||
@@ -155,6 +201,8 @@ export type SitePreferencesGroupByArgs<ExtArgs extends runtime.Types.Extensions.
|
|||||||
take?: number
|
take?: number
|
||||||
skip?: number
|
skip?: number
|
||||||
_count?: SitePreferencesCountAggregateInputType | true
|
_count?: SitePreferencesCountAggregateInputType | true
|
||||||
|
_avg?: SitePreferencesAvgAggregateInputType
|
||||||
|
_sum?: SitePreferencesSumAggregateInputType
|
||||||
_min?: SitePreferencesMinAggregateInputType
|
_min?: SitePreferencesMinAggregateInputType
|
||||||
_max?: SitePreferencesMaxAggregateInputType
|
_max?: SitePreferencesMaxAggregateInputType
|
||||||
}
|
}
|
||||||
@@ -165,9 +213,13 @@ export type SitePreferencesGroupByOutputType = {
|
|||||||
eventsBackground: string | null
|
eventsBackground: string | null
|
||||||
leaderboardBackground: string | null
|
leaderboardBackground: string | null
|
||||||
challengesBackground: string | null
|
challengesBackground: string | null
|
||||||
|
eventRegistrationPoints: number
|
||||||
|
eventFeedbackPoints: number
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
_count: SitePreferencesCountAggregateOutputType | null
|
_count: SitePreferencesCountAggregateOutputType | null
|
||||||
|
_avg: SitePreferencesAvgAggregateOutputType | null
|
||||||
|
_sum: SitePreferencesSumAggregateOutputType | null
|
||||||
_min: SitePreferencesMinAggregateOutputType | null
|
_min: SitePreferencesMinAggregateOutputType | null
|
||||||
_max: SitePreferencesMaxAggregateOutputType | null
|
_max: SitePreferencesMaxAggregateOutputType | null
|
||||||
}
|
}
|
||||||
@@ -196,6 +248,8 @@ export type SitePreferencesWhereInput = {
|
|||||||
eventsBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
eventsBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
||||||
leaderboardBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
leaderboardBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
||||||
challengesBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
challengesBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
||||||
|
eventRegistrationPoints?: Prisma.IntFilter<"SitePreferences"> | number
|
||||||
|
eventFeedbackPoints?: Prisma.IntFilter<"SitePreferences"> | number
|
||||||
createdAt?: Prisma.DateTimeFilter<"SitePreferences"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"SitePreferences"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"SitePreferences"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"SitePreferences"> | Date | string
|
||||||
}
|
}
|
||||||
@@ -206,6 +260,8 @@ export type SitePreferencesOrderByWithRelationInput = {
|
|||||||
eventsBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
eventsBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
leaderboardBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
leaderboardBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
challengesBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
challengesBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
eventRegistrationPoints?: Prisma.SortOrder
|
||||||
|
eventFeedbackPoints?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
@@ -219,6 +275,8 @@ export type SitePreferencesWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
eventsBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
eventsBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
||||||
leaderboardBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
leaderboardBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
||||||
challengesBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
challengesBackground?: Prisma.StringNullableFilter<"SitePreferences"> | string | null
|
||||||
|
eventRegistrationPoints?: Prisma.IntFilter<"SitePreferences"> | number
|
||||||
|
eventFeedbackPoints?: Prisma.IntFilter<"SitePreferences"> | number
|
||||||
createdAt?: Prisma.DateTimeFilter<"SitePreferences"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"SitePreferences"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"SitePreferences"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"SitePreferences"> | Date | string
|
||||||
}, "id">
|
}, "id">
|
||||||
@@ -229,11 +287,15 @@ export type SitePreferencesOrderByWithAggregationInput = {
|
|||||||
eventsBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
eventsBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
leaderboardBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
leaderboardBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
challengesBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
challengesBackground?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
eventRegistrationPoints?: Prisma.SortOrder
|
||||||
|
eventFeedbackPoints?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
_count?: Prisma.SitePreferencesCountOrderByAggregateInput
|
_count?: Prisma.SitePreferencesCountOrderByAggregateInput
|
||||||
|
_avg?: Prisma.SitePreferencesAvgOrderByAggregateInput
|
||||||
_max?: Prisma.SitePreferencesMaxOrderByAggregateInput
|
_max?: Prisma.SitePreferencesMaxOrderByAggregateInput
|
||||||
_min?: Prisma.SitePreferencesMinOrderByAggregateInput
|
_min?: Prisma.SitePreferencesMinOrderByAggregateInput
|
||||||
|
_sum?: Prisma.SitePreferencesSumOrderByAggregateInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SitePreferencesScalarWhereWithAggregatesInput = {
|
export type SitePreferencesScalarWhereWithAggregatesInput = {
|
||||||
@@ -245,6 +307,8 @@ export type SitePreferencesScalarWhereWithAggregatesInput = {
|
|||||||
eventsBackground?: Prisma.StringNullableWithAggregatesFilter<"SitePreferences"> | string | null
|
eventsBackground?: Prisma.StringNullableWithAggregatesFilter<"SitePreferences"> | string | null
|
||||||
leaderboardBackground?: Prisma.StringNullableWithAggregatesFilter<"SitePreferences"> | string | null
|
leaderboardBackground?: Prisma.StringNullableWithAggregatesFilter<"SitePreferences"> | string | null
|
||||||
challengesBackground?: Prisma.StringNullableWithAggregatesFilter<"SitePreferences"> | string | null
|
challengesBackground?: Prisma.StringNullableWithAggregatesFilter<"SitePreferences"> | string | null
|
||||||
|
eventRegistrationPoints?: Prisma.IntWithAggregatesFilter<"SitePreferences"> | number
|
||||||
|
eventFeedbackPoints?: Prisma.IntWithAggregatesFilter<"SitePreferences"> | number
|
||||||
createdAt?: Prisma.DateTimeWithAggregatesFilter<"SitePreferences"> | Date | string
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"SitePreferences"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"SitePreferences"> | Date | string
|
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"SitePreferences"> | Date | string
|
||||||
}
|
}
|
||||||
@@ -255,6 +319,8 @@ export type SitePreferencesCreateInput = {
|
|||||||
eventsBackground?: string | null
|
eventsBackground?: string | null
|
||||||
leaderboardBackground?: string | null
|
leaderboardBackground?: string | null
|
||||||
challengesBackground?: string | null
|
challengesBackground?: string | null
|
||||||
|
eventRegistrationPoints?: number
|
||||||
|
eventFeedbackPoints?: number
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
@@ -265,6 +331,8 @@ export type SitePreferencesUncheckedCreateInput = {
|
|||||||
eventsBackground?: string | null
|
eventsBackground?: string | null
|
||||||
leaderboardBackground?: string | null
|
leaderboardBackground?: string | null
|
||||||
challengesBackground?: string | null
|
challengesBackground?: string | null
|
||||||
|
eventRegistrationPoints?: number
|
||||||
|
eventFeedbackPoints?: number
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
@@ -275,6 +343,8 @@ export type SitePreferencesUpdateInput = {
|
|||||||
eventsBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
eventsBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
leaderboardBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
leaderboardBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
challengesBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
challengesBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
eventRegistrationPoints?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
|
eventFeedbackPoints?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -285,6 +355,8 @@ export type SitePreferencesUncheckedUpdateInput = {
|
|||||||
eventsBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
eventsBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
leaderboardBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
leaderboardBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
challengesBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
challengesBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
eventRegistrationPoints?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
|
eventFeedbackPoints?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -295,6 +367,8 @@ export type SitePreferencesCreateManyInput = {
|
|||||||
eventsBackground?: string | null
|
eventsBackground?: string | null
|
||||||
leaderboardBackground?: string | null
|
leaderboardBackground?: string | null
|
||||||
challengesBackground?: string | null
|
challengesBackground?: string | null
|
||||||
|
eventRegistrationPoints?: number
|
||||||
|
eventFeedbackPoints?: number
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
}
|
}
|
||||||
@@ -305,6 +379,8 @@ export type SitePreferencesUpdateManyMutationInput = {
|
|||||||
eventsBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
eventsBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
leaderboardBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
leaderboardBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
challengesBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
challengesBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
eventRegistrationPoints?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
|
eventFeedbackPoints?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -315,6 +391,8 @@ export type SitePreferencesUncheckedUpdateManyInput = {
|
|||||||
eventsBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
eventsBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
leaderboardBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
leaderboardBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
challengesBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
challengesBackground?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
eventRegistrationPoints?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
|
eventFeedbackPoints?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
@@ -325,16 +403,25 @@ export type SitePreferencesCountOrderByAggregateInput = {
|
|||||||
eventsBackground?: Prisma.SortOrder
|
eventsBackground?: Prisma.SortOrder
|
||||||
leaderboardBackground?: Prisma.SortOrder
|
leaderboardBackground?: Prisma.SortOrder
|
||||||
challengesBackground?: Prisma.SortOrder
|
challengesBackground?: Prisma.SortOrder
|
||||||
|
eventRegistrationPoints?: Prisma.SortOrder
|
||||||
|
eventFeedbackPoints?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SitePreferencesAvgOrderByAggregateInput = {
|
||||||
|
eventRegistrationPoints?: Prisma.SortOrder
|
||||||
|
eventFeedbackPoints?: Prisma.SortOrder
|
||||||
|
}
|
||||||
|
|
||||||
export type SitePreferencesMaxOrderByAggregateInput = {
|
export type SitePreferencesMaxOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
homeBackground?: Prisma.SortOrder
|
homeBackground?: Prisma.SortOrder
|
||||||
eventsBackground?: Prisma.SortOrder
|
eventsBackground?: Prisma.SortOrder
|
||||||
leaderboardBackground?: Prisma.SortOrder
|
leaderboardBackground?: Prisma.SortOrder
|
||||||
challengesBackground?: Prisma.SortOrder
|
challengesBackground?: Prisma.SortOrder
|
||||||
|
eventRegistrationPoints?: Prisma.SortOrder
|
||||||
|
eventFeedbackPoints?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
@@ -345,10 +432,17 @@ export type SitePreferencesMinOrderByAggregateInput = {
|
|||||||
eventsBackground?: Prisma.SortOrder
|
eventsBackground?: Prisma.SortOrder
|
||||||
leaderboardBackground?: Prisma.SortOrder
|
leaderboardBackground?: Prisma.SortOrder
|
||||||
challengesBackground?: Prisma.SortOrder
|
challengesBackground?: Prisma.SortOrder
|
||||||
|
eventRegistrationPoints?: Prisma.SortOrder
|
||||||
|
eventFeedbackPoints?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SitePreferencesSumOrderByAggregateInput = {
|
||||||
|
eventRegistrationPoints?: Prisma.SortOrder
|
||||||
|
eventFeedbackPoints?: Prisma.SortOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type SitePreferencesSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type SitePreferencesSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
@@ -357,6 +451,8 @@ export type SitePreferencesSelect<ExtArgs extends runtime.Types.Extensions.Inter
|
|||||||
eventsBackground?: boolean
|
eventsBackground?: boolean
|
||||||
leaderboardBackground?: boolean
|
leaderboardBackground?: boolean
|
||||||
challengesBackground?: boolean
|
challengesBackground?: boolean
|
||||||
|
eventRegistrationPoints?: boolean
|
||||||
|
eventFeedbackPoints?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
}, ExtArgs["result"]["sitePreferences"]>
|
}, ExtArgs["result"]["sitePreferences"]>
|
||||||
@@ -367,6 +463,8 @@ export type SitePreferencesSelectCreateManyAndReturn<ExtArgs extends runtime.Typ
|
|||||||
eventsBackground?: boolean
|
eventsBackground?: boolean
|
||||||
leaderboardBackground?: boolean
|
leaderboardBackground?: boolean
|
||||||
challengesBackground?: boolean
|
challengesBackground?: boolean
|
||||||
|
eventRegistrationPoints?: boolean
|
||||||
|
eventFeedbackPoints?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
}, ExtArgs["result"]["sitePreferences"]>
|
}, ExtArgs["result"]["sitePreferences"]>
|
||||||
@@ -377,6 +475,8 @@ export type SitePreferencesSelectUpdateManyAndReturn<ExtArgs extends runtime.Typ
|
|||||||
eventsBackground?: boolean
|
eventsBackground?: boolean
|
||||||
leaderboardBackground?: boolean
|
leaderboardBackground?: boolean
|
||||||
challengesBackground?: boolean
|
challengesBackground?: boolean
|
||||||
|
eventRegistrationPoints?: boolean
|
||||||
|
eventFeedbackPoints?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
}, ExtArgs["result"]["sitePreferences"]>
|
}, ExtArgs["result"]["sitePreferences"]>
|
||||||
@@ -387,11 +487,13 @@ export type SitePreferencesSelectScalar = {
|
|||||||
eventsBackground?: boolean
|
eventsBackground?: boolean
|
||||||
leaderboardBackground?: boolean
|
leaderboardBackground?: boolean
|
||||||
challengesBackground?: boolean
|
challengesBackground?: boolean
|
||||||
|
eventRegistrationPoints?: boolean
|
||||||
|
eventFeedbackPoints?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SitePreferencesOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "homeBackground" | "eventsBackground" | "leaderboardBackground" | "challengesBackground" | "createdAt" | "updatedAt", ExtArgs["result"]["sitePreferences"]>
|
export type SitePreferencesOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "homeBackground" | "eventsBackground" | "leaderboardBackground" | "challengesBackground" | "eventRegistrationPoints" | "eventFeedbackPoints" | "createdAt" | "updatedAt", ExtArgs["result"]["sitePreferences"]>
|
||||||
|
|
||||||
export type $SitePreferencesPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type $SitePreferencesPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
name: "SitePreferences"
|
name: "SitePreferences"
|
||||||
@@ -402,6 +504,8 @@ export type $SitePreferencesPayload<ExtArgs extends runtime.Types.Extensions.Int
|
|||||||
eventsBackground: string | null
|
eventsBackground: string | null
|
||||||
leaderboardBackground: string | null
|
leaderboardBackground: string | null
|
||||||
challengesBackground: string | null
|
challengesBackground: string | null
|
||||||
|
eventRegistrationPoints: number
|
||||||
|
eventFeedbackPoints: number
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
}, ExtArgs["result"]["sitePreferences"]>
|
}, ExtArgs["result"]["sitePreferences"]>
|
||||||
@@ -832,6 +936,8 @@ export interface SitePreferencesFieldRefs {
|
|||||||
readonly eventsBackground: Prisma.FieldRef<"SitePreferences", 'String'>
|
readonly eventsBackground: Prisma.FieldRef<"SitePreferences", 'String'>
|
||||||
readonly leaderboardBackground: Prisma.FieldRef<"SitePreferences", 'String'>
|
readonly leaderboardBackground: Prisma.FieldRef<"SitePreferences", 'String'>
|
||||||
readonly challengesBackground: Prisma.FieldRef<"SitePreferences", 'String'>
|
readonly challengesBackground: Prisma.FieldRef<"SitePreferences", 'String'>
|
||||||
|
readonly eventRegistrationPoints: Prisma.FieldRef<"SitePreferences", 'Int'>
|
||||||
|
readonly eventFeedbackPoints: Prisma.FieldRef<"SitePreferences", 'Int'>
|
||||||
readonly createdAt: Prisma.FieldRef<"SitePreferences", 'DateTime'>
|
readonly createdAt: Prisma.FieldRef<"SitePreferences", 'DateTime'>
|
||||||
readonly updatedAt: Prisma.FieldRef<"SitePreferences", 'DateTime'>
|
readonly updatedAt: Prisma.FieldRef<"SitePreferences", 'DateTime'>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA defer_foreign_keys=ON;
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_SitePreferences" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY DEFAULT 'global',
|
||||||
|
"homeBackground" TEXT,
|
||||||
|
"eventsBackground" TEXT,
|
||||||
|
"leaderboardBackground" TEXT,
|
||||||
|
"challengesBackground" TEXT,
|
||||||
|
"eventRegistrationPoints" INTEGER NOT NULL DEFAULT 100,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_SitePreferences" ("challengesBackground", "createdAt", "eventsBackground", "homeBackground", "id", "leaderboardBackground", "updatedAt") SELECT "challengesBackground", "createdAt", "eventsBackground", "homeBackground", "id", "leaderboardBackground", "updatedAt" FROM "SitePreferences";
|
||||||
|
DROP TABLE "SitePreferences";
|
||||||
|
ALTER TABLE "new_SitePreferences" RENAME TO "SitePreferences";
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
PRAGMA defer_foreign_keys=OFF;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA defer_foreign_keys=ON;
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_SitePreferences" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY DEFAULT 'global',
|
||||||
|
"homeBackground" TEXT,
|
||||||
|
"eventsBackground" TEXT,
|
||||||
|
"leaderboardBackground" TEXT,
|
||||||
|
"challengesBackground" TEXT,
|
||||||
|
"eventRegistrationPoints" INTEGER NOT NULL DEFAULT 100,
|
||||||
|
"eventFeedbackPoints" INTEGER NOT NULL DEFAULT 50,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_SitePreferences" ("challengesBackground", "createdAt", "eventRegistrationPoints", "eventsBackground", "homeBackground", "id", "leaderboardBackground", "updatedAt") SELECT "challengesBackground", "createdAt", "eventRegistrationPoints", "eventsBackground", "homeBackground", "id", "leaderboardBackground", "updatedAt" FROM "SitePreferences";
|
||||||
|
DROP TABLE "SitePreferences";
|
||||||
|
ALTER TABLE "new_SitePreferences" RENAME TO "SitePreferences";
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
PRAGMA defer_foreign_keys=OFF;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA defer_foreign_keys=ON;
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_SitePreferences" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY DEFAULT 'global',
|
||||||
|
"homeBackground" TEXT,
|
||||||
|
"eventsBackground" TEXT,
|
||||||
|
"leaderboardBackground" TEXT,
|
||||||
|
"challengesBackground" TEXT,
|
||||||
|
"eventRegistrationPoints" INTEGER NOT NULL DEFAULT 100,
|
||||||
|
"eventFeedbackPoints" INTEGER NOT NULL DEFAULT 100,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_SitePreferences" ("challengesBackground", "createdAt", "eventFeedbackPoints", "eventRegistrationPoints", "eventsBackground", "homeBackground", "id", "leaderboardBackground", "updatedAt") SELECT "challengesBackground", "createdAt", "eventFeedbackPoints", "eventRegistrationPoints", "eventsBackground", "homeBackground", "id", "leaderboardBackground", "updatedAt" FROM "SitePreferences";
|
||||||
|
DROP TABLE "SitePreferences";
|
||||||
|
ALTER TABLE "new_SitePreferences" RENAME TO "SitePreferences";
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
PRAGMA defer_foreign_keys=OFF;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA defer_foreign_keys=ON;
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_EventFeedback" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"eventId" TEXT NOT NULL,
|
||||||
|
"rating" INTEGER NOT NULL,
|
||||||
|
"comment" TEXT,
|
||||||
|
"isRead" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "EventFeedback_eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "Event" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "EventFeedback_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_EventFeedback" ("comment", "createdAt", "eventId", "id", "rating", "updatedAt", "userId") SELECT "comment", "createdAt", "eventId", "id", "rating", "updatedAt", "userId" FROM "EventFeedback";
|
||||||
|
DROP TABLE "EventFeedback";
|
||||||
|
ALTER TABLE "new_EventFeedback" RENAME TO "EventFeedback";
|
||||||
|
CREATE INDEX "EventFeedback_userId_idx" ON "EventFeedback"("userId");
|
||||||
|
CREATE INDEX "EventFeedback_eventId_idx" ON "EventFeedback"("eventId");
|
||||||
|
CREATE INDEX "EventFeedback_isRead_idx" ON "EventFeedback"("isRead");
|
||||||
|
CREATE UNIQUE INDEX "EventFeedback_userId_eventId_key" ON "EventFeedback"("userId", "eventId");
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
PRAGMA defer_foreign_keys=OFF;
|
||||||
@@ -84,6 +84,7 @@ model EventFeedback {
|
|||||||
eventId String
|
eventId String
|
||||||
rating Int
|
rating Int
|
||||||
comment String?
|
comment String?
|
||||||
|
isRead Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
event Event @relation(fields: [eventId], references: [id], onDelete: Cascade)
|
event Event @relation(fields: [eventId], references: [id], onDelete: Cascade)
|
||||||
@@ -92,6 +93,7 @@ model EventFeedback {
|
|||||||
@@unique([userId, eventId])
|
@@unique([userId, eventId])
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
@@index([eventId])
|
@@index([eventId])
|
||||||
|
@@index([isRead])
|
||||||
}
|
}
|
||||||
|
|
||||||
model SitePreferences {
|
model SitePreferences {
|
||||||
@@ -100,6 +102,8 @@ model SitePreferences {
|
|||||||
eventsBackground String?
|
eventsBackground String?
|
||||||
leaderboardBackground String?
|
leaderboardBackground String?
|
||||||
challengesBackground String?
|
challengesBackground String?
|
||||||
|
eventRegistrationPoints Int @default(100)
|
||||||
|
eventFeedbackPoints Int @default(100)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { prisma } from "../database";
|
|||||||
import type { EventFeedback, Prisma } from "@/prisma/generated/prisma/client";
|
import type { EventFeedback, Prisma } from "@/prisma/generated/prisma/client";
|
||||||
import { ValidationError, NotFoundError } from "../errors";
|
import { ValidationError, NotFoundError } from "../errors";
|
||||||
import { eventService } from "./event.service";
|
import { eventService } from "./event.service";
|
||||||
|
import { sitePreferencesService } from "../preferences/site-preferences.service";
|
||||||
|
|
||||||
export interface CreateOrUpdateFeedbackInput {
|
export interface CreateOrUpdateFeedbackInput {
|
||||||
rating: number;
|
rating: number;
|
||||||
@@ -97,6 +98,8 @@ export class EventFeedbackService {
|
|||||||
id: true,
|
id: true,
|
||||||
username: true,
|
username: true,
|
||||||
email: true,
|
email: true,
|
||||||
|
avatar: true,
|
||||||
|
score: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -168,11 +171,34 @@ export class EventFeedbackService {
|
|||||||
throw new NotFoundError("Événement");
|
throw new NotFoundError("Événement");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Créer ou mettre à jour le feedback
|
// Vérifier si c'est un nouveau feedback ou une mise à jour
|
||||||
return this.createOrUpdateFeedback(userId, eventId, {
|
const existingFeedback = await this.getUserFeedback(userId, eventId);
|
||||||
|
const isNewFeedback = !existingFeedback;
|
||||||
|
|
||||||
|
// Récupérer les points à attribuer depuis les préférences du site
|
||||||
|
const sitePreferences = await sitePreferencesService.getOrCreateSitePreferences();
|
||||||
|
const pointsToAward = sitePreferences.eventFeedbackPoints || 100;
|
||||||
|
|
||||||
|
// Créer ou mettre à jour le feedback et attribuer les points (seulement pour nouveau feedback)
|
||||||
|
const [feedback] = await Promise.all([
|
||||||
|
this.createOrUpdateFeedback(userId, eventId, {
|
||||||
rating: data.rating,
|
rating: data.rating,
|
||||||
comment: data.comment || null,
|
comment: data.comment || null,
|
||||||
});
|
}),
|
||||||
|
// Attribuer les points seulement si c'est un nouveau feedback
|
||||||
|
isNewFeedback
|
||||||
|
? prisma.user.update({
|
||||||
|
where: { id: userId },
|
||||||
|
data: {
|
||||||
|
score: {
|
||||||
|
increment: pointsToAward,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
: Promise.resolve(null),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return feedback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { EventRegistration } from "@/prisma/generated/prisma/client";
|
|||||||
import { ValidationError, NotFoundError, ConflictError } from "../errors";
|
import { ValidationError, NotFoundError, ConflictError } from "../errors";
|
||||||
import { eventService } from "./event.service";
|
import { eventService } from "./event.service";
|
||||||
import { calculateEventStatus } from "@/lib/eventStatus";
|
import { calculateEventStatus } from "@/lib/eventStatus";
|
||||||
|
import { sitePreferencesService } from "../preferences/site-preferences.service";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service de gestion des inscriptions aux événements
|
* Service de gestion des inscriptions aux événements
|
||||||
@@ -24,18 +25,40 @@ export class EventRegistrationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Désinscrit un utilisateur d'un événement
|
* Désinscrit un utilisateur d'un événement et retire les points attribués
|
||||||
*/
|
*/
|
||||||
async unregisterUserFromEvent(
|
async unregisterUserFromEvent(
|
||||||
userId: string,
|
userId: string,
|
||||||
eventId: string
|
eventId: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await prisma.eventRegistration.deleteMany({
|
// Vérifier que l'utilisateur est bien inscrit avant de retirer les points
|
||||||
|
const isRegistered = await this.checkUserRegistration(userId, eventId);
|
||||||
|
if (!isRegistered) {
|
||||||
|
return; // Pas d'inscription, rien à faire
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer les points à retirer depuis les préférences du site
|
||||||
|
const sitePreferences =
|
||||||
|
await sitePreferencesService.getOrCreateSitePreferences();
|
||||||
|
const pointsToRemove = sitePreferences.eventRegistrationPoints || 100;
|
||||||
|
|
||||||
|
// Supprimer l'inscription et retirer les points en parallèle
|
||||||
|
await Promise.all([
|
||||||
|
prisma.eventRegistration.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
eventId,
|
eventId,
|
||||||
},
|
},
|
||||||
});
|
}),
|
||||||
|
prisma.user.update({
|
||||||
|
where: { id: userId },
|
||||||
|
data: {
|
||||||
|
score: {
|
||||||
|
decrement: pointsToRemove,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,6 +119,35 @@ export class EventRegistrationService {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère tous les inscrits d'un événement avec leurs informations
|
||||||
|
*/
|
||||||
|
async getEventRegistrations(eventId: string) {
|
||||||
|
return prisma.eventRegistration.findMany({
|
||||||
|
where: {
|
||||||
|
eventId,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
avatar: true,
|
||||||
|
score: true,
|
||||||
|
level: true,
|
||||||
|
hp: true,
|
||||||
|
maxHp: true,
|
||||||
|
xp: true,
|
||||||
|
maxXp: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: "asc",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Valide et inscrit un utilisateur à un événement avec toutes les règles métier
|
* Valide et inscrit un utilisateur à un événement avec toutes les règles métier
|
||||||
*/
|
*/
|
||||||
@@ -123,8 +175,25 @@ export class EventRegistrationService {
|
|||||||
throw new ConflictError("Vous êtes déjà inscrit à cet événement");
|
throw new ConflictError("Vous êtes déjà inscrit à cet événement");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Créer l'inscription
|
// Récupérer les points à attribuer depuis les préférences du site
|
||||||
return this.registerUserToEvent(userId, eventId);
|
const sitePreferences =
|
||||||
|
await sitePreferencesService.getOrCreateSitePreferences();
|
||||||
|
const pointsToAward = sitePreferences.eventRegistrationPoints || 100;
|
||||||
|
|
||||||
|
// Créer l'inscription et attribuer les points en parallèle
|
||||||
|
const [registration] = await Promise.all([
|
||||||
|
this.registerUserToEvent(userId, eventId),
|
||||||
|
prisma.user.update({
|
||||||
|
where: { id: userId },
|
||||||
|
data: {
|
||||||
|
score: {
|
||||||
|
increment: pointsToAward,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return registration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export interface UpdateSitePreferencesInput {
|
|||||||
eventsBackground?: string | null;
|
eventsBackground?: string | null;
|
||||||
leaderboardBackground?: string | null;
|
leaderboardBackground?: string | null;
|
||||||
challengesBackground?: string | null;
|
challengesBackground?: string | null;
|
||||||
|
eventRegistrationPoints?: number;
|
||||||
|
eventFeedbackPoints?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,6 +40,8 @@ export class SitePreferencesService {
|
|||||||
eventsBackground: null,
|
eventsBackground: null,
|
||||||
leaderboardBackground: null,
|
leaderboardBackground: null,
|
||||||
challengesBackground: null,
|
challengesBackground: null,
|
||||||
|
eventRegistrationPoints: 100,
|
||||||
|
eventFeedbackPoints: 100,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -70,6 +74,14 @@ export class SitePreferencesService {
|
|||||||
data.challengesBackground === ""
|
data.challengesBackground === ""
|
||||||
? null
|
? null
|
||||||
: (data.challengesBackground ?? undefined),
|
: (data.challengesBackground ?? undefined),
|
||||||
|
eventRegistrationPoints:
|
||||||
|
data.eventRegistrationPoints !== undefined
|
||||||
|
? data.eventRegistrationPoints
|
||||||
|
: undefined,
|
||||||
|
eventFeedbackPoints:
|
||||||
|
data.eventFeedbackPoints !== undefined
|
||||||
|
? data.eventFeedbackPoints
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
id: "global",
|
id: "global",
|
||||||
@@ -85,6 +97,8 @@ export class SitePreferencesService {
|
|||||||
data.challengesBackground === ""
|
data.challengesBackground === ""
|
||||||
? null
|
? null
|
||||||
: (data.challengesBackground ?? null),
|
: (data.challengesBackground ?? null),
|
||||||
|
eventRegistrationPoints: data.eventRegistrationPoints ?? 100,
|
||||||
|
eventFeedbackPoints: data.eventFeedbackPoints ?? 100,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user