Add dotenv package for environment variable management and update pnpm-lock.yaml. Adjust layout in RegisterPage and LoginPage components for improved responsiveness. Enhance AdminPanel with ChallengeManagement section and update navigation links for challenges. Refactor Prisma schema to include Challenge model and related enums.
Some checks failed
Deploy with Docker Compose / deploy (push) Failing after 3m2s
Some checks failed
Deploy with Docker Compose / deploy (push) Failing after 3m2s
This commit is contained in:
152
actions/admin/challenges.ts
Normal file
152
actions/admin/challenges.ts
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
import { auth } from '@/lib/auth'
|
||||||
|
import { challengeService } from '@/services/challenges/challenge.service'
|
||||||
|
import { Role } from '@/prisma/generated/prisma/client'
|
||||||
|
import {
|
||||||
|
ValidationError,
|
||||||
|
NotFoundError,
|
||||||
|
} from '@/services/errors'
|
||||||
|
|
||||||
|
async function checkAdminAccess() {
|
||||||
|
const session = await auth()
|
||||||
|
if (!session?.user || session.user.role !== Role.ADMIN) {
|
||||||
|
throw new Error('Accès refusé - Admin uniquement')
|
||||||
|
}
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function validateChallenge(
|
||||||
|
challengeId: string,
|
||||||
|
winnerId: string,
|
||||||
|
adminComment?: string
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const session = await checkAdminAccess()
|
||||||
|
|
||||||
|
const challenge = await challengeService.validateChallenge(
|
||||||
|
challengeId,
|
||||||
|
session.user.id,
|
||||||
|
winnerId,
|
||||||
|
adminComment
|
||||||
|
)
|
||||||
|
|
||||||
|
revalidatePath('/admin')
|
||||||
|
revalidatePath('/challenges')
|
||||||
|
revalidatePath('/leaderboard')
|
||||||
|
|
||||||
|
return { success: true, message: 'Défi validé avec succès', data: challenge }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Validate challenge error:', error)
|
||||||
|
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
if (error instanceof NotFoundError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
if (error instanceof Error && error.message.includes('Accès refusé')) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Une erreur est survenue lors de la validation du défi' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rejectChallenge(
|
||||||
|
challengeId: string,
|
||||||
|
adminComment?: string
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const session = await checkAdminAccess()
|
||||||
|
|
||||||
|
const challenge = await challengeService.rejectChallenge(
|
||||||
|
challengeId,
|
||||||
|
session.user.id,
|
||||||
|
adminComment
|
||||||
|
)
|
||||||
|
|
||||||
|
revalidatePath('/admin')
|
||||||
|
revalidatePath('/challenges')
|
||||||
|
|
||||||
|
return { success: true, message: 'Défi rejeté', data: challenge }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Reject challenge error:', error)
|
||||||
|
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
if (error instanceof NotFoundError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
if (error instanceof Error && error.message.includes('Accès refusé')) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Une erreur est survenue lors du rejet du défi' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateChallenge(
|
||||||
|
challengeId: string,
|
||||||
|
data: {
|
||||||
|
title?: string
|
||||||
|
description?: string
|
||||||
|
pointsReward?: number
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const session = await checkAdminAccess()
|
||||||
|
|
||||||
|
const challenge = await challengeService.updateChallenge(challengeId, {
|
||||||
|
title: data.title,
|
||||||
|
description: data.description,
|
||||||
|
pointsReward: data.pointsReward,
|
||||||
|
})
|
||||||
|
|
||||||
|
revalidatePath('/admin')
|
||||||
|
revalidatePath('/challenges')
|
||||||
|
|
||||||
|
return { success: true, message: 'Défi mis à jour avec succès', data: challenge }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Update challenge error:', error)
|
||||||
|
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
if (error instanceof NotFoundError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
if (error instanceof Error && error.message.includes('Accès refusé')) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Une erreur est survenue lors de la mise à jour du défi' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteChallenge(challengeId: string) {
|
||||||
|
try {
|
||||||
|
const session = await checkAdminAccess()
|
||||||
|
|
||||||
|
await challengeService.deleteChallenge(challengeId)
|
||||||
|
|
||||||
|
revalidatePath('/admin')
|
||||||
|
revalidatePath('/challenges')
|
||||||
|
|
||||||
|
return { success: true, message: 'Défi supprimé avec succès' }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Delete challenge error:', error)
|
||||||
|
|
||||||
|
if (error instanceof NotFoundError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
if (error instanceof Error && error.message.includes('Accès refusé')) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Une erreur est survenue lors de la suppression du défi' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
112
actions/challenges/create.ts
Normal file
112
actions/challenges/create.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
import { auth } from '@/lib/auth'
|
||||||
|
import { challengeService } from '@/services/challenges/challenge.service'
|
||||||
|
import {
|
||||||
|
ValidationError,
|
||||||
|
NotFoundError,
|
||||||
|
ConflictError,
|
||||||
|
} from '@/services/errors'
|
||||||
|
|
||||||
|
export async function createChallenge(data: {
|
||||||
|
challengedId: string
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
pointsReward?: number
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
const session = await auth()
|
||||||
|
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return { success: false, error: 'Vous devez être connecté pour créer un défi' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const challenge = await challengeService.createChallenge({
|
||||||
|
challengerId: session.user.id,
|
||||||
|
challengedId: data.challengedId,
|
||||||
|
title: data.title,
|
||||||
|
description: data.description,
|
||||||
|
pointsReward: data.pointsReward || 100,
|
||||||
|
})
|
||||||
|
|
||||||
|
revalidatePath('/challenges')
|
||||||
|
revalidatePath('/profile')
|
||||||
|
|
||||||
|
return { success: true, message: 'Défi créé avec succès', data: challenge }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Create challenge error:', error)
|
||||||
|
|
||||||
|
if (error instanceof ValidationError || error instanceof ConflictError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
if (error instanceof NotFoundError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Une erreur est survenue lors de la création du défi' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function acceptChallenge(challengeId: string) {
|
||||||
|
try {
|
||||||
|
const session = await auth()
|
||||||
|
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return { success: false, error: 'Vous devez être connecté pour accepter un défi' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const challenge = await challengeService.acceptChallenge(
|
||||||
|
challengeId,
|
||||||
|
session.user.id
|
||||||
|
)
|
||||||
|
|
||||||
|
revalidatePath('/challenges')
|
||||||
|
revalidatePath('/profile')
|
||||||
|
|
||||||
|
return { success: true, message: 'Défi accepté', data: challenge }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Accept challenge error:', error)
|
||||||
|
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
if (error instanceof NotFoundError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Une erreur est survenue lors de l\'acceptation du défi' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function cancelChallenge(challengeId: string) {
|
||||||
|
try {
|
||||||
|
const session = await auth()
|
||||||
|
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return { success: false, error: 'Vous devez être connecté pour annuler un défi' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const challenge = await challengeService.cancelChallenge(
|
||||||
|
challengeId,
|
||||||
|
session.user.id
|
||||||
|
)
|
||||||
|
|
||||||
|
revalidatePath('/challenges')
|
||||||
|
revalidatePath('/profile')
|
||||||
|
|
||||||
|
return { success: true, message: 'Défi annulé', data: challenge }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Cancel challenge error:', error)
|
||||||
|
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
if (error instanceof NotFoundError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Une erreur est survenue lors de l\'annulation du défi' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
30
app/api/admin/challenges/route.ts
Normal file
30
app/api/admin/challenges/route.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { auth } from "@/lib/auth";
|
||||||
|
import { challengeService } from "@/services/challenges/challenge.service";
|
||||||
|
import { Role } from "@/prisma/generated/prisma/client";
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
try {
|
||||||
|
const session = await auth();
|
||||||
|
|
||||||
|
if (!session?.user || session.user.role !== Role.ADMIN) {
|
||||||
|
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer tous les défis (PENDING et ACCEPTED) pour l'admin
|
||||||
|
const allChallenges = await challengeService.getAllChallenges();
|
||||||
|
// Filtrer pour ne garder que PENDING et ACCEPTED
|
||||||
|
const challenges = allChallenges.filter(
|
||||||
|
(c) => c.status === "PENDING" || c.status === "ACCEPTED"
|
||||||
|
);
|
||||||
|
|
||||||
|
return NextResponse.json(challenges);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching challenges:", error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Erreur lors de la récupération des défis" },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
25
app/api/challenges/route.ts
Normal file
25
app/api/challenges/route.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { auth } from "@/lib/auth";
|
||||||
|
import { challengeService } from "@/services/challenges/challenge.service";
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
try {
|
||||||
|
const session = await auth();
|
||||||
|
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return NextResponse.json({ error: "Vous devez être connecté" }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer tous les défis de l'utilisateur
|
||||||
|
const challenges = await challengeService.getUserChallenges(session.user.id);
|
||||||
|
|
||||||
|
return NextResponse.json(challenges);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching challenges:", error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Erreur lors de la récupération des défis" },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
39
app/api/users/route.ts
Normal file
39
app/api/users/route.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { auth } from "@/lib/auth";
|
||||||
|
import { userService } from "@/services/users/user.service";
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
try {
|
||||||
|
const session = await auth();
|
||||||
|
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return NextResponse.json({ error: "Vous devez être connecté" }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer tous les utilisateurs (pour sélectionner qui défier)
|
||||||
|
const users = await userService.getAllUsers({
|
||||||
|
orderBy: {
|
||||||
|
username: "asc",
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
avatar: true,
|
||||||
|
score: true,
|
||||||
|
level: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filtrer l'utilisateur actuel
|
||||||
|
const otherUsers = users.filter((user) => user.id !== session.user.id);
|
||||||
|
|
||||||
|
return NextResponse.json(otherUsers);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching users:", error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Erreur lors de la récupération des utilisateurs" },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
28
app/challenges/page.tsx
Normal file
28
app/challenges/page.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import { auth } from "@/lib/auth";
|
||||||
|
import { getBackgroundImage } from "@/lib/preferences";
|
||||||
|
import NavigationWrapper from "@/components/navigation/NavigationWrapper";
|
||||||
|
import ChallengesSection from "@/components/challenges/ChallengesSection";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
export default async function ChallengesPage() {
|
||||||
|
const session = await auth();
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
redirect("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
const backgroundImage = await getBackgroundImage(
|
||||||
|
"home",
|
||||||
|
"/got-background.jpg"
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="min-h-screen bg-black relative">
|
||||||
|
<NavigationWrapper />
|
||||||
|
<ChallengesSection backgroundImage={backgroundImage} />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ export default function LoginPage() {
|
|||||||
<Card variant="dark" className="p-8">
|
<Card variant="dark" className="p-8">
|
||||||
<SectionTitle
|
<SectionTitle
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
size="lg"
|
size="md"
|
||||||
className="mb-2 text-center"
|
className="mb-2 text-center"
|
||||||
>
|
>
|
||||||
CONNEXION
|
CONNEXION
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ export default function RegisterPage() {
|
|||||||
<Navigation />
|
<Navigation />
|
||||||
<BackgroundSection backgroundImage="/got-2.jpg" className="pt-24">
|
<BackgroundSection backgroundImage="/got-2.jpg" className="pt-24">
|
||||||
{/* Register Form */}
|
{/* Register Form */}
|
||||||
<div className="w-full max-w-md mx-auto px-8">
|
<div className="w-full max-w-4xl mx-auto px-8">
|
||||||
<Card variant="dark" className="p-8">
|
<Card variant="dark" className="p-8">
|
||||||
<SectionTitle
|
<SectionTitle
|
||||||
variant="gradient"
|
variant="gradient"
|
||||||
@@ -397,7 +397,7 @@ export default function RegisterPage() {
|
|||||||
<label className="block text-sm font-semibold text-gray-300 mb-3 uppercase tracking-wider">
|
<label className="block text-sm font-semibold text-gray-300 mb-3 uppercase tracking-wider">
|
||||||
Classe de Personnage (optionnel)
|
Classe de Personnage (optionnel)
|
||||||
</label>
|
</label>
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||||
{CHARACTER_CLASSES.map((cls) => (
|
{CHARACTER_CLASSES.map((cls) => (
|
||||||
<button
|
<button
|
||||||
key={cls.value}
|
key={cls.value}
|
||||||
@@ -411,16 +411,16 @@ export default function RegisterPage() {
|
|||||||
: cls.value,
|
: cls.value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
className={`p-3 border-2 rounded-lg text-left transition-all ${
|
className={`p-4 border-2 rounded-lg text-left transition-all ${
|
||||||
formData.characterClass === cls.value
|
formData.characterClass === cls.value
|
||||||
? "border-pixel-gold bg-pixel-gold/20"
|
? "border-pixel-gold bg-pixel-gold/20 shadow-lg shadow-pixel-gold/30"
|
||||||
: "border-pixel-gold/30 bg-black/40 hover:border-pixel-gold/50"
|
: "border-pixel-gold/30 bg-black/40 hover:border-pixel-gold/50 hover:bg-black/60"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
<span className="text-xl">{cls.icon}</span>
|
<span className="text-2xl">{cls.icon}</span>
|
||||||
<span
|
<span
|
||||||
className={`font-bold text-xs uppercase tracking-wider ${
|
className={`font-bold text-sm uppercase tracking-wider ${
|
||||||
formData.characterClass === cls.value
|
formData.characterClass === cls.value
|
||||||
? "text-pixel-gold"
|
? "text-pixel-gold"
|
||||||
: "text-white"
|
: "text-white"
|
||||||
@@ -429,6 +429,9 @@ export default function RegisterPage() {
|
|||||||
{cls.name}
|
{cls.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-xs text-gray-400 leading-tight">
|
||||||
|
{cls.desc}
|
||||||
|
</p>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useState } from "react";
|
|||||||
import UserManagement from "@/components/admin/UserManagement";
|
import UserManagement from "@/components/admin/UserManagement";
|
||||||
import EventManagement from "@/components/admin/EventManagement";
|
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 BackgroundPreferences from "@/components/admin/BackgroundPreferences";
|
import BackgroundPreferences from "@/components/admin/BackgroundPreferences";
|
||||||
import { Button, Card, SectionTitle } from "@/components/ui";
|
import { Button, Card, SectionTitle } from "@/components/ui";
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ interface AdminPanelProps {
|
|||||||
initialPreferences: SitePreferences;
|
initialPreferences: SitePreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
type AdminSection = "preferences" | "users" | "events" | "feedbacks";
|
type AdminSection = "preferences" | "users" | "events" | "feedbacks" | "challenges";
|
||||||
|
|
||||||
export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
|
export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
|
||||||
const [activeSection, setActiveSection] =
|
const [activeSection, setActiveSection] =
|
||||||
@@ -67,6 +68,14 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
|
|||||||
>
|
>
|
||||||
Feedbacks
|
Feedbacks
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => setActiveSection("challenges")}
|
||||||
|
variant={activeSection === "challenges" ? "primary" : "secondary"}
|
||||||
|
size="md"
|
||||||
|
className={activeSection === "challenges" ? "bg-pixel-gold/10" : ""}
|
||||||
|
>
|
||||||
|
Défis
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{activeSection === "preferences" && (
|
{activeSection === "preferences" && (
|
||||||
@@ -108,6 +117,15 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
|
|||||||
<FeedbackManagement />
|
<FeedbackManagement />
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{activeSection === "challenges" && (
|
||||||
|
<Card variant="dark" className="p-6">
|
||||||
|
<h2 className="text-2xl font-gaming font-bold mb-6 text-pixel-gold">
|
||||||
|
Gestion des Défis
|
||||||
|
</h2>
|
||||||
|
<ChallengeManagement />
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
504
components/admin/ChallengeManagement.tsx
Normal file
504
components/admin/ChallengeManagement.tsx
Normal file
@@ -0,0 +1,504 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState, useTransition } from "react";
|
||||||
|
import { validateChallenge, rejectChallenge, updateChallenge, deleteChallenge } from "@/actions/admin/challenges";
|
||||||
|
import { Button, Card, Input, Textarea, Alert } from "@/components/ui";
|
||||||
|
import { Avatar } from "@/components/ui";
|
||||||
|
|
||||||
|
interface Challenge {
|
||||||
|
id: string;
|
||||||
|
challenger: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
avatar: string | null;
|
||||||
|
};
|
||||||
|
challenged: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
avatar: string | null;
|
||||||
|
};
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
pointsReward: number;
|
||||||
|
status: string;
|
||||||
|
adminComment: string | null;
|
||||||
|
createdAt: string;
|
||||||
|
acceptedAt: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ChallengeManagement() {
|
||||||
|
const [challenges, setChallenges] = useState<Challenge[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [selectedChallenge, setSelectedChallenge] = useState<Challenge | null>(null);
|
||||||
|
const [editingChallenge, setEditingChallenge] = useState<Challenge | null>(null);
|
||||||
|
const [winnerId, setWinnerId] = useState<string>("");
|
||||||
|
const [adminComment, setAdminComment] = useState("");
|
||||||
|
const [editTitle, setEditTitle] = useState("");
|
||||||
|
const [editDescription, setEditDescription] = useState("");
|
||||||
|
const [editPointsReward, setEditPointsReward] = useState<number>(0);
|
||||||
|
const [isPending, startTransition] = useTransition();
|
||||||
|
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
||||||
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchChallenges();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchChallenges = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/admin/challenges");
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
setChallenges(data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching challenges:", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleValidate = async () => {
|
||||||
|
if (!selectedChallenge || !winnerId) {
|
||||||
|
setErrorMessage("Veuillez sélectionner un gagnant");
|
||||||
|
setTimeout(() => setErrorMessage(null), 5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startTransition(async () => {
|
||||||
|
const result = await validateChallenge(
|
||||||
|
selectedChallenge.id,
|
||||||
|
winnerId,
|
||||||
|
adminComment || undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setSuccessMessage("Défi validé avec succès ! Les points ont été attribués.");
|
||||||
|
setSelectedChallenge(null);
|
||||||
|
setWinnerId("");
|
||||||
|
setAdminComment("");
|
||||||
|
fetchChallenges();
|
||||||
|
setTimeout(() => setSuccessMessage(null), 5000);
|
||||||
|
} else {
|
||||||
|
setErrorMessage(result.error || "Erreur lors de la validation");
|
||||||
|
setTimeout(() => setErrorMessage(null), 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReject = async () => {
|
||||||
|
if (!selectedChallenge) return;
|
||||||
|
|
||||||
|
if (!confirm("Êtes-vous sûr de vouloir rejeter ce défi ?")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startTransition(async () => {
|
||||||
|
const result = await rejectChallenge(
|
||||||
|
selectedChallenge.id,
|
||||||
|
adminComment || undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setSuccessMessage("Défi rejeté");
|
||||||
|
setSelectedChallenge(null);
|
||||||
|
setAdminComment("");
|
||||||
|
fetchChallenges();
|
||||||
|
setTimeout(() => setSuccessMessage(null), 5000);
|
||||||
|
} else {
|
||||||
|
setErrorMessage(result.error || "Erreur lors du rejet");
|
||||||
|
setTimeout(() => setErrorMessage(null), 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = (challenge: Challenge) => {
|
||||||
|
setEditingChallenge(challenge);
|
||||||
|
setEditTitle(challenge.title);
|
||||||
|
setEditDescription(challenge.description);
|
||||||
|
setEditPointsReward(challenge.pointsReward);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdate = async () => {
|
||||||
|
if (!editingChallenge) return;
|
||||||
|
|
||||||
|
startTransition(async () => {
|
||||||
|
const result = await updateChallenge(editingChallenge.id, {
|
||||||
|
title: editTitle,
|
||||||
|
description: editDescription,
|
||||||
|
pointsReward: editPointsReward,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setSuccessMessage("Défi mis à jour avec succès");
|
||||||
|
setEditingChallenge(null);
|
||||||
|
setEditTitle("");
|
||||||
|
setEditDescription("");
|
||||||
|
setEditPointsReward(0);
|
||||||
|
fetchChallenges();
|
||||||
|
setTimeout(() => setSuccessMessage(null), 5000);
|
||||||
|
} else {
|
||||||
|
setErrorMessage(result.error || "Erreur lors de la mise à jour");
|
||||||
|
setTimeout(() => setErrorMessage(null), 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async (challengeId: string) => {
|
||||||
|
if (!confirm("Êtes-vous sûr de vouloir supprimer ce défi ? Cette action est irréversible.")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startTransition(async () => {
|
||||||
|
const result = await deleteChallenge(challengeId);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setSuccessMessage("Défi supprimé avec succès");
|
||||||
|
fetchChallenges();
|
||||||
|
setTimeout(() => setSuccessMessage(null), 5000);
|
||||||
|
} else {
|
||||||
|
setErrorMessage(result.error || "Erreur lors de la suppression");
|
||||||
|
setTimeout(() => setErrorMessage(null), 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="text-center text-pixel-gold py-8">Chargement...</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (challenges.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="text-center text-gray-400 py-8">
|
||||||
|
Aucun défi en attente
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const acceptedChallenges = challenges.filter((c) => c.status === "ACCEPTED");
|
||||||
|
const pendingChallenges = challenges.filter((c) => c.status === "PENDING");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{successMessage && (
|
||||||
|
<Alert variant="success" className="mb-4">
|
||||||
|
{successMessage}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
{errorMessage && (
|
||||||
|
<Alert variant="error" className="mb-4">
|
||||||
|
{errorMessage}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
<div className="text-sm text-gray-400 mb-4">
|
||||||
|
{acceptedChallenges.length} défi{acceptedChallenges.length > 1 ? "s" : ""} en attente de validation
|
||||||
|
{pendingChallenges.length > 0 && (
|
||||||
|
<span className="ml-2">
|
||||||
|
• {pendingChallenges.length} défi{pendingChallenges.length > 1 ? "s" : ""} en attente d'acceptation
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{challenges.map((challenge) => (
|
||||||
|
<Card key={challenge.id} variant="dark" className="p-4">
|
||||||
|
<div className="flex items-start justify-between gap-4">
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="text-lg font-bold text-pixel-gold mb-2">
|
||||||
|
{challenge.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-300 mb-4">{challenge.description}</p>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-4 mb-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Avatar
|
||||||
|
src={challenge.challenger.avatar}
|
||||||
|
username={challenge.challenger.username}
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
<span className="text-sm text-gray-300">
|
||||||
|
{challenge.challenger.username}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-gray-500">VS</span>
|
||||||
|
<Avatar
|
||||||
|
src={challenge.challenged.avatar}
|
||||||
|
username={challenge.challenged.username}
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
<span className="text-sm text-gray-300">
|
||||||
|
{challenge.challenged.username}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-sm text-gray-400">
|
||||||
|
Récompense: <span className="text-pixel-gold font-bold">{challenge.pointsReward} points</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs mt-2">
|
||||||
|
<span className={`px-2 py-1 rounded ${
|
||||||
|
challenge.status === "ACCEPTED"
|
||||||
|
? "bg-green-500/20 text-green-400"
|
||||||
|
: "bg-yellow-500/20 text-yellow-400"
|
||||||
|
}`}>
|
||||||
|
{challenge.status === "ACCEPTED" ? "Accepté" : "En attente d'acceptation"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{challenge.acceptedAt && (
|
||||||
|
<div className="text-xs text-gray-500 mt-2">
|
||||||
|
Accepté le: {new Date(challenge.acceptedAt).toLocaleDateString("fr-FR")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() => handleEdit(challenge)}
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Modifier
|
||||||
|
</Button>
|
||||||
|
{challenge.status === "ACCEPTED" && (
|
||||||
|
<Button
|
||||||
|
onClick={() => setSelectedChallenge(challenge)}
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Valider/Rejeter
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
onClick={() => handleDelete(challenge.id)}
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
className="text-destructive hover:text-destructive"
|
||||||
|
style={{
|
||||||
|
color: "var(--destructive)",
|
||||||
|
borderColor: "var(--destructive)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Supprimer
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Modal de validation */}
|
||||||
|
{selectedChallenge && (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 z-[200] flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedChallenge(null);
|
||||||
|
setWinnerId("");
|
||||||
|
setAdminComment("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
variant="dark"
|
||||||
|
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">
|
||||||
|
Valider/Rejeter le défi
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="mb-6">
|
||||||
|
<h3 className="text-lg font-bold text-gray-300 mb-2">
|
||||||
|
{selectedChallenge.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-400 mb-4">
|
||||||
|
{selectedChallenge.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-4 mb-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Avatar
|
||||||
|
src={selectedChallenge.challenger.avatar}
|
||||||
|
username={selectedChallenge.challenger.username}
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
<span className="text-gray-300">
|
||||||
|
{selectedChallenge.challenger.username}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-gray-500">VS</span>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Avatar
|
||||||
|
src={selectedChallenge.challenged.avatar}
|
||||||
|
username={selectedChallenge.challenged.username}
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
<span className="text-gray-300">
|
||||||
|
{selectedChallenge.challenged.username}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="block text-sm font-bold text-pixel-gold mb-2">
|
||||||
|
Sélectionner le gagnant
|
||||||
|
</label>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<label className="flex items-center gap-2 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="winner"
|
||||||
|
value={selectedChallenge.challenger.id}
|
||||||
|
checked={winnerId === selectedChallenge.challenger.id}
|
||||||
|
onChange={(e) => setWinnerId(e.target.value)}
|
||||||
|
className="w-4 h-4"
|
||||||
|
/>
|
||||||
|
<span className="text-gray-300">
|
||||||
|
{selectedChallenge.challenger.username}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label className="flex items-center gap-2 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="winner"
|
||||||
|
value={selectedChallenge.challenged.id}
|
||||||
|
checked={winnerId === selectedChallenge.challenged.id}
|
||||||
|
onChange={(e) => setWinnerId(e.target.value)}
|
||||||
|
className="w-4 h-4"
|
||||||
|
/>
|
||||||
|
<span className="text-gray-300">
|
||||||
|
{selectedChallenge.challenged.username}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="block text-sm font-bold text-pixel-gold mb-2">
|
||||||
|
Commentaire (optionnel)
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
value={adminComment}
|
||||||
|
onChange={(e) => setAdminComment(e.target.value)}
|
||||||
|
className="w-full p-2 bg-black/60 border border-pixel-gold/30 rounded text-gray-300"
|
||||||
|
rows={3}
|
||||||
|
placeholder="Commentaire pour les joueurs..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<Button
|
||||||
|
onClick={handleValidate}
|
||||||
|
variant="primary"
|
||||||
|
disabled={!winnerId || isPending}
|
||||||
|
className="flex-1"
|
||||||
|
>
|
||||||
|
{isPending ? "Validation..." : "Valider le défi"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleReject}
|
||||||
|
variant="secondary"
|
||||||
|
disabled={isPending}
|
||||||
|
className="flex-1"
|
||||||
|
>
|
||||||
|
{isPending ? "Rejet..." : "Rejeter le défi"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedChallenge(null);
|
||||||
|
setWinnerId("");
|
||||||
|
setAdminComment("");
|
||||||
|
}}
|
||||||
|
variant="secondary"
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Modal d'édition */}
|
||||||
|
{editingChallenge && (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 z-[200] flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm"
|
||||||
|
onClick={() => {
|
||||||
|
setEditingChallenge(null);
|
||||||
|
setEditTitle("");
|
||||||
|
setEditDescription("");
|
||||||
|
setEditPointsReward(0);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
variant="dark"
|
||||||
|
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">
|
||||||
|
<Input
|
||||||
|
id="edit-title"
|
||||||
|
label="Titre"
|
||||||
|
value={editTitle}
|
||||||
|
onChange={(e) => setEditTitle(e.target.value)}
|
||||||
|
required
|
||||||
|
placeholder="Titre du défi"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Textarea
|
||||||
|
id="edit-description"
|
||||||
|
label="Description"
|
||||||
|
value={editDescription}
|
||||||
|
onChange={(e) => setEditDescription(e.target.value)}
|
||||||
|
required
|
||||||
|
rows={4}
|
||||||
|
placeholder="Description du défi"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
id="edit-points"
|
||||||
|
label="Récompense (points)"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
value={editPointsReward}
|
||||||
|
onChange={(e) => setEditPointsReward(parseInt(e.target.value) || 0)}
|
||||||
|
required
|
||||||
|
placeholder="100"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex gap-4 pt-4">
|
||||||
|
<Button
|
||||||
|
onClick={handleUpdate}
|
||||||
|
variant="primary"
|
||||||
|
disabled={isPending || !editTitle || !editDescription || editPointsReward <= 0}
|
||||||
|
className="flex-1"
|
||||||
|
>
|
||||||
|
{isPending ? "Mise à jour..." : "Enregistrer"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setEditingChallenge(null);
|
||||||
|
setEditTitle("");
|
||||||
|
setEditDescription("");
|
||||||
|
setEditPointsReward(0);
|
||||||
|
}}
|
||||||
|
variant="secondary"
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
428
components/challenges/ChallengesSection.tsx
Normal file
428
components/challenges/ChallengesSection.tsx
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState, useTransition } from "react";
|
||||||
|
import { useSession } from "next-auth/react";
|
||||||
|
import { createChallenge, acceptChallenge, cancelChallenge } from "@/actions/challenges/create";
|
||||||
|
import { Button, Card, SectionTitle, Input, Textarea, Alert } from "@/components/ui";
|
||||||
|
import { Avatar } from "@/components/ui";
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
avatar: string | null;
|
||||||
|
score: number;
|
||||||
|
level: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Challenge {
|
||||||
|
id: string;
|
||||||
|
challenger: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
avatar: string | null;
|
||||||
|
};
|
||||||
|
challenged: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
avatar: string | null;
|
||||||
|
};
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
pointsReward: number;
|
||||||
|
status: string;
|
||||||
|
adminComment: string | null;
|
||||||
|
winner?: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
} | null;
|
||||||
|
createdAt: string;
|
||||||
|
acceptedAt: string | null;
|
||||||
|
completedAt: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChallengesSectionProps {
|
||||||
|
backgroundImage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ChallengesSection({ backgroundImage }: ChallengesSectionProps) {
|
||||||
|
const { data: session } = useSession();
|
||||||
|
const [challenges, setChallenges] = useState<Challenge[]>([]);
|
||||||
|
const [users, setUsers] = useState<User[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [showCreateForm, setShowCreateForm] = useState(false);
|
||||||
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
|
// Form state
|
||||||
|
const [challengedId, setChallengedId] = useState("");
|
||||||
|
const [title, setTitle] = useState("");
|
||||||
|
const [description, setDescription] = useState("");
|
||||||
|
const [pointsReward, setPointsReward] = useState(100);
|
||||||
|
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
||||||
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchChallenges();
|
||||||
|
fetchUsers();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchChallenges = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/challenges");
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
setChallenges(data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching challenges:", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchUsers = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/users");
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
setUsers(data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching users:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreateChallenge = () => {
|
||||||
|
if (!challengedId || !title || !description) {
|
||||||
|
setErrorMessage("Veuillez remplir tous les champs");
|
||||||
|
setTimeout(() => setErrorMessage(null), 5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startTransition(async () => {
|
||||||
|
const result = await createChallenge({
|
||||||
|
challengedId,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
pointsReward,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setSuccessMessage("Défi créé avec succès !");
|
||||||
|
setShowCreateForm(false);
|
||||||
|
setChallengedId("");
|
||||||
|
setTitle("");
|
||||||
|
setDescription("");
|
||||||
|
setPointsReward(100);
|
||||||
|
fetchChallenges();
|
||||||
|
setTimeout(() => setSuccessMessage(null), 5000);
|
||||||
|
} else {
|
||||||
|
setErrorMessage(result.error || "Erreur lors de la création du défi");
|
||||||
|
setTimeout(() => setErrorMessage(null), 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAcceptChallenge = (challengeId: string) => {
|
||||||
|
startTransition(async () => {
|
||||||
|
const result = await acceptChallenge(challengeId);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setSuccessMessage("Défi accepté ! En attente de validation admin.");
|
||||||
|
fetchChallenges();
|
||||||
|
setTimeout(() => setSuccessMessage(null), 5000);
|
||||||
|
} else {
|
||||||
|
setErrorMessage(result.error || "Erreur lors de l'acceptation");
|
||||||
|
setTimeout(() => setErrorMessage(null), 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancelChallenge = (challengeId: string) => {
|
||||||
|
if (!confirm("Êtes-vous sûr de vouloir annuler ce défi ?")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startTransition(async () => {
|
||||||
|
const result = await cancelChallenge(challengeId);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setSuccessMessage("Défi annulé");
|
||||||
|
fetchChallenges();
|
||||||
|
setTimeout(() => setSuccessMessage(null), 5000);
|
||||||
|
} else {
|
||||||
|
setErrorMessage(result.error || "Erreur lors de l'annulation");
|
||||||
|
setTimeout(() => setErrorMessage(null), 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusLabel = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case "PENDING":
|
||||||
|
return "En attente d'acceptation";
|
||||||
|
case "ACCEPTED":
|
||||||
|
return "Accepté - En attente de validation admin";
|
||||||
|
case "COMPLETED":
|
||||||
|
return "Complété";
|
||||||
|
case "REJECTED":
|
||||||
|
return "Rejeté";
|
||||||
|
case "CANCELLED":
|
||||||
|
return "Annulé";
|
||||||
|
default:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusColor = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case "PENDING":
|
||||||
|
return "text-yellow-400";
|
||||||
|
case "ACCEPTED":
|
||||||
|
return "text-blue-400";
|
||||||
|
case "COMPLETED":
|
||||||
|
return "text-green-400";
|
||||||
|
case "REJECTED":
|
||||||
|
return "text-red-400";
|
||||||
|
case "CANCELLED":
|
||||||
|
return "text-gray-400";
|
||||||
|
default:
|
||||||
|
return "text-gray-300";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className="relative w-full min-h-screen flex flex-col items-center overflow-hidden pt-24 pb-16"
|
||||||
|
style={{
|
||||||
|
backgroundImage: `url(${backgroundImage})`,
|
||||||
|
backgroundSize: "cover",
|
||||||
|
backgroundPosition: "center",
|
||||||
|
backgroundRepeat: "no-repeat",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm"></div>
|
||||||
|
|
||||||
|
<div className="relative z-10 w-full max-w-6xl mx-auto px-8 py-16">
|
||||||
|
<SectionTitle variant="gradient" size="md" className="mb-8 text-center">
|
||||||
|
DÉFIS ENTRE JOUEURS
|
||||||
|
</SectionTitle>
|
||||||
|
|
||||||
|
{successMessage && (
|
||||||
|
<Alert variant="success" className="mb-4">
|
||||||
|
{successMessage}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
{errorMessage && (
|
||||||
|
<Alert variant="error" className="mb-4">
|
||||||
|
{errorMessage}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="mb-6 flex justify-center">
|
||||||
|
<Button
|
||||||
|
onClick={() => setShowCreateForm(!showCreateForm)}
|
||||||
|
variant="primary"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
{showCreateForm ? "Annuler" : "Créer un défi"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Create Form */}
|
||||||
|
{showCreateForm && (
|
||||||
|
<Card variant="dark" className="p-6 mb-8">
|
||||||
|
<h2 className="text-xl font-bold text-pixel-gold mb-4">
|
||||||
|
Créer un nouveau défi
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-bold text-pixel-gold mb-2">
|
||||||
|
Défier qui ?
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={challengedId}
|
||||||
|
onChange={(e) => setChallengedId(e.target.value)}
|
||||||
|
className="w-full p-2 bg-black/60 border border-pixel-gold/30 rounded text-gray-300"
|
||||||
|
>
|
||||||
|
<option value="">Sélectionner un joueur</option>
|
||||||
|
{users.map((user) => (
|
||||||
|
<option key={user.id} value={user.id}>
|
||||||
|
{user.username} (Lv.{user.level} - {user.score} pts)
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-bold text-pixel-gold mb-2">
|
||||||
|
Titre du défi
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
value={title}
|
||||||
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
|
placeholder="Ex: Qui participera à plus d'événements ce mois ?"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-bold text-pixel-gold mb-2">
|
||||||
|
Description
|
||||||
|
</label>
|
||||||
|
<Textarea
|
||||||
|
value={description}
|
||||||
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
|
placeholder="Décrivez les règles du défi..."
|
||||||
|
rows={4}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-bold text-pixel-gold mb-2">
|
||||||
|
Points à gagner (défaut: 100)
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={pointsReward}
|
||||||
|
onChange={(e) => setPointsReward(parseInt(e.target.value) || 100)}
|
||||||
|
min={1}
|
||||||
|
max={1000}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={handleCreateChallenge}
|
||||||
|
variant="primary"
|
||||||
|
disabled={isPending || !challengedId || !title || !description}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{isPending ? "Création..." : "Créer le défi"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Challenges List */}
|
||||||
|
{loading ? (
|
||||||
|
<div className="text-center text-pixel-gold py-8">Chargement...</div>
|
||||||
|
) : challenges.length === 0 ? (
|
||||||
|
<Card variant="dark" className="p-6 text-center">
|
||||||
|
<p className="text-gray-400">
|
||||||
|
Vous n'avez aucun défi pour le moment.
|
||||||
|
</p>
|
||||||
|
</Card>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{challenges.map((challenge) => {
|
||||||
|
const currentUserId = session?.user?.id;
|
||||||
|
const isChallenger = challenge.challenger.id === currentUserId;
|
||||||
|
const isChallenged = challenge.challenged.id === currentUserId;
|
||||||
|
const canAccept = challenge.status === "PENDING" && isChallenged;
|
||||||
|
const canCancel =
|
||||||
|
(challenge.status === "PENDING" || challenge.status === "ACCEPTED") &&
|
||||||
|
(isChallenger || isChallenged);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card key={challenge.id} variant="dark" className="p-6">
|
||||||
|
<div className="flex items-start justify-between gap-4">
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<h3 className="text-lg font-bold text-pixel-gold">
|
||||||
|
{challenge.title}
|
||||||
|
</h3>
|
||||||
|
<span
|
||||||
|
className={`text-xs px-2 py-1 rounded ${getStatusColor(
|
||||||
|
challenge.status
|
||||||
|
)} bg-black/40`}
|
||||||
|
>
|
||||||
|
{getStatusLabel(challenge.status)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-gray-300 mb-4">{challenge.description}</p>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-4 mb-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Avatar
|
||||||
|
src={challenge.challenger.avatar}
|
||||||
|
username={challenge.challenger.username}
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
<span className="text-sm text-gray-300">
|
||||||
|
{challenge.challenger.username}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-gray-500">VS</span>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Avatar
|
||||||
|
src={challenge.challenged.avatar}
|
||||||
|
username={challenge.challenged.username}
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
<span className="text-sm text-gray-300">
|
||||||
|
{challenge.challenged.username}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-sm text-gray-400">
|
||||||
|
Récompense:{" "}
|
||||||
|
<span className="text-pixel-gold font-bold">
|
||||||
|
{challenge.pointsReward} points
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{challenge.winner && (
|
||||||
|
<div className="text-sm text-green-400 mt-2">
|
||||||
|
🏆 Gagnant: {challenge.winner.username}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{challenge.adminComment && (
|
||||||
|
<div className="text-xs text-gray-500 mt-2 italic">
|
||||||
|
Admin: {challenge.adminComment}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="text-xs text-gray-500 mt-2">
|
||||||
|
Créé le: {new Date(challenge.createdAt).toLocaleDateString("fr-FR")}
|
||||||
|
{challenge.acceptedAt &&
|
||||||
|
` • Accepté le: ${new Date(challenge.acceptedAt).toLocaleDateString("fr-FR")}`}
|
||||||
|
{challenge.completedAt &&
|
||||||
|
` • Complété le: ${new Date(challenge.completedAt).toLocaleDateString("fr-FR")}`}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{canAccept && (
|
||||||
|
<Button
|
||||||
|
onClick={() => handleAcceptChallenge(challenge.id)}
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
Accepter
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{canCancel && (
|
||||||
|
<Button
|
||||||
|
onClick={() => handleCancelChallenge(challenge.id)}
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -113,6 +113,21 @@ export default function Navigation({
|
|||||||
>
|
>
|
||||||
LEADERBOARD
|
LEADERBOARD
|
||||||
</Link>
|
</Link>
|
||||||
|
{isAuthenticated && (
|
||||||
|
<Link
|
||||||
|
href="/challenges"
|
||||||
|
className="transition text-xs font-normal uppercase tracking-widest"
|
||||||
|
style={{ color: "var(--foreground)" }}
|
||||||
|
onMouseEnter={(e) =>
|
||||||
|
(e.currentTarget.style.color = "var(--accent-color)")
|
||||||
|
}
|
||||||
|
onMouseLeave={(e) =>
|
||||||
|
(e.currentTarget.style.color = "var(--foreground)")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
DÉFIS
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<Link
|
<Link
|
||||||
href="/admin"
|
href="/admin"
|
||||||
@@ -271,6 +286,22 @@ export default function Navigation({
|
|||||||
>
|
>
|
||||||
LEADERBOARD
|
LEADERBOARD
|
||||||
</Link>
|
</Link>
|
||||||
|
{isAuthenticated && (
|
||||||
|
<Link
|
||||||
|
href="/challenges"
|
||||||
|
onClick={() => setIsMenuOpen(false)}
|
||||||
|
className="transition text-xs font-normal uppercase tracking-widest py-2"
|
||||||
|
style={{ color: "var(--foreground)" }}
|
||||||
|
onMouseEnter={(e) =>
|
||||||
|
(e.currentTarget.style.color = "var(--accent-color)")
|
||||||
|
}
|
||||||
|
onMouseLeave={(e) =>
|
||||||
|
(e.currentTarget.style.color = "var(--foreground)")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
DÉFIS
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<Link
|
<Link
|
||||||
href="/admin"
|
href="/admin"
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ export default function Alert({
|
|||||||
color: "var(--success)",
|
color: "var(--success)",
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
backgroundColor: "color-mix(in srgb, var(--destructive) 20%, transparent)",
|
backgroundColor:
|
||||||
|
"color-mix(in srgb, var(--destructive) 20%, transparent)",
|
||||||
borderColor: "color-mix(in srgb, var(--destructive) 50%, transparent)",
|
borderColor: "color-mix(in srgb, var(--destructive) 50%, transparent)",
|
||||||
color: "var(--destructive)",
|
color: "var(--destructive)",
|
||||||
},
|
},
|
||||||
@@ -53,4 +54,3 @@ export default function Alert({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
||||||
"@typescript-eslint/parser": "^8.49.0",
|
"@typescript-eslint/parser": "^8.49.0",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
|
"dotenv": "^17.2.3",
|
||||||
"eslint": "^9.39.1",
|
"eslint": "^9.39.1",
|
||||||
"eslint-config-next": "^16.0.8",
|
"eslint-config-next": "^16.0.8",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
|
|||||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -60,6 +60,9 @@ importers:
|
|||||||
autoprefixer:
|
autoprefixer:
|
||||||
specifier: ^10.4.19
|
specifier: ^10.4.19
|
||||||
version: 10.4.22(postcss@8.5.6)
|
version: 10.4.22(postcss@8.5.6)
|
||||||
|
dotenv:
|
||||||
|
specifier: ^17.2.3
|
||||||
|
version: 17.2.3
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^9.39.1
|
specifier: ^9.39.1
|
||||||
version: 9.39.1(jiti@1.21.7)
|
version: 9.39.1(jiti@1.21.7)
|
||||||
@@ -1235,6 +1238,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
|
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
dotenv@17.2.3:
|
||||||
|
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
dunder-proto@1.0.1:
|
dunder-proto@1.0.1:
|
||||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3693,6 +3700,8 @@ snapshots:
|
|||||||
|
|
||||||
dotenv@16.6.1: {}
|
dotenv@16.6.1: {}
|
||||||
|
|
||||||
|
dotenv@17.2.3: {}
|
||||||
|
|
||||||
dunder-proto@1.0.1:
|
dunder-proto@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind-apply-helpers: 1.0.2
|
call-bind-apply-helpers: 1.0.2
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// biome-ignore-all lint: generated file
|
// biome-ignore-all lint: generated file
|
||||||
@@ -12,37 +13,42 @@
|
|||||||
* 🟢 You can import this file directly.
|
* 🟢 You can import this file directly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as Prisma from "./internal/prismaNamespaceBrowser";
|
import * as Prisma from './internal/prismaNamespaceBrowser'
|
||||||
export { Prisma };
|
export { Prisma }
|
||||||
export * as $Enums from "./enums";
|
export * as $Enums from './enums'
|
||||||
export * from "./enums";
|
export * from './enums';
|
||||||
/**
|
/**
|
||||||
* Model User
|
* Model User
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type User = Prisma.UserModel;
|
export type User = Prisma.UserModel
|
||||||
/**
|
/**
|
||||||
* Model UserPreferences
|
* Model UserPreferences
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type UserPreferences = Prisma.UserPreferencesModel;
|
export type UserPreferences = Prisma.UserPreferencesModel
|
||||||
/**
|
/**
|
||||||
* Model Event
|
* Model Event
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type Event = Prisma.EventModel;
|
export type Event = Prisma.EventModel
|
||||||
/**
|
/**
|
||||||
* Model EventRegistration
|
* Model EventRegistration
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type EventRegistration = Prisma.EventRegistrationModel;
|
export type EventRegistration = Prisma.EventRegistrationModel
|
||||||
/**
|
/**
|
||||||
* Model EventFeedback
|
* Model EventFeedback
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type EventFeedback = Prisma.EventFeedbackModel;
|
export type EventFeedback = Prisma.EventFeedbackModel
|
||||||
/**
|
/**
|
||||||
* Model SitePreferences
|
* Model SitePreferences
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type SitePreferences = Prisma.SitePreferencesModel;
|
export type SitePreferences = Prisma.SitePreferencesModel
|
||||||
|
/**
|
||||||
|
* Model Challenge
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Challenge = Prisma.ChallengeModel
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// biome-ignore-all lint: generated file
|
// biome-ignore-all lint: generated file
|
||||||
@@ -9,18 +10,18 @@
|
|||||||
* 🟢 You can import this file directly.
|
* 🟢 You can import this file directly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as process from "node:process";
|
import * as process from 'node:process'
|
||||||
import * as path from "node:path";
|
import * as path from 'node:path'
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from 'node:url'
|
||||||
globalThis["__dirname"] = path.dirname(fileURLToPath(import.meta.url));
|
globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
import * as runtime from "@prisma/client/runtime/client";
|
import * as runtime from "@prisma/client/runtime/client"
|
||||||
import * as $Enums from "./enums";
|
import * as $Enums from "./enums"
|
||||||
import * as $Class from "./internal/class";
|
import * as $Class from "./internal/class"
|
||||||
import * as Prisma from "./internal/prismaNamespace";
|
import * as Prisma from "./internal/prismaNamespace"
|
||||||
|
|
||||||
export * as $Enums from "./enums";
|
export * as $Enums from './enums'
|
||||||
export * from "./enums";
|
export * from "./enums"
|
||||||
/**
|
/**
|
||||||
* ## Prisma Client
|
* ## Prisma Client
|
||||||
*
|
*
|
||||||
@@ -34,43 +35,42 @@ export * from "./enums";
|
|||||||
*
|
*
|
||||||
* Read more in our [docs](https://pris.ly/d/client).
|
* Read more in our [docs](https://pris.ly/d/client).
|
||||||
*/
|
*/
|
||||||
export const PrismaClient = $Class.getPrismaClientClass();
|
export const PrismaClient = $Class.getPrismaClientClass()
|
||||||
export type PrismaClient<
|
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
||||||
LogOpts extends Prisma.LogLevel = never,
|
export { Prisma }
|
||||||
OmitOpts extends Prisma.PrismaClientOptions["omit"] =
|
|
||||||
Prisma.PrismaClientOptions["omit"],
|
|
||||||
ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
|
||||||
runtime.Types.Extensions.DefaultArgs,
|
|
||||||
> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>;
|
|
||||||
export { Prisma };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model User
|
* Model User
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type User = Prisma.UserModel;
|
export type User = Prisma.UserModel
|
||||||
/**
|
/**
|
||||||
* Model UserPreferences
|
* Model UserPreferences
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type UserPreferences = Prisma.UserPreferencesModel;
|
export type UserPreferences = Prisma.UserPreferencesModel
|
||||||
/**
|
/**
|
||||||
* Model Event
|
* Model Event
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type Event = Prisma.EventModel;
|
export type Event = Prisma.EventModel
|
||||||
/**
|
/**
|
||||||
* Model EventRegistration
|
* Model EventRegistration
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type EventRegistration = Prisma.EventRegistrationModel;
|
export type EventRegistration = Prisma.EventRegistrationModel
|
||||||
/**
|
/**
|
||||||
* Model EventFeedback
|
* Model EventFeedback
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type EventFeedback = Prisma.EventFeedbackModel;
|
export type EventFeedback = Prisma.EventFeedbackModel
|
||||||
/**
|
/**
|
||||||
* Model SitePreferences
|
* Model SitePreferences
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type SitePreferences = Prisma.SitePreferencesModel;
|
export type SitePreferences = Prisma.SitePreferencesModel
|
||||||
|
/**
|
||||||
|
* Model Challenge
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Challenge = Prisma.ChallengeModel
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// biome-ignore-all lint: generated file
|
// biome-ignore-all lint: generated file
|
||||||
@@ -8,461 +9,504 @@
|
|||||||
* 🟢 You can import this file directly.
|
* 🟢 You can import this file directly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type * as runtime from "@prisma/client/runtime/client";
|
import type * as runtime from "@prisma/client/runtime/client"
|
||||||
import * as $Enums from "./enums";
|
import * as $Enums from "./enums"
|
||||||
import type * as Prisma from "./internal/prismaNamespace";
|
import type * as Prisma from "./internal/prismaNamespace"
|
||||||
|
|
||||||
|
|
||||||
export type StringFilter<$PrismaModel = never> = {
|
export type StringFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[];
|
in?: string[]
|
||||||
notIn?: string[];
|
notIn?: string[]
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedStringFilter<$PrismaModel> | string;
|
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||||
};
|
}
|
||||||
|
|
||||||
export type EnumRoleFilter<$PrismaModel = never> = {
|
export type EnumRoleFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.Role | Prisma.EnumRoleFieldRefInput<$PrismaModel>;
|
equals?: $Enums.Role | Prisma.EnumRoleFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.Role[];
|
in?: $Enums.Role[]
|
||||||
notIn?: $Enums.Role[];
|
notIn?: $Enums.Role[]
|
||||||
not?: Prisma.NestedEnumRoleFilter<$PrismaModel> | $Enums.Role;
|
not?: Prisma.NestedEnumRoleFilter<$PrismaModel> | $Enums.Role
|
||||||
};
|
}
|
||||||
|
|
||||||
export type IntFilter<$PrismaModel = never> = {
|
export type IntFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[];
|
in?: number[]
|
||||||
notIn?: number[];
|
notIn?: number[]
|
||||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedIntFilter<$PrismaModel> | number;
|
not?: Prisma.NestedIntFilter<$PrismaModel> | number
|
||||||
};
|
}
|
||||||
|
|
||||||
export type StringNullableFilter<$PrismaModel = never> = {
|
export type StringNullableFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null;
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
in?: string[] | null;
|
in?: string[] | null
|
||||||
notIn?: string[] | null;
|
notIn?: string[] | null
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null;
|
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||||
};
|
}
|
||||||
|
|
||||||
export type EnumCharacterClassNullableFilter<$PrismaModel = never> = {
|
|
||||||
equals?:
|
|
||||||
| $Enums.CharacterClass
|
|
||||||
| Prisma.EnumCharacterClassFieldRefInput<$PrismaModel>
|
|
||||||
| null;
|
|
||||||
in?: $Enums.CharacterClass[] | null;
|
|
||||||
notIn?: $Enums.CharacterClass[] | null;
|
|
||||||
not?:
|
|
||||||
| Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel>
|
|
||||||
| $Enums.CharacterClass
|
|
||||||
| null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DateTimeFilter<$PrismaModel = never> = {
|
export type DateTimeFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[];
|
in?: Date[] | string[]
|
||||||
notIn?: Date[] | string[];
|
notIn?: Date[] | string[]
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string;
|
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export type EnumCharacterClassNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: $Enums.CharacterClass | Prisma.EnumCharacterClassFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: $Enums.CharacterClass[] | null
|
||||||
|
notIn?: $Enums.CharacterClass[] | null
|
||||||
|
not?: Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel> | $Enums.CharacterClass | null
|
||||||
|
}
|
||||||
|
|
||||||
export type SortOrderInput = {
|
export type SortOrderInput = {
|
||||||
sort: Prisma.SortOrder;
|
sort: Prisma.SortOrder
|
||||||
nulls?: Prisma.NullsOrder;
|
nulls?: Prisma.NullsOrder
|
||||||
};
|
}
|
||||||
|
|
||||||
export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[];
|
in?: string[]
|
||||||
notIn?: string[];
|
notIn?: string[]
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string;
|
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>;
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedStringFilter<$PrismaModel>;
|
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedStringFilter<$PrismaModel>;
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
};
|
}
|
||||||
|
|
||||||
export type EnumRoleWithAggregatesFilter<$PrismaModel = never> = {
|
export type EnumRoleWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.Role | Prisma.EnumRoleFieldRefInput<$PrismaModel>;
|
equals?: $Enums.Role | Prisma.EnumRoleFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.Role[];
|
in?: $Enums.Role[]
|
||||||
notIn?: $Enums.Role[];
|
notIn?: $Enums.Role[]
|
||||||
not?: Prisma.NestedEnumRoleWithAggregatesFilter<$PrismaModel> | $Enums.Role;
|
not?: Prisma.NestedEnumRoleWithAggregatesFilter<$PrismaModel> | $Enums.Role
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>;
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedEnumRoleFilter<$PrismaModel>;
|
_min?: Prisma.NestedEnumRoleFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedEnumRoleFilter<$PrismaModel>;
|
_max?: Prisma.NestedEnumRoleFilter<$PrismaModel>
|
||||||
};
|
}
|
||||||
|
|
||||||
export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[];
|
in?: number[]
|
||||||
notIn?: number[];
|
notIn?: number[]
|
||||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number;
|
not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>;
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_avg?: Prisma.NestedFloatFilter<$PrismaModel>;
|
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
|
||||||
_sum?: Prisma.NestedIntFilter<$PrismaModel>;
|
_sum?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedIntFilter<$PrismaModel>;
|
_min?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedIntFilter<$PrismaModel>;
|
_max?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
};
|
}
|
||||||
|
|
||||||
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null;
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
in?: string[] | null;
|
in?: string[] | null
|
||||||
notIn?: string[] | null;
|
notIn?: string[] | null
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?:
|
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||||
| Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel>
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
| string
|
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
| null;
|
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
}
|
||||||
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>;
|
|
||||||
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type EnumCharacterClassNullableWithAggregatesFilter<
|
|
||||||
$PrismaModel = never,
|
|
||||||
> = {
|
|
||||||
equals?:
|
|
||||||
| $Enums.CharacterClass
|
|
||||||
| Prisma.EnumCharacterClassFieldRefInput<$PrismaModel>
|
|
||||||
| null;
|
|
||||||
in?: $Enums.CharacterClass[] | null;
|
|
||||||
notIn?: $Enums.CharacterClass[] | null;
|
|
||||||
not?:
|
|
||||||
| Prisma.NestedEnumCharacterClassNullableWithAggregatesFilter<$PrismaModel>
|
|
||||||
| $Enums.CharacterClass
|
|
||||||
| null;
|
|
||||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
|
||||||
_min?: Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel>;
|
|
||||||
_max?: Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[];
|
in?: Date[] | string[]
|
||||||
notIn?: Date[] | string[];
|
notIn?: Date[] | string[]
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string;
|
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>;
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>;
|
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>;
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export type EnumCharacterClassNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: $Enums.CharacterClass | Prisma.EnumCharacterClassFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: $Enums.CharacterClass[] | null
|
||||||
|
notIn?: $Enums.CharacterClass[] | null
|
||||||
|
not?: Prisma.NestedEnumCharacterClassNullableWithAggregatesFilter<$PrismaModel> | $Enums.CharacterClass | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type EnumEventTypeFilter<$PrismaModel = never> = {
|
export type EnumEventTypeFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.EventType | Prisma.EnumEventTypeFieldRefInput<$PrismaModel>;
|
equals?: $Enums.EventType | Prisma.EnumEventTypeFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.EventType[];
|
in?: $Enums.EventType[]
|
||||||
notIn?: $Enums.EventType[];
|
notIn?: $Enums.EventType[]
|
||||||
not?: Prisma.NestedEnumEventTypeFilter<$PrismaModel> | $Enums.EventType;
|
not?: Prisma.NestedEnumEventTypeFilter<$PrismaModel> | $Enums.EventType
|
||||||
};
|
}
|
||||||
|
|
||||||
export type IntNullableFilter<$PrismaModel = never> = {
|
export type IntNullableFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null;
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||||
in?: number[] | null;
|
in?: number[] | null
|
||||||
notIn?: number[] | null;
|
notIn?: number[] | null
|
||||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null;
|
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
||||||
};
|
}
|
||||||
|
|
||||||
export type EnumEventTypeWithAggregatesFilter<$PrismaModel = never> = {
|
export type EnumEventTypeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.EventType | Prisma.EnumEventTypeFieldRefInput<$PrismaModel>;
|
equals?: $Enums.EventType | Prisma.EnumEventTypeFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.EventType[];
|
in?: $Enums.EventType[]
|
||||||
notIn?: $Enums.EventType[];
|
notIn?: $Enums.EventType[]
|
||||||
not?:
|
not?: Prisma.NestedEnumEventTypeWithAggregatesFilter<$PrismaModel> | $Enums.EventType
|
||||||
| Prisma.NestedEnumEventTypeWithAggregatesFilter<$PrismaModel>
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
| $Enums.EventType;
|
_min?: Prisma.NestedEnumEventTypeFilter<$PrismaModel>
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>;
|
_max?: Prisma.NestedEnumEventTypeFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedEnumEventTypeFilter<$PrismaModel>;
|
}
|
||||||
_max?: Prisma.NestedEnumEventTypeFilter<$PrismaModel>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type IntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
export type IntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null;
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||||
in?: number[] | null;
|
in?: number[] | null
|
||||||
notIn?: number[] | null;
|
notIn?: number[] | null
|
||||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
not?:
|
not?: Prisma.NestedIntNullableWithAggregatesFilter<$PrismaModel> | number | null
|
||||||
| Prisma.NestedIntNullableWithAggregatesFilter<$PrismaModel>
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
| number
|
_avg?: Prisma.NestedFloatNullableFilter<$PrismaModel>
|
||||||
| null;
|
_sum?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
_min?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
_avg?: Prisma.NestedFloatNullableFilter<$PrismaModel>;
|
_max?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
_sum?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
}
|
||||||
_min?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
|
||||||
_max?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
export type EnumChallengeStatusFilter<$PrismaModel = never> = {
|
||||||
};
|
equals?: $Enums.ChallengeStatus | Prisma.EnumChallengeStatusFieldRefInput<$PrismaModel>
|
||||||
|
in?: $Enums.ChallengeStatus[]
|
||||||
|
notIn?: $Enums.ChallengeStatus[]
|
||||||
|
not?: Prisma.NestedEnumChallengeStatusFilter<$PrismaModel> | $Enums.ChallengeStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DateTimeNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | null
|
||||||
|
notIn?: Date[] | string[] | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EnumChallengeStatusWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: $Enums.ChallengeStatus | Prisma.EnumChallengeStatusFieldRefInput<$PrismaModel>
|
||||||
|
in?: $Enums.ChallengeStatus[]
|
||||||
|
notIn?: $Enums.ChallengeStatus[]
|
||||||
|
not?: Prisma.NestedEnumChallengeStatusWithAggregatesFilter<$PrismaModel> | $Enums.ChallengeStatus
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedEnumChallengeStatusFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedEnumChallengeStatusFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | null
|
||||||
|
notIn?: Date[] | string[] | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedStringFilter<$PrismaModel = never> = {
|
export type NestedStringFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[];
|
in?: string[]
|
||||||
notIn?: string[];
|
notIn?: string[]
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedStringFilter<$PrismaModel> | string;
|
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NestedEnumRoleFilter<$PrismaModel = never> = {
|
export type NestedEnumRoleFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.Role | Prisma.EnumRoleFieldRefInput<$PrismaModel>;
|
equals?: $Enums.Role | Prisma.EnumRoleFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.Role[];
|
in?: $Enums.Role[]
|
||||||
notIn?: $Enums.Role[];
|
notIn?: $Enums.Role[]
|
||||||
not?: Prisma.NestedEnumRoleFilter<$PrismaModel> | $Enums.Role;
|
not?: Prisma.NestedEnumRoleFilter<$PrismaModel> | $Enums.Role
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NestedIntFilter<$PrismaModel = never> = {
|
export type NestedIntFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[];
|
in?: number[]
|
||||||
notIn?: number[];
|
notIn?: number[]
|
||||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedIntFilter<$PrismaModel> | number;
|
not?: Prisma.NestedIntFilter<$PrismaModel> | number
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NestedStringNullableFilter<$PrismaModel = never> = {
|
export type NestedStringNullableFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null;
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
in?: string[] | null;
|
in?: string[] | null
|
||||||
notIn?: string[] | null;
|
notIn?: string[] | null
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null;
|
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NestedEnumCharacterClassNullableFilter<$PrismaModel = never> = {
|
|
||||||
equals?:
|
|
||||||
| $Enums.CharacterClass
|
|
||||||
| Prisma.EnumCharacterClassFieldRefInput<$PrismaModel>
|
|
||||||
| null;
|
|
||||||
in?: $Enums.CharacterClass[] | null;
|
|
||||||
notIn?: $Enums.CharacterClass[] | null;
|
|
||||||
not?:
|
|
||||||
| Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel>
|
|
||||||
| $Enums.CharacterClass
|
|
||||||
| null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type NestedDateTimeFilter<$PrismaModel = never> = {
|
export type NestedDateTimeFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[];
|
in?: Date[] | string[]
|
||||||
notIn?: Date[] | string[];
|
notIn?: Date[] | string[]
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string;
|
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export type NestedEnumCharacterClassNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: $Enums.CharacterClass | Prisma.EnumCharacterClassFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: $Enums.CharacterClass[] | null
|
||||||
|
notIn?: $Enums.CharacterClass[] | null
|
||||||
|
not?: Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel> | $Enums.CharacterClass | null
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[];
|
in?: string[]
|
||||||
notIn?: string[];
|
notIn?: string[]
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string;
|
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>;
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedStringFilter<$PrismaModel>;
|
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedStringFilter<$PrismaModel>;
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NestedEnumRoleWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedEnumRoleWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.Role | Prisma.EnumRoleFieldRefInput<$PrismaModel>;
|
equals?: $Enums.Role | Prisma.EnumRoleFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.Role[];
|
in?: $Enums.Role[]
|
||||||
notIn?: $Enums.Role[];
|
notIn?: $Enums.Role[]
|
||||||
not?: Prisma.NestedEnumRoleWithAggregatesFilter<$PrismaModel> | $Enums.Role;
|
not?: Prisma.NestedEnumRoleWithAggregatesFilter<$PrismaModel> | $Enums.Role
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>;
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedEnumRoleFilter<$PrismaModel>;
|
_min?: Prisma.NestedEnumRoleFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedEnumRoleFilter<$PrismaModel>;
|
_max?: Prisma.NestedEnumRoleFilter<$PrismaModel>
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[];
|
in?: number[]
|
||||||
notIn?: number[];
|
notIn?: number[]
|
||||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number;
|
not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>;
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_avg?: Prisma.NestedFloatFilter<$PrismaModel>;
|
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
|
||||||
_sum?: Prisma.NestedIntFilter<$PrismaModel>;
|
_sum?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedIntFilter<$PrismaModel>;
|
_min?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedIntFilter<$PrismaModel>;
|
_max?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NestedFloatFilter<$PrismaModel = never> = {
|
export type NestedFloatFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel>;
|
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
in?: number[];
|
in?: number[]
|
||||||
notIn?: number[];
|
notIn?: number[]
|
||||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>;
|
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>;
|
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>;
|
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
gte?: number | Prisma.FloatFieldRefInput<$PrismaModel>;
|
gte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedFloatFilter<$PrismaModel> | number;
|
not?: Prisma.NestedFloatFilter<$PrismaModel> | number
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null;
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
in?: string[] | null;
|
in?: string[] | null
|
||||||
notIn?: string[] | null;
|
notIn?: string[] | null
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>;
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?:
|
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||||
| Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel>
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
| string
|
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
| null;
|
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
}
|
||||||
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>;
|
|
||||||
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type NestedIntNullableFilter<$PrismaModel = never> = {
|
export type NestedIntNullableFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null;
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||||
in?: number[] | null;
|
in?: number[] | null
|
||||||
notIn?: number[] | null;
|
notIn?: number[] | null
|
||||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null;
|
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NestedEnumCharacterClassNullableWithAggregatesFilter<
|
|
||||||
$PrismaModel = never,
|
|
||||||
> = {
|
|
||||||
equals?:
|
|
||||||
| $Enums.CharacterClass
|
|
||||||
| Prisma.EnumCharacterClassFieldRefInput<$PrismaModel>
|
|
||||||
| null;
|
|
||||||
in?: $Enums.CharacterClass[] | null;
|
|
||||||
notIn?: $Enums.CharacterClass[] | null;
|
|
||||||
not?:
|
|
||||||
| Prisma.NestedEnumCharacterClassNullableWithAggregatesFilter<$PrismaModel>
|
|
||||||
| $Enums.CharacterClass
|
|
||||||
| null;
|
|
||||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
|
||||||
_min?: Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel>;
|
|
||||||
_max?: Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
in?: Date[] | string[];
|
in?: Date[] | string[]
|
||||||
notIn?: Date[] | string[];
|
notIn?: Date[] | string[]
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>;
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string;
|
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>;
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>;
|
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>;
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export type NestedEnumCharacterClassNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: $Enums.CharacterClass | Prisma.EnumCharacterClassFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: $Enums.CharacterClass[] | null
|
||||||
|
notIn?: $Enums.CharacterClass[] | null
|
||||||
|
not?: Prisma.NestedEnumCharacterClassNullableWithAggregatesFilter<$PrismaModel> | $Enums.CharacterClass | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedEnumCharacterClassNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedEnumEventTypeFilter<$PrismaModel = never> = {
|
export type NestedEnumEventTypeFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.EventType | Prisma.EnumEventTypeFieldRefInput<$PrismaModel>;
|
equals?: $Enums.EventType | Prisma.EnumEventTypeFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.EventType[];
|
in?: $Enums.EventType[]
|
||||||
notIn?: $Enums.EventType[];
|
notIn?: $Enums.EventType[]
|
||||||
not?: Prisma.NestedEnumEventTypeFilter<$PrismaModel> | $Enums.EventType;
|
not?: Prisma.NestedEnumEventTypeFilter<$PrismaModel> | $Enums.EventType
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NestedEnumEventTypeWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedEnumEventTypeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.EventType | Prisma.EnumEventTypeFieldRefInput<$PrismaModel>;
|
equals?: $Enums.EventType | Prisma.EnumEventTypeFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.EventType[];
|
in?: $Enums.EventType[]
|
||||||
notIn?: $Enums.EventType[];
|
notIn?: $Enums.EventType[]
|
||||||
not?:
|
not?: Prisma.NestedEnumEventTypeWithAggregatesFilter<$PrismaModel> | $Enums.EventType
|
||||||
| Prisma.NestedEnumEventTypeWithAggregatesFilter<$PrismaModel>
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
| $Enums.EventType;
|
_min?: Prisma.NestedEnumEventTypeFilter<$PrismaModel>
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>;
|
_max?: Prisma.NestedEnumEventTypeFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedEnumEventTypeFilter<$PrismaModel>;
|
}
|
||||||
_max?: Prisma.NestedEnumEventTypeFilter<$PrismaModel>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type NestedIntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedIntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null;
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||||
in?: number[] | null;
|
in?: number[] | null
|
||||||
notIn?: number[] | null;
|
notIn?: number[] | null
|
||||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>;
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
not?:
|
not?: Prisma.NestedIntNullableWithAggregatesFilter<$PrismaModel> | number | null
|
||||||
| Prisma.NestedIntNullableWithAggregatesFilter<$PrismaModel>
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
| number
|
_avg?: Prisma.NestedFloatNullableFilter<$PrismaModel>
|
||||||
| null;
|
_sum?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
_min?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
_avg?: Prisma.NestedFloatNullableFilter<$PrismaModel>;
|
_max?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
_sum?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
}
|
||||||
_min?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
|
||||||
_max?: Prisma.NestedIntNullableFilter<$PrismaModel>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type NestedFloatNullableFilter<$PrismaModel = never> = {
|
export type NestedFloatNullableFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null;
|
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null
|
||||||
in?: number[] | null;
|
in?: number[] | null
|
||||||
notIn?: number[] | null;
|
notIn?: number[] | null
|
||||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>;
|
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>;
|
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>;
|
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
gte?: number | Prisma.FloatFieldRefInput<$PrismaModel>;
|
gte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedFloatNullableFilter<$PrismaModel> | number | null;
|
not?: Prisma.NestedFloatNullableFilter<$PrismaModel> | number | null
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export type NestedEnumChallengeStatusFilter<$PrismaModel = never> = {
|
||||||
|
equals?: $Enums.ChallengeStatus | Prisma.EnumChallengeStatusFieldRefInput<$PrismaModel>
|
||||||
|
in?: $Enums.ChallengeStatus[]
|
||||||
|
notIn?: $Enums.ChallengeStatus[]
|
||||||
|
not?: Prisma.NestedEnumChallengeStatusFilter<$PrismaModel> | $Enums.ChallengeStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedDateTimeNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | null
|
||||||
|
notIn?: Date[] | string[] | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedEnumChallengeStatusWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: $Enums.ChallengeStatus | Prisma.EnumChallengeStatusFieldRefInput<$PrismaModel>
|
||||||
|
in?: $Enums.ChallengeStatus[]
|
||||||
|
notIn?: $Enums.ChallengeStatus[]
|
||||||
|
not?: Prisma.NestedEnumChallengeStatusWithAggregatesFilter<$PrismaModel> | $Enums.ChallengeStatus
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedEnumChallengeStatusFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedEnumChallengeStatusFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedDateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | null
|
||||||
|
notIn?: Date[] | string[] | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +1,54 @@
|
|||||||
|
|
||||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// biome-ignore-all lint: generated file
|
// biome-ignore-all lint: generated file
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
/*
|
/*
|
||||||
* This file exports all enum related types from the schema.
|
* This file exports all enum related types from the schema.
|
||||||
*
|
*
|
||||||
* 🟢 You can import this file directly.
|
* 🟢 You can import this file directly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const Role = {
|
export const Role = {
|
||||||
USER: "USER",
|
USER: 'USER',
|
||||||
ADMIN: "ADMIN",
|
ADMIN: 'ADMIN'
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
|
export type Role = (typeof Role)[keyof typeof Role]
|
||||||
|
|
||||||
export type Role = (typeof Role)[keyof typeof Role];
|
|
||||||
|
|
||||||
export const EventType = {
|
export const EventType = {
|
||||||
ATELIER: "ATELIER",
|
ATELIER: 'ATELIER',
|
||||||
KATA: "KATA",
|
KATA: 'KATA',
|
||||||
PRESENTATION: "PRESENTATION",
|
PRESENTATION: 'PRESENTATION',
|
||||||
LEARNING_HOUR: "LEARNING_HOUR",
|
LEARNING_HOUR: 'LEARNING_HOUR'
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
|
export type EventType = (typeof EventType)[keyof typeof EventType]
|
||||||
|
|
||||||
export type EventType = (typeof EventType)[keyof typeof EventType];
|
|
||||||
|
|
||||||
export const CharacterClass = {
|
export const CharacterClass = {
|
||||||
WARRIOR: "WARRIOR",
|
WARRIOR: 'WARRIOR',
|
||||||
MAGE: "MAGE",
|
MAGE: 'MAGE',
|
||||||
ROGUE: "ROGUE",
|
ROGUE: 'ROGUE',
|
||||||
RANGER: "RANGER",
|
RANGER: 'RANGER',
|
||||||
PALADIN: "PALADIN",
|
PALADIN: 'PALADIN',
|
||||||
ENGINEER: "ENGINEER",
|
ENGINEER: 'ENGINEER',
|
||||||
MERCHANT: "MERCHANT",
|
MERCHANT: 'MERCHANT',
|
||||||
SCHOLAR: "SCHOLAR",
|
SCHOLAR: 'SCHOLAR',
|
||||||
BERSERKER: "BERSERKER",
|
BERSERKER: 'BERSERKER',
|
||||||
NECROMANCER: "NECROMANCER",
|
NECROMANCER: 'NECROMANCER'
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
export type CharacterClass =
|
export type CharacterClass = (typeof CharacterClass)[keyof typeof CharacterClass]
|
||||||
(typeof CharacterClass)[keyof typeof CharacterClass];
|
|
||||||
|
|
||||||
|
export const ChallengeStatus = {
|
||||||
|
PENDING: 'PENDING',
|
||||||
|
ACCEPTED: 'ACCEPTED',
|
||||||
|
COMPLETED: 'COMPLETED',
|
||||||
|
REJECTED: 'REJECTED',
|
||||||
|
CANCELLED: 'CANCELLED'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ChallengeStatus = (typeof ChallengeStatus)[keyof typeof ChallengeStatus]
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// biome-ignore-all lint: generated file
|
// biome-ignore-all lint: generated file
|
||||||
@@ -14,164 +15,184 @@
|
|||||||
* model files in the `model` directory!
|
* model files in the `model` directory!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as runtime from "@prisma/client/runtime/index-browser";
|
import * as runtime from "@prisma/client/runtime/index-browser"
|
||||||
|
|
||||||
export type * from "../models";
|
export type * from '../models'
|
||||||
export type * from "./prismaNamespace";
|
export type * from './prismaNamespace'
|
||||||
|
|
||||||
|
export const Decimal = runtime.Decimal
|
||||||
|
|
||||||
export const Decimal = runtime.Decimal;
|
|
||||||
|
|
||||||
export const NullTypes = {
|
export const NullTypes = {
|
||||||
DbNull: runtime.NullTypes.DbNull as new (
|
DbNull: runtime.NullTypes.DbNull as (new (secret: never) => typeof runtime.DbNull),
|
||||||
secret: never
|
JsonNull: runtime.NullTypes.JsonNull as (new (secret: never) => typeof runtime.JsonNull),
|
||||||
) => typeof runtime.DbNull,
|
AnyNull: runtime.NullTypes.AnyNull as (new (secret: never) => typeof runtime.AnyNull),
|
||||||
JsonNull: runtime.NullTypes.JsonNull as new (
|
}
|
||||||
secret: never
|
|
||||||
) => typeof runtime.JsonNull,
|
|
||||||
AnyNull: runtime.NullTypes.AnyNull as new (
|
|
||||||
secret: never
|
|
||||||
) => typeof runtime.AnyNull,
|
|
||||||
};
|
|
||||||
/**
|
/**
|
||||||
* Helper for filtering JSON entries that have `null` on the database (empty on the db)
|
* Helper for filtering JSON entries that have `null` on the database (empty on the db)
|
||||||
*
|
*
|
||||||
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||||
*/
|
*/
|
||||||
export const DbNull = runtime.DbNull;
|
export const DbNull = runtime.DbNull
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper for filtering JSON entries that have JSON `null` values (not empty on the db)
|
* Helper for filtering JSON entries that have JSON `null` values (not empty on the db)
|
||||||
*
|
*
|
||||||
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||||
*/
|
*/
|
||||||
export const JsonNull = runtime.JsonNull;
|
export const JsonNull = runtime.JsonNull
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper for filtering JSON entries that are `Prisma.DbNull` or `Prisma.JsonNull`
|
* Helper for filtering JSON entries that are `Prisma.DbNull` or `Prisma.JsonNull`
|
||||||
*
|
*
|
||||||
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||||
*/
|
*/
|
||||||
export const AnyNull = runtime.AnyNull;
|
export const AnyNull = runtime.AnyNull
|
||||||
|
|
||||||
|
|
||||||
export const ModelName = {
|
export const ModelName = {
|
||||||
User: "User",
|
User: 'User',
|
||||||
UserPreferences: "UserPreferences",
|
UserPreferences: 'UserPreferences',
|
||||||
Event: "Event",
|
Event: 'Event',
|
||||||
EventRegistration: "EventRegistration",
|
EventRegistration: 'EventRegistration',
|
||||||
EventFeedback: "EventFeedback",
|
EventFeedback: 'EventFeedback',
|
||||||
SitePreferences: "SitePreferences",
|
SitePreferences: 'SitePreferences',
|
||||||
} as const;
|
Challenge: 'Challenge'
|
||||||
|
} as const
|
||||||
|
|
||||||
export type ModelName = (typeof ModelName)[keyof typeof ModelName];
|
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enums
|
* Enums
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const TransactionIsolationLevel = {
|
export const TransactionIsolationLevel = {
|
||||||
Serializable: "Serializable",
|
Serializable: 'Serializable'
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
|
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
||||||
|
|
||||||
export type TransactionIsolationLevel =
|
|
||||||
(typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel];
|
|
||||||
|
|
||||||
export const UserScalarFieldEnum = {
|
export const UserScalarFieldEnum = {
|
||||||
id: "id",
|
id: 'id',
|
||||||
email: "email",
|
email: 'email',
|
||||||
password: "password",
|
password: 'password',
|
||||||
username: "username",
|
username: 'username',
|
||||||
role: "role",
|
role: 'role',
|
||||||
score: "score",
|
score: 'score',
|
||||||
level: "level",
|
level: 'level',
|
||||||
hp: "hp",
|
hp: 'hp',
|
||||||
maxHp: "maxHp",
|
maxHp: 'maxHp',
|
||||||
xp: "xp",
|
xp: 'xp',
|
||||||
maxXp: "maxXp",
|
maxXp: 'maxXp',
|
||||||
avatar: "avatar",
|
avatar: 'avatar',
|
||||||
bio: "bio",
|
createdAt: 'createdAt',
|
||||||
characterClass: "characterClass",
|
updatedAt: 'updatedAt',
|
||||||
createdAt: "createdAt",
|
bio: 'bio',
|
||||||
updatedAt: "updatedAt",
|
characterClass: 'characterClass'
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
|
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||||
|
|
||||||
export type UserScalarFieldEnum =
|
|
||||||
(typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum];
|
|
||||||
|
|
||||||
export const UserPreferencesScalarFieldEnum = {
|
export const UserPreferencesScalarFieldEnum = {
|
||||||
id: "id",
|
id: 'id',
|
||||||
userId: "userId",
|
userId: 'userId',
|
||||||
homeBackground: "homeBackground",
|
homeBackground: 'homeBackground',
|
||||||
eventsBackground: "eventsBackground",
|
eventsBackground: 'eventsBackground',
|
||||||
leaderboardBackground: "leaderboardBackground",
|
leaderboardBackground: 'leaderboardBackground',
|
||||||
theme: "theme",
|
theme: 'theme',
|
||||||
createdAt: "createdAt",
|
createdAt: 'createdAt',
|
||||||
updatedAt: "updatedAt",
|
updatedAt: 'updatedAt'
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
|
export type UserPreferencesScalarFieldEnum = (typeof UserPreferencesScalarFieldEnum)[keyof typeof UserPreferencesScalarFieldEnum]
|
||||||
|
|
||||||
export type UserPreferencesScalarFieldEnum =
|
|
||||||
(typeof UserPreferencesScalarFieldEnum)[keyof typeof UserPreferencesScalarFieldEnum];
|
|
||||||
|
|
||||||
export const EventScalarFieldEnum = {
|
export const EventScalarFieldEnum = {
|
||||||
id: "id",
|
id: 'id',
|
||||||
date: "date",
|
date: 'date',
|
||||||
name: "name",
|
name: 'name',
|
||||||
description: "description",
|
description: 'description',
|
||||||
type: "type",
|
type: 'type',
|
||||||
room: "room",
|
room: 'room',
|
||||||
time: "time",
|
time: 'time',
|
||||||
maxPlaces: "maxPlaces",
|
maxPlaces: 'maxPlaces',
|
||||||
createdAt: "createdAt",
|
createdAt: 'createdAt',
|
||||||
updatedAt: "updatedAt",
|
updatedAt: 'updatedAt'
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
|
export type EventScalarFieldEnum = (typeof EventScalarFieldEnum)[keyof typeof EventScalarFieldEnum]
|
||||||
|
|
||||||
export type EventScalarFieldEnum =
|
|
||||||
(typeof EventScalarFieldEnum)[keyof typeof EventScalarFieldEnum];
|
|
||||||
|
|
||||||
export const EventRegistrationScalarFieldEnum = {
|
export const EventRegistrationScalarFieldEnum = {
|
||||||
id: "id",
|
id: 'id',
|
||||||
userId: "userId",
|
userId: 'userId',
|
||||||
eventId: "eventId",
|
eventId: 'eventId',
|
||||||
createdAt: "createdAt",
|
createdAt: 'createdAt'
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
|
export type EventRegistrationScalarFieldEnum = (typeof EventRegistrationScalarFieldEnum)[keyof typeof EventRegistrationScalarFieldEnum]
|
||||||
|
|
||||||
export type EventRegistrationScalarFieldEnum =
|
|
||||||
(typeof EventRegistrationScalarFieldEnum)[keyof typeof EventRegistrationScalarFieldEnum];
|
|
||||||
|
|
||||||
export const EventFeedbackScalarFieldEnum = {
|
export const EventFeedbackScalarFieldEnum = {
|
||||||
id: "id",
|
id: 'id',
|
||||||
userId: "userId",
|
userId: 'userId',
|
||||||
eventId: "eventId",
|
eventId: 'eventId',
|
||||||
rating: "rating",
|
rating: 'rating',
|
||||||
comment: "comment",
|
comment: 'comment',
|
||||||
createdAt: "createdAt",
|
createdAt: 'createdAt',
|
||||||
updatedAt: "updatedAt",
|
updatedAt: 'updatedAt'
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
|
export type EventFeedbackScalarFieldEnum = (typeof EventFeedbackScalarFieldEnum)[keyof typeof EventFeedbackScalarFieldEnum]
|
||||||
|
|
||||||
export type EventFeedbackScalarFieldEnum =
|
|
||||||
(typeof EventFeedbackScalarFieldEnum)[keyof typeof EventFeedbackScalarFieldEnum];
|
|
||||||
|
|
||||||
export const SitePreferencesScalarFieldEnum = {
|
export const SitePreferencesScalarFieldEnum = {
|
||||||
id: "id",
|
id: 'id',
|
||||||
homeBackground: "homeBackground",
|
homeBackground: 'homeBackground',
|
||||||
eventsBackground: "eventsBackground",
|
eventsBackground: 'eventsBackground',
|
||||||
leaderboardBackground: "leaderboardBackground",
|
leaderboardBackground: 'leaderboardBackground',
|
||||||
createdAt: "createdAt",
|
createdAt: 'createdAt',
|
||||||
updatedAt: "updatedAt",
|
updatedAt: 'updatedAt',
|
||||||
} as const;
|
primaryColor: 'primaryColor'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type SitePreferencesScalarFieldEnum = (typeof SitePreferencesScalarFieldEnum)[keyof typeof SitePreferencesScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ChallengeScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
challengerId: 'challengerId',
|
||||||
|
challengedId: 'challengedId',
|
||||||
|
title: 'title',
|
||||||
|
description: 'description',
|
||||||
|
pointsReward: 'pointsReward',
|
||||||
|
status: 'status',
|
||||||
|
adminId: 'adminId',
|
||||||
|
adminComment: 'adminComment',
|
||||||
|
winnerId: 'winnerId',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
acceptedAt: 'acceptedAt',
|
||||||
|
completedAt: 'completedAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ChallengeScalarFieldEnum = (typeof ChallengeScalarFieldEnum)[keyof typeof ChallengeScalarFieldEnum]
|
||||||
|
|
||||||
export type SitePreferencesScalarFieldEnum =
|
|
||||||
(typeof SitePreferencesScalarFieldEnum)[keyof typeof SitePreferencesScalarFieldEnum];
|
|
||||||
|
|
||||||
export const SortOrder = {
|
export const SortOrder = {
|
||||||
asc: "asc",
|
asc: 'asc',
|
||||||
desc: "desc",
|
desc: 'desc'
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
|
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
|
||||||
|
|
||||||
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder];
|
|
||||||
|
|
||||||
export const NullsOrder = {
|
export const NullsOrder = {
|
||||||
first: "first",
|
first: 'first',
|
||||||
last: "last",
|
last: 'last'
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
|
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
||||||
|
|
||||||
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder];
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// biome-ignore-all lint: generated file
|
// biome-ignore-all lint: generated file
|
||||||
@@ -7,10 +8,11 @@
|
|||||||
*
|
*
|
||||||
* 🟢 You can import this file directly.
|
* 🟢 You can import this file directly.
|
||||||
*/
|
*/
|
||||||
export type * from "./models/User";
|
export type * from './models/User'
|
||||||
export type * from "./models/UserPreferences";
|
export type * from './models/UserPreferences'
|
||||||
export type * from "./models/Event";
|
export type * from './models/Event'
|
||||||
export type * from "./models/EventRegistration";
|
export type * from './models/EventRegistration'
|
||||||
export type * from "./models/EventFeedback";
|
export type * from './models/EventFeedback'
|
||||||
export type * from "./models/SitePreferences";
|
export type * from './models/SitePreferences'
|
||||||
export type * from "./commonInputTypes";
|
export type * from './models/Challenge'
|
||||||
|
export type * from './commonInputTypes'
|
||||||
2261
prisma/generated/prisma/models/Challenge.ts
Normal file
2261
prisma/generated/prisma/models/Challenge.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "SitePreferences" ADD COLUMN "primaryColor" TEXT DEFAULT '#0be4cc';
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Challenge" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"challengerId" TEXT NOT NULL,
|
||||||
|
"challengedId" TEXT NOT NULL,
|
||||||
|
"title" TEXT NOT NULL,
|
||||||
|
"description" TEXT NOT NULL,
|
||||||
|
"pointsReward" INTEGER NOT NULL DEFAULT 100,
|
||||||
|
"status" TEXT NOT NULL DEFAULT 'PENDING',
|
||||||
|
"adminId" TEXT,
|
||||||
|
"adminComment" TEXT,
|
||||||
|
"winnerId" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"acceptedAt" DATETIME,
|
||||||
|
"completedAt" DATETIME,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
CONSTRAINT "Challenge_challengerId_fkey" FOREIGN KEY ("challengerId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "Challenge_challengedId_fkey" FOREIGN KEY ("challengedId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "Challenge_adminId_fkey" FOREIGN KEY ("adminId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "Challenge_winnerId_fkey" FOREIGN KEY ("winnerId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Challenge_challengerId_idx" ON "Challenge"("challengerId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Challenge_challengedId_idx" ON "Challenge"("challengedId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Challenge_status_idx" ON "Challenge"("status");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Challenge_adminId_idx" ON "Challenge"("adminId");
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
// This is your Prisma schema file,
|
|
||||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
||||||
|
|
||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client"
|
provider = "prisma-client"
|
||||||
output = "./generated/prisma"
|
output = "./generated/prisma"
|
||||||
@@ -10,6 +7,103 @@ datasource db {
|
|||||||
provider = "sqlite"
|
provider = "sqlite"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
email String @unique
|
||||||
|
password String
|
||||||
|
username String @unique
|
||||||
|
role Role @default(USER)
|
||||||
|
score Int @default(0)
|
||||||
|
level Int @default(1)
|
||||||
|
hp Int @default(1000)
|
||||||
|
maxHp Int @default(1000)
|
||||||
|
xp Int @default(0)
|
||||||
|
maxXp Int @default(5000)
|
||||||
|
avatar String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
bio String?
|
||||||
|
characterClass CharacterClass?
|
||||||
|
eventFeedbacks EventFeedback[]
|
||||||
|
eventRegistrations EventRegistration[]
|
||||||
|
preferences UserPreferences?
|
||||||
|
challengesAsChallenger Challenge[] @relation("Challenger")
|
||||||
|
challengesAsChallenged Challenge[] @relation("Challenged")
|
||||||
|
challengesAsAdmin Challenge[] @relation("AdminValidator")
|
||||||
|
challengesAsWinner Challenge[] @relation("ChallengeWinner")
|
||||||
|
|
||||||
|
@@index([score])
|
||||||
|
@@index([email])
|
||||||
|
}
|
||||||
|
|
||||||
|
model UserPreferences {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
userId String @unique
|
||||||
|
homeBackground String?
|
||||||
|
eventsBackground String?
|
||||||
|
leaderboardBackground String?
|
||||||
|
theme String? @default("default")
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
}
|
||||||
|
|
||||||
|
model Event {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
date DateTime
|
||||||
|
name String
|
||||||
|
description String
|
||||||
|
type EventType
|
||||||
|
room String?
|
||||||
|
time String?
|
||||||
|
maxPlaces Int?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
feedbacks EventFeedback[]
|
||||||
|
registrations EventRegistration[]
|
||||||
|
|
||||||
|
@@index([date])
|
||||||
|
}
|
||||||
|
|
||||||
|
model EventRegistration {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
userId String
|
||||||
|
eventId String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
event Event @relation(fields: [eventId], references: [id], onDelete: Cascade)
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([userId, eventId])
|
||||||
|
@@index([userId])
|
||||||
|
@@index([eventId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model EventFeedback {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
userId String
|
||||||
|
eventId String
|
||||||
|
rating Int
|
||||||
|
comment String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
event Event @relation(fields: [eventId], references: [id], onDelete: Cascade)
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([userId, eventId])
|
||||||
|
@@index([userId])
|
||||||
|
@@index([eventId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model SitePreferences {
|
||||||
|
id String @id @default("global")
|
||||||
|
homeBackground String?
|
||||||
|
eventsBackground String?
|
||||||
|
leaderboardBackground String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
primaryColor String? @default("#0be4cc")
|
||||||
|
}
|
||||||
|
|
||||||
enum Role {
|
enum Role {
|
||||||
USER
|
USER
|
||||||
ADMIN
|
ADMIN
|
||||||
@@ -35,99 +129,36 @@ enum CharacterClass {
|
|||||||
NECROMANCER
|
NECROMANCER
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
enum ChallengeStatus {
|
||||||
id String @id @default(cuid())
|
PENDING
|
||||||
email String @unique
|
ACCEPTED
|
||||||
password String
|
COMPLETED
|
||||||
username String @unique
|
REJECTED
|
||||||
role Role @default(USER)
|
CANCELLED
|
||||||
score Int @default(0)
|
|
||||||
level Int @default(1)
|
|
||||||
hp Int @default(1000)
|
|
||||||
maxHp Int @default(1000)
|
|
||||||
xp Int @default(0)
|
|
||||||
maxXp Int @default(5000)
|
|
||||||
avatar String?
|
|
||||||
bio String?
|
|
||||||
characterClass CharacterClass?
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
preferences UserPreferences?
|
|
||||||
eventRegistrations EventRegistration[]
|
|
||||||
eventFeedbacks EventFeedback[]
|
|
||||||
|
|
||||||
@@index([score])
|
|
||||||
@@index([email])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model UserPreferences {
|
model Challenge {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String @unique
|
challengerId String // Joueur qui lance le défi
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
challengedId String // Joueur qui reçoit le défi
|
||||||
|
challenger User @relation("Challenger", fields: [challengerId], references: [id], onDelete: Cascade)
|
||||||
// Background images for each page
|
challenged User @relation("Challenged", fields: [challengedId], references: [id], onDelete: Cascade)
|
||||||
homeBackground String?
|
title String // Titre du défi
|
||||||
eventsBackground String?
|
description String // Description détaillée du défi
|
||||||
leaderboardBackground String?
|
pointsReward Int @default(100) // Points à gagner pour le gagnant
|
||||||
|
status ChallengeStatus @default(PENDING)
|
||||||
// Other UI preferences can be added here
|
adminId String? // Admin qui valide le défi
|
||||||
theme String? @default("default")
|
admin User? @relation("AdminValidator", fields: [adminId], references: [id], onDelete: SetNull)
|
||||||
|
adminComment String? // Commentaire de l'admin lors de la validation/rejet
|
||||||
createdAt DateTime @default(now())
|
winnerId String? // ID du gagnant (challengerId ou challengedId)
|
||||||
updatedAt DateTime @updatedAt
|
winner User? @relation("ChallengeWinner", fields: [winnerId], references: [id], onDelete: SetNull)
|
||||||
}
|
|
||||||
|
|
||||||
model Event {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
date DateTime
|
|
||||||
name String
|
|
||||||
description String
|
|
||||||
type EventType
|
|
||||||
room String?
|
|
||||||
time String?
|
|
||||||
maxPlaces Int?
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
registrations EventRegistration[]
|
|
||||||
feedbacks EventFeedback[]
|
|
||||||
|
|
||||||
@@index([date])
|
|
||||||
}
|
|
||||||
|
|
||||||
model EventRegistration {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
userId String
|
|
||||||
eventId String
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
||||||
event Event @relation(fields: [eventId], references: [id], onDelete: Cascade)
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
|
|
||||||
@@unique([userId, eventId])
|
|
||||||
@@index([userId])
|
|
||||||
@@index([eventId])
|
|
||||||
}
|
|
||||||
|
|
||||||
model EventFeedback {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
userId String
|
|
||||||
eventId String
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
||||||
event Event @relation(fields: [eventId], references: [id], onDelete: Cascade)
|
|
||||||
rating Int // Note de 1 à 5
|
|
||||||
comment String? // Commentaire optionnel
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
acceptedAt DateTime? // Date d'acceptation du défi
|
||||||
|
completedAt DateTime? // Date de validation par l'admin
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
@@unique([userId, eventId])
|
@@index([challengerId])
|
||||||
@@index([userId])
|
@@index([challengedId])
|
||||||
@@index([eventId])
|
@@index([status])
|
||||||
}
|
@@index([adminId])
|
||||||
|
|
||||||
model SitePreferences {
|
|
||||||
id String @id @default("global")
|
|
||||||
homeBackground String?
|
|
||||||
eventsBackground String?
|
|
||||||
leaderboardBackground String?
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
}
|
}
|
||||||
|
|||||||
500
services/challenges/challenge.service.ts
Normal file
500
services/challenges/challenge.service.ts
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
import { prisma } from "../database";
|
||||||
|
import type {
|
||||||
|
Challenge,
|
||||||
|
ChallengeStatus,
|
||||||
|
Prisma,
|
||||||
|
} from "@/prisma/generated/prisma/client";
|
||||||
|
import { ValidationError, NotFoundError, ConflictError } from "../errors";
|
||||||
|
|
||||||
|
export interface CreateChallengeInput {
|
||||||
|
challengerId: string;
|
||||||
|
challengedId: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
pointsReward?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateChallengeInput {
|
||||||
|
status?: ChallengeStatus;
|
||||||
|
adminId?: string;
|
||||||
|
adminComment?: string;
|
||||||
|
winnerId?: string;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
pointsReward?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChallengeWithUsers extends Challenge {
|
||||||
|
challenger: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
avatar: string | null;
|
||||||
|
};
|
||||||
|
challenged: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
avatar: string | null;
|
||||||
|
};
|
||||||
|
admin?: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
} | null;
|
||||||
|
winner?: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
} | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service de gestion des défis entre joueurs
|
||||||
|
*/
|
||||||
|
export class ChallengeService {
|
||||||
|
/**
|
||||||
|
* Crée un nouveau défi
|
||||||
|
*/
|
||||||
|
async createChallenge(
|
||||||
|
data: CreateChallengeInput
|
||||||
|
): Promise<Challenge> {
|
||||||
|
// Vérifier que les deux joueurs existent
|
||||||
|
const [challenger, challenged] = await Promise.all([
|
||||||
|
prisma.user.findUnique({ where: { id: data.challengerId } }),
|
||||||
|
prisma.user.findUnique({ where: { id: data.challengedId } }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!challenger) {
|
||||||
|
throw new NotFoundError("Joueur qui lance le défi");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!challenged) {
|
||||||
|
throw new NotFoundError("Joueur qui reçoit le défi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier qu'on ne se défie pas soi-même
|
||||||
|
if (data.challengerId === data.challengedId) {
|
||||||
|
throw new ValidationError("Vous ne pouvez pas vous défier vous-même");
|
||||||
|
}
|
||||||
|
|
||||||
|
return prisma.challenge.create({
|
||||||
|
data: {
|
||||||
|
challengerId: data.challengerId,
|
||||||
|
challengedId: data.challengedId,
|
||||||
|
title: data.title,
|
||||||
|
description: data.description,
|
||||||
|
pointsReward: data.pointsReward || 100,
|
||||||
|
status: "PENDING",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepte un défi
|
||||||
|
*/
|
||||||
|
async acceptChallenge(
|
||||||
|
challengeId: string,
|
||||||
|
userId: string
|
||||||
|
): Promise<Challenge> {
|
||||||
|
const challenge = await prisma.challenge.findUnique({
|
||||||
|
where: { id: challengeId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!challenge) {
|
||||||
|
throw new NotFoundError("Défi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que l'utilisateur est bien celui qui reçoit le défi
|
||||||
|
if (challenge.challengedId !== userId) {
|
||||||
|
throw new ValidationError(
|
||||||
|
"Vous n'êtes pas autorisé à accepter ce défi"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que le défi est en attente
|
||||||
|
if (challenge.status !== "PENDING") {
|
||||||
|
throw new ValidationError(
|
||||||
|
"Ce défi ne peut plus être accepté (statut: " + challenge.status + ")"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return prisma.challenge.update({
|
||||||
|
where: { id: challengeId },
|
||||||
|
data: {
|
||||||
|
status: "ACCEPTED",
|
||||||
|
acceptedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annule un défi (par le challenger ou le challenged)
|
||||||
|
*/
|
||||||
|
async cancelChallenge(
|
||||||
|
challengeId: string,
|
||||||
|
userId: string
|
||||||
|
): Promise<Challenge> {
|
||||||
|
const challenge = await prisma.challenge.findUnique({
|
||||||
|
where: { id: challengeId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!challenge) {
|
||||||
|
throw new NotFoundError("Défi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que l'utilisateur est bien impliqué dans le défi
|
||||||
|
if (
|
||||||
|
challenge.challengerId !== userId &&
|
||||||
|
challenge.challengedId !== userId
|
||||||
|
) {
|
||||||
|
throw new ValidationError(
|
||||||
|
"Vous n'êtes pas autorisé à annuler ce défi"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que le défi peut être annulé
|
||||||
|
if (challenge.status === "COMPLETED") {
|
||||||
|
throw new ValidationError("Un défi complété ne peut pas être annulé");
|
||||||
|
}
|
||||||
|
|
||||||
|
return prisma.challenge.update({
|
||||||
|
where: { id: challengeId },
|
||||||
|
data: {
|
||||||
|
status: "CANCELLED",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valide un défi (admin seulement)
|
||||||
|
*/
|
||||||
|
async validateChallenge(
|
||||||
|
challengeId: string,
|
||||||
|
adminId: string,
|
||||||
|
winnerId: string,
|
||||||
|
adminComment?: string
|
||||||
|
): Promise<Challenge> {
|
||||||
|
const challenge = await prisma.challenge.findUnique({
|
||||||
|
where: { id: challengeId },
|
||||||
|
include: {
|
||||||
|
challenger: true,
|
||||||
|
challenged: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!challenge) {
|
||||||
|
throw new NotFoundError("Défi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que le défi est accepté
|
||||||
|
if (challenge.status !== "ACCEPTED") {
|
||||||
|
throw new ValidationError(
|
||||||
|
"Seuls les défis acceptés peuvent être validés"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que le winner est bien l'un des deux joueurs
|
||||||
|
if (
|
||||||
|
winnerId !== challenge.challengerId &&
|
||||||
|
winnerId !== challenge.challengedId
|
||||||
|
) {
|
||||||
|
throw new ValidationError(
|
||||||
|
"Le gagnant doit être l'un des deux joueurs du défi"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour le défi
|
||||||
|
const updatedChallenge = await prisma.challenge.update({
|
||||||
|
where: { id: challengeId },
|
||||||
|
data: {
|
||||||
|
status: "COMPLETED",
|
||||||
|
adminId,
|
||||||
|
adminComment: adminComment || null,
|
||||||
|
winnerId,
|
||||||
|
completedAt: new Date(),
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
challenger: true,
|
||||||
|
challenged: true,
|
||||||
|
winner: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Attribuer les points au gagnant
|
||||||
|
await prisma.user.update({
|
||||||
|
where: { id: winnerId },
|
||||||
|
data: {
|
||||||
|
score: {
|
||||||
|
increment: challenge.pointsReward,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedChallenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rejette un défi (admin seulement)
|
||||||
|
*/
|
||||||
|
async rejectChallenge(
|
||||||
|
challengeId: string,
|
||||||
|
adminId: string,
|
||||||
|
adminComment?: string
|
||||||
|
): Promise<Challenge> {
|
||||||
|
const challenge = await prisma.challenge.findUnique({
|
||||||
|
where: { id: challengeId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!challenge) {
|
||||||
|
throw new NotFoundError("Défi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que le défi est accepté
|
||||||
|
if (challenge.status !== "ACCEPTED") {
|
||||||
|
throw new ValidationError(
|
||||||
|
"Seuls les défis acceptés peuvent être rejetés"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return prisma.challenge.update({
|
||||||
|
where: { id: challengeId },
|
||||||
|
data: {
|
||||||
|
status: "REJECTED",
|
||||||
|
adminId,
|
||||||
|
adminComment: adminComment || null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Met à jour un défi (admin seulement)
|
||||||
|
*/
|
||||||
|
async updateChallenge(
|
||||||
|
challengeId: string,
|
||||||
|
data: UpdateChallengeInput
|
||||||
|
): Promise<Challenge> {
|
||||||
|
const challenge = await prisma.challenge.findUnique({
|
||||||
|
where: { id: challengeId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!challenge) {
|
||||||
|
throw new NotFoundError("Défi");
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData: Prisma.ChallengeUpdateInput = {};
|
||||||
|
|
||||||
|
if (data.title !== undefined) {
|
||||||
|
updateData.title = data.title;
|
||||||
|
}
|
||||||
|
if (data.description !== undefined) {
|
||||||
|
updateData.description = data.description;
|
||||||
|
}
|
||||||
|
if (data.pointsReward !== undefined) {
|
||||||
|
updateData.pointsReward = data.pointsReward;
|
||||||
|
}
|
||||||
|
if (data.status !== undefined) {
|
||||||
|
updateData.status = data.status;
|
||||||
|
}
|
||||||
|
if (data.adminId !== undefined) {
|
||||||
|
updateData.adminId = data.adminId;
|
||||||
|
}
|
||||||
|
if (data.adminComment !== undefined) {
|
||||||
|
updateData.adminComment = data.adminComment;
|
||||||
|
}
|
||||||
|
if (data.winnerId !== undefined) {
|
||||||
|
updateData.winnerId = data.winnerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prisma.challenge.update({
|
||||||
|
where: { id: challengeId },
|
||||||
|
data: updateData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime un défi (admin seulement)
|
||||||
|
*/
|
||||||
|
async deleteChallenge(challengeId: string): Promise<void> {
|
||||||
|
const challenge = await prisma.challenge.findUnique({
|
||||||
|
where: { id: challengeId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!challenge) {
|
||||||
|
throw new NotFoundError("Défi");
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.challenge.delete({
|
||||||
|
where: { id: challengeId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère un défi par son ID avec les utilisateurs
|
||||||
|
*/
|
||||||
|
async getChallengeById(id: string): Promise<ChallengeWithUsers | null> {
|
||||||
|
const challenge = await prisma.challenge.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
challenger: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
avatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
challenged: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
avatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
winner: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return challenge as ChallengeWithUsers | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère tous les défis d'un utilisateur
|
||||||
|
*/
|
||||||
|
async getUserChallenges(userId: string): Promise<ChallengeWithUsers[]> {
|
||||||
|
return prisma.challenge.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ challengerId: userId },
|
||||||
|
{ challengedId: userId },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
challenger: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
avatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
challenged: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
avatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
winner: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: "desc",
|
||||||
|
},
|
||||||
|
}) as Promise<ChallengeWithUsers[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère les défis en attente de validation admin
|
||||||
|
*/
|
||||||
|
async getPendingValidationChallenges(): Promise<ChallengeWithUsers[]> {
|
||||||
|
return prisma.challenge.findMany({
|
||||||
|
where: {
|
||||||
|
status: "ACCEPTED",
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
challenger: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
avatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
challenged: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
avatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
winner: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
acceptedAt: "asc",
|
||||||
|
},
|
||||||
|
}) as Promise<ChallengeWithUsers[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère tous les défis (pour admin)
|
||||||
|
*/
|
||||||
|
async getAllChallenges(options?: {
|
||||||
|
status?: ChallengeStatus;
|
||||||
|
take?: number;
|
||||||
|
}): Promise<ChallengeWithUsers[]> {
|
||||||
|
return prisma.challenge.findMany({
|
||||||
|
where: options?.status ? { status: options.status } : undefined,
|
||||||
|
include: {
|
||||||
|
challenger: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
avatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
challenged: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
avatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
winner: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: "desc",
|
||||||
|
},
|
||||||
|
take: options?.take,
|
||||||
|
}) as Promise<ChallengeWithUsers[]>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const challengeService = new ChallengeService();
|
||||||
|
|
||||||
Reference in New Issue
Block a user