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.
181 lines
4.2 KiB
TypeScript
181 lines
4.2 KiB
TypeScript
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
|
|
*/
|