Refactor API routes and component logic: Remove unused event and user management routes, streamline feedback handling in components, and enhance state management with transitions for improved user experience. Update service layer methods for better organization and maintainability across the application.
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 2m38s
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 2m38s
This commit is contained in:
131
actions/admin/events.ts
Normal file
131
actions/admin/events.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
import { auth } from '@/lib/auth'
|
||||||
|
import { eventService } from '@/services/events/event.service'
|
||||||
|
import { Role, EventType } from '@/prisma/generated/prisma/client'
|
||||||
|
import { ValidationError, NotFoundError } from '@/services/errors'
|
||||||
|
|
||||||
|
function checkAdminAccess() {
|
||||||
|
return async () => {
|
||||||
|
const session = await auth()
|
||||||
|
if (!session?.user || session.user.role !== Role.ADMIN) {
|
||||||
|
throw new Error('Accès refusé')
|
||||||
|
}
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createEvent(data: {
|
||||||
|
date: string
|
||||||
|
name: string
|
||||||
|
description?: string | null
|
||||||
|
type: string
|
||||||
|
room?: string | null
|
||||||
|
time?: string | null
|
||||||
|
maxPlaces?: number | null
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
await checkAdminAccess()()
|
||||||
|
|
||||||
|
const event = await eventService.validateAndCreateEvent({
|
||||||
|
date: data.date,
|
||||||
|
name: data.name,
|
||||||
|
description: data.description ?? '',
|
||||||
|
type: data.type as EventType,
|
||||||
|
room: data.room ?? undefined,
|
||||||
|
time: data.time ?? undefined,
|
||||||
|
maxPlaces: data.maxPlaces ?? undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
revalidatePath('/admin')
|
||||||
|
revalidatePath('/events')
|
||||||
|
revalidatePath('/')
|
||||||
|
|
||||||
|
return { success: true, data: event }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating event:', error)
|
||||||
|
|
||||||
|
if (error instanceof ValidationError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
if (error instanceof Error && error.message === 'Accès refusé') {
|
||||||
|
return { success: false, error: 'Accès refusé' }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Erreur lors de la création de l\'événement' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateEvent(eventId: string, data: {
|
||||||
|
date?: string
|
||||||
|
name?: string
|
||||||
|
description?: string | null
|
||||||
|
type?: string
|
||||||
|
room?: string | null
|
||||||
|
time?: string | null
|
||||||
|
maxPlaces?: number | null
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
await checkAdminAccess()()
|
||||||
|
|
||||||
|
const event = await eventService.validateAndUpdateEvent(eventId, {
|
||||||
|
date: data.date,
|
||||||
|
name: data.name,
|
||||||
|
description: data.description ?? undefined,
|
||||||
|
type: data.type as EventType,
|
||||||
|
room: data.room ?? undefined,
|
||||||
|
time: data.time ?? undefined,
|
||||||
|
maxPlaces: data.maxPlaces ?? undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
revalidatePath('/admin')
|
||||||
|
revalidatePath('/events')
|
||||||
|
revalidatePath('/')
|
||||||
|
|
||||||
|
return { success: true, data: event }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating event:', 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 === 'Accès refusé') {
|
||||||
|
return { success: false, error: 'Accès refusé' }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Erreur lors de la mise à jour de l\'événement' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteEvent(eventId: string) {
|
||||||
|
try {
|
||||||
|
await checkAdminAccess()()
|
||||||
|
|
||||||
|
const existingEvent = await eventService.getEventById(eventId)
|
||||||
|
|
||||||
|
if (!existingEvent) {
|
||||||
|
return { success: false, error: 'Événement non trouvé' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await eventService.deleteEvent(eventId)
|
||||||
|
|
||||||
|
revalidatePath('/admin')
|
||||||
|
revalidatePath('/events')
|
||||||
|
revalidatePath('/')
|
||||||
|
|
||||||
|
return { success: true }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting event:', error)
|
||||||
|
|
||||||
|
if (error instanceof Error && error.message === 'Accès refusé') {
|
||||||
|
return { success: false, error: 'Accès refusé' }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Erreur lors de la suppression de l\'événement' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
48
actions/admin/preferences.ts
Normal file
48
actions/admin/preferences.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
import { auth } from '@/lib/auth'
|
||||||
|
import { sitePreferencesService } from '@/services/preferences/site-preferences.service'
|
||||||
|
import { Role } from '@/prisma/generated/prisma/client'
|
||||||
|
|
||||||
|
function checkAdminAccess() {
|
||||||
|
return async () => {
|
||||||
|
const session = await auth()
|
||||||
|
if (!session?.user || session.user.role !== Role.ADMIN) {
|
||||||
|
throw new Error('Accès refusé')
|
||||||
|
}
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateSitePreferences(data: {
|
||||||
|
homeBackground?: string | null
|
||||||
|
eventsBackground?: string | null
|
||||||
|
leaderboardBackground?: string | null
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
await checkAdminAccess()()
|
||||||
|
|
||||||
|
const preferences = await sitePreferencesService.updateSitePreferences({
|
||||||
|
homeBackground: data.homeBackground,
|
||||||
|
eventsBackground: data.eventsBackground,
|
||||||
|
leaderboardBackground: data.leaderboardBackground,
|
||||||
|
})
|
||||||
|
|
||||||
|
revalidatePath('/admin')
|
||||||
|
revalidatePath('/')
|
||||||
|
revalidatePath('/events')
|
||||||
|
revalidatePath('/leaderboard')
|
||||||
|
|
||||||
|
return { success: true, data: preferences }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating admin preferences:', error)
|
||||||
|
|
||||||
|
if (error instanceof Error && error.message === 'Accès refusé') {
|
||||||
|
return { success: false, error: 'Accès refusé' }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Erreur lors de la mise à jour des préférences' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
116
actions/admin/users.ts
Normal file
116
actions/admin/users.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
import { auth } from '@/lib/auth'
|
||||||
|
import { userService } from '@/services/users/user.service'
|
||||||
|
import { userStatsService } from '@/services/users/user-stats.service'
|
||||||
|
import { Role } from '@/prisma/generated/prisma/client'
|
||||||
|
import {
|
||||||
|
ValidationError,
|
||||||
|
NotFoundError,
|
||||||
|
ConflictError,
|
||||||
|
} from '@/services/errors'
|
||||||
|
|
||||||
|
function checkAdminAccess() {
|
||||||
|
return async () => {
|
||||||
|
const session = await auth()
|
||||||
|
if (!session?.user || session.user.role !== Role.ADMIN) {
|
||||||
|
throw new Error('Accès refusé')
|
||||||
|
}
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUser(userId: string, data: {
|
||||||
|
username?: string
|
||||||
|
avatar?: string | null
|
||||||
|
hpDelta?: number
|
||||||
|
xpDelta?: number
|
||||||
|
score?: number
|
||||||
|
level?: number
|
||||||
|
role?: string
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
await checkAdminAccess()()
|
||||||
|
|
||||||
|
// Valider username si fourni
|
||||||
|
if (data.username !== undefined) {
|
||||||
|
try {
|
||||||
|
await userService.validateAndUpdateUserProfile(userId, { username: data.username })
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ValidationError || error instanceof ConflictError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour stats et profil
|
||||||
|
const updatedUser = await userStatsService.updateUserStatsAndProfile(
|
||||||
|
userId,
|
||||||
|
{
|
||||||
|
username: data.username,
|
||||||
|
avatar: data.avatar,
|
||||||
|
hpDelta: data.hpDelta,
|
||||||
|
xpDelta: data.xpDelta,
|
||||||
|
score: data.score,
|
||||||
|
level: data.level,
|
||||||
|
role: data.role ? (data.role as Role) : undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
email: true,
|
||||||
|
role: true,
|
||||||
|
score: true,
|
||||||
|
level: true,
|
||||||
|
hp: true,
|
||||||
|
maxHp: true,
|
||||||
|
xp: true,
|
||||||
|
maxXp: true,
|
||||||
|
avatar: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
revalidatePath('/admin')
|
||||||
|
revalidatePath('/leaderboard')
|
||||||
|
|
||||||
|
return { success: true, data: updatedUser }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating user:', error)
|
||||||
|
|
||||||
|
if (error instanceof Error && error.message === 'Accès refusé') {
|
||||||
|
return { success: false, error: 'Accès refusé' }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Erreur lors de la mise à jour de l\'utilisateur' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteUser(userId: string) {
|
||||||
|
try {
|
||||||
|
const session = await checkAdminAccess()()
|
||||||
|
|
||||||
|
await userService.validateAndDeleteUser(userId, session.user.id)
|
||||||
|
|
||||||
|
revalidatePath('/admin')
|
||||||
|
revalidatePath('/leaderboard')
|
||||||
|
|
||||||
|
return { success: true }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting user:', 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 === 'Accès refusé') {
|
||||||
|
return { success: false, error: 'Accès refusé' }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Erreur lors de la suppression de l\'utilisateur' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
45
actions/events/feedback.ts
Normal file
45
actions/events/feedback.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
import { auth } from '@/lib/auth'
|
||||||
|
import { eventFeedbackService } from '@/services/events/event-feedback.service'
|
||||||
|
import {
|
||||||
|
ValidationError,
|
||||||
|
NotFoundError,
|
||||||
|
} from '@/services/errors'
|
||||||
|
|
||||||
|
export async function createFeedback(eventId: string, data: {
|
||||||
|
rating: number
|
||||||
|
comment?: string | null
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
const session = await auth()
|
||||||
|
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return { success: false, error: 'Non authentifié' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const feedback = await eventFeedbackService.validateAndCreateFeedback(
|
||||||
|
session.user.id,
|
||||||
|
eventId,
|
||||||
|
{ rating: data.rating, comment: data.comment }
|
||||||
|
)
|
||||||
|
|
||||||
|
revalidatePath(`/feedback/${eventId}`)
|
||||||
|
revalidatePath('/events')
|
||||||
|
|
||||||
|
return { success: true, data: feedback }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving feedback:', 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: 'Erreur lors de l\'enregistrement du feedback' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
65
actions/events/register.ts
Normal file
65
actions/events/register.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
import { auth } from '@/lib/auth'
|
||||||
|
import { eventRegistrationService } from '@/services/events/event-registration.service'
|
||||||
|
import {
|
||||||
|
ValidationError,
|
||||||
|
NotFoundError,
|
||||||
|
ConflictError,
|
||||||
|
} from '@/services/errors'
|
||||||
|
|
||||||
|
export async function registerForEvent(eventId: string) {
|
||||||
|
try {
|
||||||
|
const session = await auth()
|
||||||
|
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return { success: false, error: 'Vous devez être connecté pour vous inscrire' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const registration = await eventRegistrationService.validateAndRegisterUser(
|
||||||
|
session.user.id,
|
||||||
|
eventId
|
||||||
|
)
|
||||||
|
|
||||||
|
revalidatePath('/events')
|
||||||
|
revalidatePath('/')
|
||||||
|
|
||||||
|
return { success: true, message: 'Inscription réussie', data: registration }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Registration 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 l\'inscription' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unregisterFromEvent(eventId: string) {
|
||||||
|
try {
|
||||||
|
const session = await auth()
|
||||||
|
|
||||||
|
if (!session?.user?.id) {
|
||||||
|
return { success: false, error: 'Vous devez être connecté' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await eventRegistrationService.unregisterUserFromEvent(
|
||||||
|
session.user.id,
|
||||||
|
eventId
|
||||||
|
)
|
||||||
|
|
||||||
|
revalidatePath('/events')
|
||||||
|
revalidatePath('/')
|
||||||
|
|
||||||
|
return { success: true, message: 'Inscription annulée' }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Unregistration error:', error)
|
||||||
|
return { success: false, error: 'Une erreur est survenue lors de l\'annulation' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
46
actions/profile/update-password.ts
Normal file
46
actions/profile/update-password.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
import { auth } from '@/lib/auth'
|
||||||
|
import { userService } from '@/services/users/user.service'
|
||||||
|
import {
|
||||||
|
ValidationError,
|
||||||
|
NotFoundError,
|
||||||
|
} from '@/services/errors'
|
||||||
|
|
||||||
|
export async function updatePassword(data: {
|
||||||
|
currentPassword: string
|
||||||
|
newPassword: string
|
||||||
|
confirmPassword: string
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
const session = await auth()
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
return { success: false, error: 'Non authentifié' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await userService.validateAndUpdatePassword(
|
||||||
|
session.user.id,
|
||||||
|
data.currentPassword,
|
||||||
|
data.newPassword,
|
||||||
|
data.confirmPassword
|
||||||
|
)
|
||||||
|
|
||||||
|
revalidatePath('/profile')
|
||||||
|
|
||||||
|
return { success: true, message: 'Mot de passe modifié avec succès' }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating password:', 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: 'Erreur lors de la modification du mot de passe' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
63
actions/profile/update-profile.ts
Normal file
63
actions/profile/update-profile.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
import { auth } from '@/lib/auth'
|
||||||
|
import { userService } from '@/services/users/user.service'
|
||||||
|
import { CharacterClass } from '@/prisma/generated/prisma/client'
|
||||||
|
import {
|
||||||
|
ValidationError,
|
||||||
|
ConflictError,
|
||||||
|
} from '@/services/errors'
|
||||||
|
|
||||||
|
export async function updateProfile(data: {
|
||||||
|
username?: string
|
||||||
|
avatar?: string | null
|
||||||
|
bio?: string | null
|
||||||
|
characterClass?: string | null
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
const session = await auth()
|
||||||
|
|
||||||
|
if (!session?.user) {
|
||||||
|
return { success: false, error: 'Non authentifié' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedUser = await userService.validateAndUpdateUserProfile(
|
||||||
|
session.user.id,
|
||||||
|
{
|
||||||
|
username: data.username,
|
||||||
|
avatar: data.avatar,
|
||||||
|
bio: data.bio,
|
||||||
|
characterClass: data.characterClass ? (data.characterClass as CharacterClass) : null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: true,
|
||||||
|
email: true,
|
||||||
|
username: true,
|
||||||
|
avatar: true,
|
||||||
|
bio: true,
|
||||||
|
characterClass: true,
|
||||||
|
hp: true,
|
||||||
|
maxHp: true,
|
||||||
|
xp: true,
|
||||||
|
maxXp: true,
|
||||||
|
level: true,
|
||||||
|
score: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
revalidatePath('/profile')
|
||||||
|
revalidatePath('/')
|
||||||
|
|
||||||
|
return { success: true, data: updatedUser }
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating profile:', error)
|
||||||
|
|
||||||
|
if (error instanceof ValidationError || error instanceof ConflictError) {
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Erreur lors de la mise à jour du profil' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
import { NextResponse } from "next/server";
|
|
||||||
import { auth } from "@/lib/auth";
|
|
||||||
import { eventService } from "@/services/events/event.service";
|
|
||||||
import { Role } from "@/prisma/generated/prisma/client";
|
|
||||||
import { ValidationError, NotFoundError } from "@/services/errors";
|
|
||||||
|
|
||||||
export async function PUT(
|
|
||||||
request: Request,
|
|
||||||
{ params }: { params: Promise<{ id: string }> }
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const session = await auth();
|
|
||||||
|
|
||||||
if (!session?.user || session.user.role !== Role.ADMIN) {
|
|
||||||
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const { id } = await params;
|
|
||||||
const body = await request.json();
|
|
||||||
const { date, name, description, type, room, time, maxPlaces } = body;
|
|
||||||
// Le statut est ignoré s'il est fourni, il sera calculé automatiquement
|
|
||||||
|
|
||||||
const event = await eventService.validateAndUpdateEvent(id, {
|
|
||||||
date,
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
type,
|
|
||||||
room,
|
|
||||||
time,
|
|
||||||
maxPlaces,
|
|
||||||
});
|
|
||||||
|
|
||||||
return NextResponse.json(event);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating event:", error);
|
|
||||||
|
|
||||||
if (error instanceof ValidationError) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
|
||||||
}
|
|
||||||
if (error instanceof NotFoundError) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Erreur lors de la mise à jour de l'événement" },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
request: Request,
|
|
||||||
{ params }: { params: Promise<{ id: string }> }
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const session = await auth();
|
|
||||||
|
|
||||||
if (!session?.user || session.user.role !== Role.ADMIN) {
|
|
||||||
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const { id } = await params;
|
|
||||||
|
|
||||||
// Vérifier que l'événement existe
|
|
||||||
const existingEvent = await eventService.getEventById(id);
|
|
||||||
|
|
||||||
if (!existingEvent) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Événement non trouvé" },
|
|
||||||
{ status: 404 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await eventService.deleteEvent(id);
|
|
||||||
|
|
||||||
return NextResponse.json({ success: true });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error deleting event:", error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Erreur lors de la suppression de l'événement" },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ import { NextResponse } from "next/server";
|
|||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import { eventService } from "@/services/events/event.service";
|
import { eventService } from "@/services/events/event.service";
|
||||||
import { Role } from "@/prisma/generated/prisma/client";
|
import { Role } from "@/prisma/generated/prisma/client";
|
||||||
import { ValidationError } from "@/services/errors";
|
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
@@ -40,38 +39,3 @@ export async function GET() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
try {
|
|
||||||
const session = await auth();
|
|
||||||
|
|
||||||
if (!session?.user || session.user.role !== Role.ADMIN) {
|
|
||||||
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const body = await request.json();
|
|
||||||
const { date, name, description, type, room, time, maxPlaces } = body;
|
|
||||||
|
|
||||||
const event = await eventService.validateAndCreateEvent({
|
|
||||||
date,
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
type,
|
|
||||||
room,
|
|
||||||
time,
|
|
||||||
maxPlaces,
|
|
||||||
});
|
|
||||||
|
|
||||||
return NextResponse.json(event);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error creating event:", error);
|
|
||||||
|
|
||||||
if (error instanceof ValidationError) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Erreur lors de la création de l'événement" },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -23,30 +23,3 @@ export async function GET() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function PUT(request: Request) {
|
|
||||||
try {
|
|
||||||
const session = await auth();
|
|
||||||
|
|
||||||
if (!session?.user || session.user.role !== Role.ADMIN) {
|
|
||||||
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const body = await request.json();
|
|
||||||
const { homeBackground, eventsBackground, leaderboardBackground } = body;
|
|
||||||
|
|
||||||
const preferences = await sitePreferencesService.updateSitePreferences({
|
|
||||||
homeBackground,
|
|
||||||
eventsBackground,
|
|
||||||
leaderboardBackground,
|
|
||||||
});
|
|
||||||
|
|
||||||
return NextResponse.json(preferences);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating admin preferences:", error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Erreur lors de la mise à jour des préférences" },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
import { NextResponse } from "next/server";
|
|
||||||
import { auth } from "@/lib/auth";
|
|
||||||
import { userService } from "@/services/users/user.service";
|
|
||||||
import { userStatsService } from "@/services/users/user-stats.service";
|
|
||||||
import { Role } from "@/prisma/generated/prisma/client";
|
|
||||||
import {
|
|
||||||
ValidationError,
|
|
||||||
NotFoundError,
|
|
||||||
ConflictError,
|
|
||||||
} from "@/services/errors";
|
|
||||||
|
|
||||||
export async function PUT(
|
|
||||||
request: Request,
|
|
||||||
{ params }: { params: Promise<{ id: string }> }
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const session = await auth();
|
|
||||||
|
|
||||||
if (!session?.user || session.user.role !== Role.ADMIN) {
|
|
||||||
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const { id } = await params;
|
|
||||||
const body = await request.json();
|
|
||||||
const { username, avatar, hpDelta, xpDelta, score, level, role } = body;
|
|
||||||
|
|
||||||
// Valider username si fourni
|
|
||||||
if (username !== undefined) {
|
|
||||||
try {
|
|
||||||
await userService.validateAndUpdateUserProfile(id, { username });
|
|
||||||
} catch (error) {
|
|
||||||
if (
|
|
||||||
error instanceof ValidationError ||
|
|
||||||
error instanceof ConflictError
|
|
||||||
) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mettre à jour stats et profil
|
|
||||||
const updatedUser = await userStatsService.updateUserStatsAndProfile(
|
|
||||||
id,
|
|
||||||
{ username, avatar, hpDelta, xpDelta, score, level, role },
|
|
||||||
{
|
|
||||||
id: true,
|
|
||||||
username: true,
|
|
||||||
email: true,
|
|
||||||
role: true,
|
|
||||||
score: true,
|
|
||||||
level: true,
|
|
||||||
hp: true,
|
|
||||||
maxHp: true,
|
|
||||||
xp: true,
|
|
||||||
maxXp: true,
|
|
||||||
avatar: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return NextResponse.json(updatedUser);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating user:", error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Erreur lors de la mise à jour de l'utilisateur" },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
request: Request,
|
|
||||||
{ params }: { params: Promise<{ id: string }> }
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const session = await auth();
|
|
||||||
|
|
||||||
if (!session?.user || session.user.role !== Role.ADMIN) {
|
|
||||||
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const { id } = await params;
|
|
||||||
|
|
||||||
await userService.validateAndDeleteUser(id, session.user.id);
|
|
||||||
|
|
||||||
return NextResponse.json({ success: true });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error deleting user:", error);
|
|
||||||
|
|
||||||
if (error instanceof ValidationError) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
|
||||||
}
|
|
||||||
if (error instanceof NotFoundError) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Erreur lors de la suppression de l'utilisateur" },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +1,7 @@
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import { eventRegistrationService } from "@/services/events/event-registration.service";
|
import { eventRegistrationService } from "@/services/events/event-registration.service";
|
||||||
import {
|
|
||||||
ValidationError,
|
|
||||||
NotFoundError,
|
|
||||||
ConflictError,
|
|
||||||
} from "@/services/errors";
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: Request,
|
|
||||||
{ params }: { params: Promise<{ id: string }> }
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const session = await auth();
|
|
||||||
|
|
||||||
if (!session?.user?.id) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Vous devez être connecté pour vous inscrire" },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { id: eventId } = await params;
|
|
||||||
|
|
||||||
const registration = await eventRegistrationService.validateAndRegisterUser(
|
|
||||||
session.user.id,
|
|
||||||
eventId
|
|
||||||
);
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{ message: "Inscription réussie", registration },
|
|
||||||
{ status: 201 }
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Registration error:", error);
|
|
||||||
|
|
||||||
if (
|
|
||||||
error instanceof ValidationError ||
|
|
||||||
error instanceof ConflictError
|
|
||||||
) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
|
||||||
}
|
|
||||||
if (error instanceof NotFoundError) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Une erreur est survenue lors de l'inscription" },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
request: Request,
|
|
||||||
{ params }: { params: Promise<{ id: string }> }
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const session = await auth();
|
|
||||||
|
|
||||||
if (!session?.user?.id) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Vous devez être connecté" },
|
|
||||||
{ status: 401 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { id: eventId } = await params;
|
|
||||||
|
|
||||||
// Supprimer l'inscription
|
|
||||||
await eventRegistrationService.unregisterUserFromEvent(
|
|
||||||
session.user.id,
|
|
||||||
eventId
|
|
||||||
);
|
|
||||||
|
|
||||||
return NextResponse.json({ message: "Inscription annulée" });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Unregistration error:", error);
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Une erreur est survenue lors de l'annulation" },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|||||||
@@ -1,48 +1,7 @@
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import { eventFeedbackService } from "@/services/events/event-feedback.service";
|
import { eventFeedbackService } from "@/services/events/event-feedback.service";
|
||||||
import {
|
|
||||||
ValidationError,
|
|
||||||
NotFoundError,
|
|
||||||
} from "@/services/errors";
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: Request,
|
|
||||||
{ params }: { params: Promise<{ eventId: string }> }
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const session = await auth();
|
|
||||||
if (!session?.user?.id) {
|
|
||||||
return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const { eventId } = await params;
|
|
||||||
const body = await request.json();
|
|
||||||
const { rating, comment } = body;
|
|
||||||
|
|
||||||
const feedback = await eventFeedbackService.validateAndCreateFeedback(
|
|
||||||
session.user.id,
|
|
||||||
eventId,
|
|
||||||
{ rating, comment }
|
|
||||||
);
|
|
||||||
|
|
||||||
return NextResponse.json({ success: true, feedback });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error saving feedback:", error);
|
|
||||||
|
|
||||||
if (error instanceof ValidationError) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
|
||||||
}
|
|
||||||
if (error instanceof NotFoundError) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Erreur lors de l'enregistrement du feedback" },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
import { NextResponse } from "next/server";
|
|
||||||
import { auth } from "@/lib/auth";
|
|
||||||
import { userService } from "@/services/users/user.service";
|
|
||||||
import { ValidationError, NotFoundError } from "@/services/errors";
|
|
||||||
|
|
||||||
export async function PUT(request: Request) {
|
|
||||||
try {
|
|
||||||
const session = await auth();
|
|
||||||
|
|
||||||
if (!session?.user) {
|
|
||||||
return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const body = await request.json();
|
|
||||||
const { currentPassword, newPassword, confirmPassword } = body;
|
|
||||||
|
|
||||||
await userService.validateAndUpdatePassword(
|
|
||||||
session.user.id,
|
|
||||||
currentPassword,
|
|
||||||
newPassword,
|
|
||||||
confirmPassword
|
|
||||||
);
|
|
||||||
|
|
||||||
return NextResponse.json({ message: "Mot de passe modifié avec succès" });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating password:", error);
|
|
||||||
|
|
||||||
if (error instanceof ValidationError) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
|
||||||
}
|
|
||||||
if (error instanceof NotFoundError) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Erreur lors de la modification du mot de passe" },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import { userService } from "@/services/users/user.service";
|
import { userService } from "@/services/users/user.service";
|
||||||
import {
|
|
||||||
ValidationError,
|
|
||||||
ConflictError,
|
|
||||||
NotFoundError,
|
|
||||||
} from "@/services/errors";
|
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
@@ -48,47 +43,3 @@ export async function GET() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function PUT(request: Request) {
|
|
||||||
try {
|
|
||||||
const session = await auth();
|
|
||||||
|
|
||||||
if (!session?.user) {
|
|
||||||
return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const body = await request.json();
|
|
||||||
const { username, avatar, bio, characterClass } = body;
|
|
||||||
|
|
||||||
const updatedUser = await userService.validateAndUpdateUserProfile(
|
|
||||||
session.user.id,
|
|
||||||
{ username, avatar, bio, characterClass },
|
|
||||||
{
|
|
||||||
id: true,
|
|
||||||
email: true,
|
|
||||||
username: true,
|
|
||||||
avatar: true,
|
|
||||||
bio: true,
|
|
||||||
characterClass: true,
|
|
||||||
hp: true,
|
|
||||||
maxHp: true,
|
|
||||||
xp: true,
|
|
||||||
maxXp: true,
|
|
||||||
level: true,
|
|
||||||
score: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return NextResponse.json(updatedUser);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating profile:", error);
|
|
||||||
|
|
||||||
if (error instanceof ValidationError || error instanceof ConflictError) {
|
|
||||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "Erreur lors de la mise à jour du profil" },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, type FormEvent } from "react";
|
import { useState, useEffect, useTransition, type FormEvent } from "react";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { useRouter, useParams } from "next/navigation";
|
import { useRouter, useParams } from "next/navigation";
|
||||||
import Navigation from "@/components/Navigation";
|
import Navigation from "@/components/Navigation";
|
||||||
|
import { createFeedback } from "@/actions/events/feedback";
|
||||||
|
|
||||||
interface Event {
|
interface Event {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -38,6 +39,7 @@ export default function FeedbackPageClient({
|
|||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [success, setSuccess] = useState(false);
|
const [success, setSuccess] = useState(false);
|
||||||
|
const [, startTransition] = useTransition();
|
||||||
|
|
||||||
const [rating, setRating] = useState(0);
|
const [rating, setRating] = useState(0);
|
||||||
const [comment, setComment] = useState("");
|
const [comment, setComment] = useState("");
|
||||||
@@ -95,37 +97,38 @@ export default function FeedbackPageClient({
|
|||||||
|
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
|
|
||||||
|
startTransition(async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/feedback/${eventId}`, {
|
const result = await createFeedback(eventId, {
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
rating,
|
rating,
|
||||||
comment: comment.trim() || null,
|
comment: comment.trim() || null,
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
if (!result.success) {
|
||||||
|
setError(result.error || "Erreur lors de l'enregistrement");
|
||||||
if (!response.ok) {
|
setSubmitting(false);
|
||||||
setError(data.error || "Erreur lors de l'enregistrement");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSuccess(true);
|
setSuccess(true);
|
||||||
setExistingFeedback(data.feedback);
|
if (result.data) {
|
||||||
|
setExistingFeedback({
|
||||||
|
id: result.data.id,
|
||||||
|
rating: result.data.rating,
|
||||||
|
comment: result.data.comment,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Rediriger après 2 secondes
|
// Rediriger après 2 secondes
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
router.push("/events");
|
router.push("/events");
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} catch {
|
} catch {
|
||||||
setError("Erreur lors de l'enregistrement");
|
setError("Erreur lors de l'enregistrement");
|
||||||
} finally {
|
} finally {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (status === "loading" || loading) {
|
if (status === "loading" || loading) {
|
||||||
@@ -262,4 +265,3 @@ export default function FeedbackPageClient({
|
|||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useState, useEffect, useMemo } from "react";
|
import { useState, useEffect, useMemo } from "react";
|
||||||
import ImageSelector from "@/components/ImageSelector";
|
import ImageSelector from "@/components/ImageSelector";
|
||||||
|
import { updateSitePreferences } from "@/actions/admin/preferences";
|
||||||
|
|
||||||
interface SitePreferences {
|
interface SitePreferences {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -90,40 +91,33 @@ export default function BackgroundPreferences({
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch("/api/admin/preferences", {
|
const result = await updateSitePreferences(apiData);
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(apiData),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (result.success && result.data) {
|
||||||
const data = await response.json();
|
setPreferences(result.data);
|
||||||
setPreferences(data);
|
|
||||||
// Réinitialiser formData avec les nouvelles valeurs (ou images par défaut)
|
// Réinitialiser formData avec les nouvelles valeurs (ou images par défaut)
|
||||||
setFormData({
|
setFormData({
|
||||||
homeBackground: getFormValue(
|
homeBackground: getFormValue(
|
||||||
data.homeBackground,
|
result.data.homeBackground,
|
||||||
DEFAULT_IMAGES.home
|
DEFAULT_IMAGES.home
|
||||||
),
|
),
|
||||||
eventsBackground: getFormValue(
|
eventsBackground: getFormValue(
|
||||||
data.eventsBackground,
|
result.data.eventsBackground,
|
||||||
DEFAULT_IMAGES.events
|
DEFAULT_IMAGES.events
|
||||||
),
|
),
|
||||||
leaderboardBackground: getFormValue(
|
leaderboardBackground: getFormValue(
|
||||||
data.leaderboardBackground,
|
result.data.leaderboardBackground,
|
||||||
DEFAULT_IMAGES.leaderboard
|
DEFAULT_IMAGES.leaderboard
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
} else {
|
} else {
|
||||||
const errorData = await response.json();
|
console.error("Error updating preferences:", result.error);
|
||||||
console.error("Error updating preferences:", errorData);
|
alert(result.error || "Erreur lors de la mise à jour");
|
||||||
alert(errorData.error || "Erreur lors de la mise à jour");
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating preferences:", error);
|
console.error("Error updating preferences:", error);
|
||||||
|
alert("Erreur lors de la mise à jour");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useTransition } from "react";
|
||||||
import { calculateEventStatus } from "@/lib/eventStatus";
|
import { calculateEventStatus } from "@/lib/eventStatus";
|
||||||
|
import { createEvent, updateEvent, deleteEvent } from "@/actions/admin/events";
|
||||||
|
|
||||||
interface Event {
|
interface Event {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -124,29 +125,20 @@ export default function EventManagement() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [, startTransition] = useTransition();
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
|
startTransition(async () => {
|
||||||
try {
|
try {
|
||||||
let response;
|
let result;
|
||||||
if (isCreating) {
|
if (isCreating) {
|
||||||
response = await fetch("/api/admin/events", {
|
result = await createEvent(formData);
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(formData),
|
|
||||||
});
|
|
||||||
} else if (editingEvent) {
|
} else if (editingEvent) {
|
||||||
response = await fetch(`/api/admin/events/${editingEvent.id}`, {
|
result = await updateEvent(editingEvent.id, formData);
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(formData),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response?.ok) {
|
if (result?.success) {
|
||||||
await fetchEvents();
|
await fetchEvents();
|
||||||
setEditingEvent(null);
|
setEditingEvent(null);
|
||||||
setIsCreating(false);
|
setIsCreating(false);
|
||||||
@@ -160,8 +152,7 @@ export default function EventManagement() {
|
|||||||
maxPlaces: undefined,
|
maxPlaces: undefined,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const error = await response?.json();
|
alert(result?.error || "Erreur lors de la sauvegarde");
|
||||||
alert(error.error || "Erreur lors de la sauvegarde");
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error saving event:", error);
|
console.error("Error saving event:", error);
|
||||||
@@ -169,6 +160,7 @@ export default function EventManagement() {
|
|||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (eventId: string) => {
|
const handleDelete = async (eventId: string) => {
|
||||||
@@ -176,21 +168,20 @@ export default function EventManagement() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startTransition(async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/admin/events/${eventId}`, {
|
const result = await deleteEvent(eventId);
|
||||||
method: "DELETE",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (result.success) {
|
||||||
await fetchEvents();
|
await fetchEvents();
|
||||||
} else {
|
} else {
|
||||||
const error = await response.json();
|
alert(result.error || "Erreur lors de la suppression");
|
||||||
alert(error.error || "Erreur lors de la suppression");
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error deleting event:", error);
|
console.error("Error deleting event:", error);
|
||||||
alert("Erreur lors de la suppression");
|
alert("Erreur lors de la suppression");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, useMemo, useRef } from "react";
|
import { useState, useEffect, useMemo, useRef, useTransition } from "react";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { calculateEventStatus } from "@/lib/eventStatus";
|
import { calculateEventStatus } from "@/lib/eventStatus";
|
||||||
import FeedbackModal from "@/components/FeedbackModal";
|
import FeedbackModal from "@/components/FeedbackModal";
|
||||||
|
import {
|
||||||
|
registerForEvent,
|
||||||
|
unregisterFromEvent,
|
||||||
|
} from "@/actions/events/register";
|
||||||
|
|
||||||
interface Event {
|
interface Event {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -526,6 +530,8 @@ export default function EventsPageSection({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [, startTransition] = useTransition();
|
||||||
|
|
||||||
const handleRegister = async (eventId: string) => {
|
const handleRegister = async (eventId: string) => {
|
||||||
if (!session?.user?.id) {
|
if (!session?.user?.id) {
|
||||||
router.push("/login");
|
router.push("/login");
|
||||||
@@ -535,53 +541,38 @@ export default function EventsPageSection({
|
|||||||
setLoading((prev) => ({ ...prev, [eventId]: true }));
|
setLoading((prev) => ({ ...prev, [eventId]: true }));
|
||||||
setError("");
|
setError("");
|
||||||
|
|
||||||
try {
|
startTransition(async () => {
|
||||||
const response = await fetch(`/api/events/${eventId}/register`, {
|
const result = await registerForEvent(eventId);
|
||||||
method: "POST",
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
setError(data.error || "Une erreur est survenue");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
setRegistrations((prev) => ({
|
setRegistrations((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[eventId]: true,
|
[eventId]: true,
|
||||||
}));
|
}));
|
||||||
} catch {
|
} else {
|
||||||
setError("Une erreur est survenue");
|
setError(result.error || "Une erreur est survenue");
|
||||||
} finally {
|
|
||||||
setLoading((prev) => ({ ...prev, [eventId]: false }));
|
|
||||||
}
|
}
|
||||||
|
setLoading((prev) => ({ ...prev, [eventId]: false }));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUnregister = async (eventId: string) => {
|
const handleUnregister = async (eventId: string) => {
|
||||||
setLoading((prev) => ({ ...prev, [eventId]: true }));
|
setLoading((prev) => ({ ...prev, [eventId]: true }));
|
||||||
setError("");
|
setError("");
|
||||||
|
|
||||||
try {
|
startTransition(async () => {
|
||||||
const response = await fetch(`/api/events/${eventId}/register`, {
|
const result = await unregisterFromEvent(eventId);
|
||||||
method: "DELETE",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
setError(data.error || "Une erreur est survenue");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
setRegistrations((prev) => ({
|
setRegistrations((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[eventId]: false,
|
[eventId]: false,
|
||||||
}));
|
}));
|
||||||
} catch {
|
} else {
|
||||||
setError("Une erreur est survenue");
|
setError(result.error || "Une erreur est survenue");
|
||||||
} finally {
|
|
||||||
setLoading((prev) => ({ ...prev, [eventId]: false }));
|
|
||||||
}
|
}
|
||||||
|
setLoading((prev) => ({ ...prev, [eventId]: false }));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, type FormEvent } from "react";
|
import { useState, useEffect, useTransition, type FormEvent } from "react";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
|
import { createFeedback } from "@/actions/events/feedback";
|
||||||
|
|
||||||
interface Event {
|
interface Event {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -36,6 +37,7 @@ export default function FeedbackModal({
|
|||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [success, setSuccess] = useState(false);
|
const [success, setSuccess] = useState(false);
|
||||||
|
const [, startTransition] = useTransition();
|
||||||
|
|
||||||
const [rating, setRating] = useState(0);
|
const [rating, setRating] = useState(0);
|
||||||
const [comment, setComment] = useState("");
|
const [comment, setComment] = useState("");
|
||||||
@@ -118,27 +120,27 @@ export default function FeedbackModal({
|
|||||||
|
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
|
|
||||||
|
startTransition(async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/feedback/${eventId}`, {
|
const result = await createFeedback(eventId, {
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
rating,
|
rating,
|
||||||
comment: comment.trim() || null,
|
comment: comment.trim() || null,
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
if (!result.success) {
|
||||||
|
setError(result.error || "Erreur lors de l'enregistrement");
|
||||||
if (!response.ok) {
|
setSubmitting(false);
|
||||||
setError(data.error || "Erreur lors de l'enregistrement");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSuccess(true);
|
setSuccess(true);
|
||||||
setExistingFeedback(data.feedback);
|
if (result.data) {
|
||||||
|
setExistingFeedback({
|
||||||
|
id: result.data.id,
|
||||||
|
rating: result.data.rating,
|
||||||
|
comment: result.data.comment,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Fermer la modale après 1.5 secondes
|
// Fermer la modale après 1.5 secondes
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -149,6 +151,7 @@ export default function FeedbackModal({
|
|||||||
} finally {
|
} finally {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useRef, type ChangeEvent } from "react";
|
import { useState, useRef, useTransition, type ChangeEvent } from "react";
|
||||||
import Avatar from "./Avatar";
|
import Avatar from "./Avatar";
|
||||||
|
import { updateProfile } from "@/actions/profile/update-profile";
|
||||||
|
import { updatePassword } from "@/actions/profile/update-password";
|
||||||
|
|
||||||
type CharacterClass =
|
type CharacterClass =
|
||||||
| "WARRIOR"
|
| "WARRIOR"
|
||||||
@@ -46,7 +48,7 @@ export default function ProfileForm({
|
|||||||
backgroundImage,
|
backgroundImage,
|
||||||
}: ProfileFormProps) {
|
}: ProfileFormProps) {
|
||||||
const [profile, setProfile] = useState<UserProfile>(initialProfile);
|
const [profile, setProfile] = useState<UserProfile>(initialProfile);
|
||||||
const [saving, setSaving] = useState(false);
|
const [isPending, startTransition] = useTransition();
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [success, setSuccess] = useState<string | null>(null);
|
const [success, setSuccess] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -64,7 +66,7 @@ export default function ProfileForm({
|
|||||||
const [currentPassword, setCurrentPassword] = useState("");
|
const [currentPassword, setCurrentPassword] = useState("");
|
||||||
const [newPassword, setNewPassword] = useState("");
|
const [newPassword, setNewPassword] = useState("");
|
||||||
const [confirmPassword, setConfirmPassword] = useState("");
|
const [confirmPassword, setConfirmPassword] = useState("");
|
||||||
const [changingPassword, setChangingPassword] = useState(false);
|
const [isChangingPassword, startPasswordTransition] = useTransition();
|
||||||
|
|
||||||
const handleAvatarUpload = async (e: ChangeEvent<HTMLInputElement>) => {
|
const handleAvatarUpload = async (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
@@ -104,81 +106,57 @@ export default function ProfileForm({
|
|||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setSaving(true);
|
|
||||||
setError(null);
|
setError(null);
|
||||||
setSuccess(null);
|
setSuccess(null);
|
||||||
|
|
||||||
try {
|
startTransition(async () => {
|
||||||
const response = await fetch("/api/profile", {
|
const result = await updateProfile({
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
username,
|
username,
|
||||||
avatar,
|
avatar,
|
||||||
bio,
|
bio,
|
||||||
characterClass,
|
characterClass,
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (result.success && result.data) {
|
||||||
const data = await response.json();
|
setProfile({
|
||||||
setProfile(data);
|
...result.data,
|
||||||
setBio(data.bio || null);
|
createdAt: result.data.createdAt instanceof Date
|
||||||
setCharacterClass(data.characterClass || null);
|
? result.data.createdAt.toISOString()
|
||||||
|
: result.data.createdAt,
|
||||||
|
} as UserProfile);
|
||||||
|
setBio(result.data.bio || null);
|
||||||
|
setCharacterClass(result.data.characterClass as CharacterClass || null);
|
||||||
setSuccess("Profil mis à jour avec succès");
|
setSuccess("Profil mis à jour avec succès");
|
||||||
setTimeout(() => setSuccess(null), 3000);
|
setTimeout(() => setSuccess(null), 3000);
|
||||||
} else {
|
} else {
|
||||||
const errorData = await response.json();
|
setError(result.error || "Erreur lors de la mise à jour");
|
||||||
setError(errorData.error || "Erreur lors de la mise à jour");
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error updating profile:", err);
|
|
||||||
setError("Erreur lors de la mise à jour du profil");
|
|
||||||
} finally {
|
|
||||||
setSaving(false);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePasswordChange = async (e: React.FormEvent) => {
|
const handlePasswordChange = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setChangingPassword(true);
|
|
||||||
setError(null);
|
setError(null);
|
||||||
setSuccess(null);
|
setSuccess(null);
|
||||||
|
|
||||||
try {
|
startPasswordTransition(async () => {
|
||||||
const response = await fetch("/api/profile/password", {
|
const result = await updatePassword({
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
currentPassword,
|
currentPassword,
|
||||||
newPassword,
|
newPassword,
|
||||||
confirmPassword,
|
confirmPassword,
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (result.success) {
|
||||||
setSuccess("Mot de passe modifié avec succès");
|
setSuccess(result.message || "Mot de passe modifié avec succès");
|
||||||
setCurrentPassword("");
|
setCurrentPassword("");
|
||||||
setNewPassword("");
|
setNewPassword("");
|
||||||
setConfirmPassword("");
|
setConfirmPassword("");
|
||||||
setShowPasswordForm(false);
|
setShowPasswordForm(false);
|
||||||
setTimeout(() => setSuccess(null), 3000);
|
setTimeout(() => setSuccess(null), 3000);
|
||||||
} else {
|
} else {
|
||||||
const errorData = await response.json();
|
setError(result.error || "Erreur lors de la modification du mot de passe");
|
||||||
setError(
|
|
||||||
errorData.error || "Erreur lors de la modification du mot de passe"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error changing password:", err);
|
|
||||||
setError("Erreur lors de la modification du mot de passe");
|
|
||||||
} finally {
|
|
||||||
setChangingPassword(false);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const hpPercentage = (profile.hp / profile.maxHp) * 100;
|
const hpPercentage = (profile.hp / profile.maxHp) * 100;
|
||||||
@@ -529,10 +507,10 @@ export default function ProfileForm({
|
|||||||
<div className="flex justify-end gap-4 pt-4 border-t border-pixel-gold/20">
|
<div className="flex justify-end gap-4 pt-4 border-t border-pixel-gold/20">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={saving}
|
disabled={isPending}
|
||||||
className="px-6 py-2 border border-pixel-gold/50 bg-black/40 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition disabled:opacity-50 disabled:cursor-not-allowed"
|
className="px-6 py-2 border border-pixel-gold/50 bg-black/40 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{saving ? "Enregistrement..." : "Enregistrer les modifications"}
|
{isPending ? "Enregistrement..." : "Enregistrer les modifications"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -616,10 +594,10 @@ export default function ProfileForm({
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={changingPassword}
|
disabled={isChangingPassword}
|
||||||
className="px-4 py-2 border border-pixel-gold/50 bg-black/40 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition disabled:opacity-50 disabled:cursor-not-allowed"
|
className="px-4 py-2 border border-pixel-gold/50 bg-black/40 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{changingPassword
|
{isChangingPassword
|
||||||
? "Modification..."
|
? "Modification..."
|
||||||
: "Modifier le mot de passe"}
|
: "Modifier le mot de passe"}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useTransition } from "react";
|
||||||
import Avatar from "./Avatar";
|
import Avatar from "./Avatar";
|
||||||
|
import { updateUser, deleteUser } from "@/actions/admin/users";
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -35,6 +36,7 @@ export default function UserManagement() {
|
|||||||
const [editingUser, setEditingUser] = useState<EditingUser | null>(null);
|
const [editingUser, setEditingUser] = useState<EditingUser | null>(null);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [deletingUserId, setDeletingUserId] = useState<string | null>(null);
|
const [deletingUserId, setDeletingUserId] = useState<string | null>(null);
|
||||||
|
const [, startTransition] = useTransition();
|
||||||
const [uploadingAvatar, setUploadingAvatar] = useState<string | null>(null);
|
const [uploadingAvatar, setUploadingAvatar] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -72,6 +74,7 @@ export default function UserManagement() {
|
|||||||
if (!editingUser) return;
|
if (!editingUser) return;
|
||||||
|
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
|
startTransition(async () => {
|
||||||
try {
|
try {
|
||||||
const body: {
|
const body: {
|
||||||
username?: string;
|
username?: string;
|
||||||
@@ -105,20 +108,13 @@ export default function UserManagement() {
|
|||||||
body.role = editingUser.role;
|
body.role = editingUser.role;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`/api/admin/users/${editingUser.userId}`, {
|
const result = await updateUser(editingUser.userId, body);
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (result.success) {
|
||||||
await fetchUsers();
|
await fetchUsers();
|
||||||
setEditingUser(null);
|
setEditingUser(null);
|
||||||
} else {
|
} else {
|
||||||
const error = await response.json();
|
alert(result.error || "Erreur lors de la mise à jour");
|
||||||
alert(error.error || "Erreur lors de la mise à jour");
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating user:", error);
|
console.error("Error updating user:", error);
|
||||||
@@ -126,6 +122,7 @@ export default function UserManagement() {
|
|||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
@@ -143,15 +140,12 @@ export default function UserManagement() {
|
|||||||
|
|
||||||
setDeletingUserId(userId);
|
setDeletingUserId(userId);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/admin/users/${userId}`, {
|
const result = await deleteUser(userId);
|
||||||
method: "DELETE",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (result.success) {
|
||||||
await fetchUsers();
|
await fetchUsers();
|
||||||
} else {
|
} else {
|
||||||
const error = await response.json();
|
alert(result.error || "Erreur lors de la suppression");
|
||||||
alert(error.error || "Erreur lors de la suppression");
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error deleting user:", error);
|
console.error("Error deleting user:", error);
|
||||||
|
|||||||
@@ -81,9 +81,6 @@ export class EventRegistrationService {
|
|||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
},
|
},
|
||||||
select: {
|
|
||||||
eventId: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { prisma } from "../database";
|
import { prisma } from "../database";
|
||||||
import type {
|
import type {
|
||||||
Event,
|
Event,
|
||||||
EventType,
|
|
||||||
Prisma,
|
Prisma,
|
||||||
} from "@/prisma/generated/prisma/client";
|
} from "@/prisma/generated/prisma/client";
|
||||||
|
import { EventType } from "@/prisma/generated/prisma/client";
|
||||||
import { ValidationError, NotFoundError } from "../errors";
|
import { ValidationError, NotFoundError } from "../errors";
|
||||||
import { calculateEventStatus } from "@/lib/eventStatus";
|
import { calculateEventStatus } from "@/lib/eventStatus";
|
||||||
|
|
||||||
|
|||||||
@@ -225,10 +225,11 @@ export class UserStatsService {
|
|||||||
selectFields
|
selectFields
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
updatedUser = await userService.getUserById(id, selectFields);
|
const user = await userService.getUserById(id, selectFields);
|
||||||
if (!updatedUser) {
|
if (!user) {
|
||||||
throw new NotFoundError("Utilisateur");
|
throw new NotFoundError("Utilisateur");
|
||||||
}
|
}
|
||||||
|
updatedUser = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mettre à jour username/avatar si nécessaire
|
// Mettre à jour username/avatar si nécessaire
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import bcrypt from "bcryptjs";
|
|||||||
import type {
|
import type {
|
||||||
User,
|
User,
|
||||||
CharacterClass,
|
CharacterClass,
|
||||||
Role,
|
|
||||||
Prisma,
|
Prisma,
|
||||||
} from "@/prisma/generated/prisma/client";
|
} from "@/prisma/generated/prisma/client";
|
||||||
import { ValidationError, NotFoundError, ConflictError } from "../errors";
|
import { ValidationError, NotFoundError, ConflictError } from "../errors";
|
||||||
|
|||||||
Reference in New Issue
Block a user