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

This commit is contained in:
Julien Froidefond
2025-12-12 16:28:07 +01:00
parent 494ac3f503
commit db01c25de7
26 changed files with 747 additions and 743 deletions

131
actions/admin/events.ts Normal file
View 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' }
}
}

View 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
View 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' }
}
}

View 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' }
}
}

View 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' }
}
}

View 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' }
}
}

View 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' }
}
}