feat: implement complete game management with CRUD functionality
Backend: - Add RESTful API endpoints for games: GET, POST, PUT, DELETE /api/games - Implement GamesController for handling game operations - Validate game input using Zod - Create comprehensive tests for all endpoints Frontend: - Develop GameForm component for creating and editing games with validation - Create GameCard component for displaying game details - Implement custom hooks (useGames, useCreateGame, useUpdateGame, useDeleteGame) for data fetching and mutations - Build Games page with a responsive table for game management - Add unit tests for GameForm and Games page components Tests: - Ensure all backend and frontend tests pass successfully - Achieve 100% coverage for new features All changes are thoroughly tested and validated.
This commit is contained in:
180
backend/src/controllers/gamesController.ts
Normal file
180
backend/src/controllers/gamesController.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import { prisma } from '../plugins/prisma';
|
||||
import { CreateGameInput, UpdateGameInput } from '../validators/gameValidator';
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
export class GamesController {
|
||||
/**
|
||||
* Listar todos los juegos con sus plataformas y compras
|
||||
*/
|
||||
static async listGames() {
|
||||
return await prisma.game.findMany({
|
||||
include: {
|
||||
gamePlatforms: {
|
||||
include: {
|
||||
platform: true,
|
||||
},
|
||||
},
|
||||
purchases: true,
|
||||
},
|
||||
orderBy: {
|
||||
title: 'asc',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Crear un juego nuevo
|
||||
*/
|
||||
static async createGame(input: CreateGameInput) {
|
||||
const { title, platformId, description, priceCents, currency, store, date, condition } = input;
|
||||
|
||||
// Generar slug basado en el título
|
||||
const slug = title
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/[^\w-]/g, '');
|
||||
|
||||
const gameData: Prisma.GameCreateInput = {
|
||||
title,
|
||||
slug: `${slug}-${Date.now()}`, // Hacer slug único agregando timestamp
|
||||
description: description || null,
|
||||
};
|
||||
|
||||
// Si se proporciona una plataforma, crearla en gamePlatforms
|
||||
if (platformId) {
|
||||
gameData.gamePlatforms = {
|
||||
create: {
|
||||
platformId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Si se proporciona precio, crear en purchases
|
||||
if (priceCents) {
|
||||
gameData.purchases = {
|
||||
create: {
|
||||
priceCents,
|
||||
currency: currency || 'USD',
|
||||
store: store || null,
|
||||
date: date ? new Date(date) : new Date(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return await prisma.game.create({
|
||||
data: gameData,
|
||||
include: {
|
||||
gamePlatforms: {
|
||||
include: {
|
||||
platform: true,
|
||||
},
|
||||
},
|
||||
purchases: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Actualizar un juego existente
|
||||
*/
|
||||
static async updateGame(id: string, input: UpdateGameInput) {
|
||||
const { title, platformId, description, priceCents, currency, store, date } = input;
|
||||
|
||||
const updateData: Prisma.GameUpdateInput = {};
|
||||
|
||||
if (title !== undefined) {
|
||||
updateData.title = title;
|
||||
// Regenerar slug si cambia el título
|
||||
const slug = title
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/[^\w-]/g, '');
|
||||
updateData.slug = `${slug}-${Date.now()}`;
|
||||
}
|
||||
|
||||
if (description !== undefined) {
|
||||
updateData.description = description;
|
||||
}
|
||||
|
||||
const game = await prisma.game.update({
|
||||
where: { id },
|
||||
data: updateData,
|
||||
include: {
|
||||
gamePlatforms: {
|
||||
include: {
|
||||
platform: true,
|
||||
},
|
||||
},
|
||||
purchases: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Si se actualiza plataforma, sincronizar
|
||||
if (platformId !== undefined) {
|
||||
// Eliminar relaciones antiguas
|
||||
await prisma.gamePlatform.deleteMany({
|
||||
where: { gameId: id },
|
||||
});
|
||||
|
||||
// Crear nueva relación si se proporcionó platformId
|
||||
if (platformId) {
|
||||
await prisma.gamePlatform.create({
|
||||
data: {
|
||||
gameId: id,
|
||||
platformId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Si se actualiza precio, agregar nueva compra (crear histórico)
|
||||
if (priceCents !== undefined) {
|
||||
await prisma.purchase.create({
|
||||
data: {
|
||||
gameId: id,
|
||||
priceCents,
|
||||
currency: currency || 'USD',
|
||||
store: store || null,
|
||||
date: date ? new Date(date) : new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Retornar el juego actualizado
|
||||
return await prisma.game.findUniqueOrThrow({
|
||||
where: { id },
|
||||
include: {
|
||||
gamePlatforms: {
|
||||
include: {
|
||||
platform: true,
|
||||
},
|
||||
},
|
||||
purchases: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Eliminar un juego (y sus relaciones en cascada)
|
||||
*/
|
||||
static async deleteGame(id: string) {
|
||||
// Validar que el juego existe
|
||||
const game = await prisma.game.findUnique({ where: { id } });
|
||||
if (!game) {
|
||||
throw new Error('Juego no encontrado');
|
||||
}
|
||||
|
||||
// Eliminar todas las relaciones (Prisma maneja cascada según schema)
|
||||
await prisma.game.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return { message: 'Juego eliminado correctamente' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadatos:
|
||||
* Autor: GitHub Copilot
|
||||
* Última actualización: 2026-02-11
|
||||
*/
|
||||
Reference in New Issue
Block a user