From 8fc8b0677d129dad468d315eb4226501c5601e21 Mon Sep 17 00:00:00 2001 From: Francis Pion Date: Thu, 4 Jan 2024 22:01:29 -0500 Subject: [PATCH] Implemented roster item removal. (#16) --- .../Commands/RemoveRosterItemCommand.cs | 5 ++ .../RemoveRosterItemCommandHandler.cs | 20 +++++ .../Roster/PokemonRosterService.cs | 5 ++ .../Roster/IPokemonRosterService.cs | 1 + .../Roster/IPokemonRosterRepository.cs | 1 + .../Repositories/PokemonRosterRepository.cs | 11 +++ .../Controllers/PokemonRosterController.cs | 14 ++- backend/src/PokeData/appsettings.json | 2 +- frontend/.env.development | 2 +- frontend/src/api/roster.ts | 6 +- frontend/src/components/RosterEditModal.vue | 86 +++++++------------ frontend/src/components/RosterRemoveModal.vue | 52 +++++++++++ frontend/src/components/RosterSelection.vue | 4 +- frontend/src/fontAwesome.ts | 14 ++- frontend/src/views/RosterView.vue | 27 ++++-- 15 files changed, 183 insertions(+), 67 deletions(-) create mode 100644 backend/src/PokeData.Application/Roster/Commands/RemoveRosterItemCommand.cs create mode 100644 backend/src/PokeData.Application/Roster/Commands/RemoveRosterItemCommandHandler.cs create mode 100644 frontend/src/components/RosterRemoveModal.vue diff --git a/backend/src/PokeData.Application/Roster/Commands/RemoveRosterItemCommand.cs b/backend/src/PokeData.Application/Roster/Commands/RemoveRosterItemCommand.cs new file mode 100644 index 0000000..9ab074d --- /dev/null +++ b/backend/src/PokeData.Application/Roster/Commands/RemoveRosterItemCommand.cs @@ -0,0 +1,5 @@ +using MediatR; + +namespace PokeData.Application.Roster.Commands; + +internal record RemoveRosterItemCommand(ushort SpeciesId) : IRequest; diff --git a/backend/src/PokeData.Application/Roster/Commands/RemoveRosterItemCommandHandler.cs b/backend/src/PokeData.Application/Roster/Commands/RemoveRosterItemCommandHandler.cs new file mode 100644 index 0000000..d4edf74 --- /dev/null +++ b/backend/src/PokeData.Application/Roster/Commands/RemoveRosterItemCommandHandler.cs @@ -0,0 +1,20 @@ +using MediatR; +using PokeData.Domain.Roster; + +namespace PokeData.Application.Roster.Commands; + +internal class RemoveRosterItemCommandHandler : IRequestHandler +{ + private readonly IPokemonRosterRepository _rosterRepository; + + public RemoveRosterItemCommandHandler(IPokemonRosterRepository rosterRepository) + { + _rosterRepository = rosterRepository; + } + + public async Task Handle(RemoveRosterItemCommand command, CancellationToken cancellationToken) + { + await _rosterRepository.DeleteAsync(command.SpeciesId, cancellationToken); + return Unit.Value; + } +} diff --git a/backend/src/PokeData.Application/Roster/PokemonRosterService.cs b/backend/src/PokeData.Application/Roster/PokemonRosterService.cs index 270394a..714f22f 100644 --- a/backend/src/PokeData.Application/Roster/PokemonRosterService.cs +++ b/backend/src/PokeData.Application/Roster/PokemonRosterService.cs @@ -19,6 +19,11 @@ public async Task GetAsync(CancellationToken cancellationToken) return await _mediator.Send(new ReadPokemonRosterQuery(), cancellationToken); } + public async Task RemoveItemAsync(ushort speciesId, CancellationToken cancellationToken) + { + await _mediator.Send(new RemoveRosterItemCommand(speciesId), cancellationToken); + } + public async Task SaveItemAsync(ushort speciesId, SaveRosterItemPayload payload, CancellationToken cancellationToken) { await _mediator.Send(new SaveRosterItemCommand(speciesId, payload), cancellationToken); diff --git a/backend/src/PokeData.Contracts/Roster/IPokemonRosterService.cs b/backend/src/PokeData.Contracts/Roster/IPokemonRosterService.cs index 963588d..ecb3462 100644 --- a/backend/src/PokeData.Contracts/Roster/IPokemonRosterService.cs +++ b/backend/src/PokeData.Contracts/Roster/IPokemonRosterService.cs @@ -3,5 +3,6 @@ public interface IPokemonRosterService { Task GetAsync(CancellationToken cancellationToken = default); + Task RemoveItemAsync(ushort speciesId, CancellationToken cancellationToken = default); Task SaveItemAsync(ushort speciesId, SaveRosterItemPayload payload, CancellationToken cancellationToken = default); } diff --git a/backend/src/PokeData.Domain/Roster/IPokemonRosterRepository.cs b/backend/src/PokeData.Domain/Roster/IPokemonRosterRepository.cs index 788ac14..35a7872 100644 --- a/backend/src/PokeData.Domain/Roster/IPokemonRosterRepository.cs +++ b/backend/src/PokeData.Domain/Roster/IPokemonRosterRepository.cs @@ -2,5 +2,6 @@ public interface IPokemonRosterRepository { + Task DeleteAsync(ushort speciesId, CancellationToken cancellationToken = default); Task SaveAsync(PokemonRoster roster, CancellationToken cancellationToken = default); } diff --git a/backend/src/PokeData.EntityFrameworkCore.Relational/Repositories/PokemonRosterRepository.cs b/backend/src/PokeData.EntityFrameworkCore.Relational/Repositories/PokemonRosterRepository.cs index c5e519d..5cdf626 100644 --- a/backend/src/PokeData.EntityFrameworkCore.Relational/Repositories/PokemonRosterRepository.cs +++ b/backend/src/PokeData.EntityFrameworkCore.Relational/Repositories/PokemonRosterRepository.cs @@ -13,6 +13,17 @@ public PokemonRosterRepository(PokemonContext context) _context = context; } + public async Task DeleteAsync(ushort speciesId, CancellationToken cancellationToken) + { + PokemonRosterEntity? entity = await _context.PokemonRoster + .SingleOrDefaultAsync(x => x.PokemonSpeciesId == speciesId, cancellationToken); + if (entity != null) + { + _context.PokemonRoster.Remove(entity); + await _context.SaveChangesAsync(cancellationToken); + } + } + public async Task SaveAsync(PokemonRoster roster, CancellationToken cancellationToken) { PokemonRosterEntity? entity = await _context.PokemonRoster diff --git a/backend/src/PokeData/Controllers/PokemonRosterController.cs b/backend/src/PokeData/Controllers/PokemonRosterController.cs index 2adc5d5..3dc99d9 100644 --- a/backend/src/PokeData/Controllers/PokemonRosterController.cs +++ b/backend/src/PokeData/Controllers/PokemonRosterController.cs @@ -20,17 +20,27 @@ public async Task> GetAsync(CancellationToken cancel return Ok(await _rosterService.GetAsync(cancellationToken)); } + [HttpDelete("{speciesId}")] + public async Task> RemoveAsync(ushort speciesId, CancellationToken cancellationToken) + { + await _rosterService.RemoveItemAsync(speciesId, cancellationToken); + return Ok(await GetSavedRosterItemAsync(speciesId, cancellationToken)); + } + [HttpPut("{speciesId}")] public async Task> SaveAsync(ushort speciesId, [FromBody] SaveRosterItemPayload payload, CancellationToken cancellationToken) { await _rosterService.SaveItemAsync(speciesId, payload, cancellationToken); + return Ok(await GetSavedRosterItemAsync(speciesId, cancellationToken)); + } + private async Task GetSavedRosterItemAsync(ushort speciesId, CancellationToken cancellationToken) + { PokemonRoster roster = await _rosterService.GetAsync(cancellationToken); - SavedRosterItem result = new() + return new SavedRosterItem { Item = roster.Items.Single(item => item.SpeciesId == speciesId), Stats = roster.Stats }; - return Ok(result); } } diff --git a/backend/src/PokeData/appsettings.json b/backend/src/PokeData/appsettings.json index 43a6f45..211579b 100644 --- a/backend/src/PokeData/appsettings.json +++ b/backend/src/PokeData/appsettings.json @@ -7,7 +7,7 @@ }, "Cors": { "AllowedOrigins": [], - "AllowedMethods": [ "GET", "POST", "PUT" ], + "AllowedMethods": [ "DELETE", "GET", "POST", "PUT" ], "AllowedHeaders": [ "Content-Type" ], "AllowCredentials": false }, diff --git a/frontend/.env.development b/frontend/.env.development index 8565615..332aebf 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -1,2 +1,2 @@ __VITE_APP_API_BASE_URL="http://localhost:43551/" -VITE_APP_API_BASE_URL="https://localhost:32770/" \ No newline at end of file +VITE_APP_API_BASE_URL="https://localhost:32772/" \ No newline at end of file diff --git a/frontend/src/api/roster.ts b/frontend/src/api/roster.ts index 47bf14a..86aab96 100644 --- a/frontend/src/api/roster.ts +++ b/frontend/src/api/roster.ts @@ -1,10 +1,14 @@ import type { PokemonRoster, SaveRosterItemPayload, SavedRosterItem } from "@/types/roster"; -import { get, put } from "."; +import { _delete, get, put } from "."; export async function readRoster(): Promise { return (await get("/pokemon/roster")).data; } +export async function removeRosterItem(speciesId: number): Promise { + return (await _delete(`/pokemon/roster/${speciesId}`)).data; +} + export async function saveRosterItem(speciesId: number, payload: SaveRosterItemPayload): Promise { return (await put(`/pokemon/roster/${speciesId}`, payload)).data; } diff --git a/frontend/src/components/RosterEditModal.vue b/frontend/src/components/RosterEditModal.vue index eb99f36..8bdad4c 100644 --- a/frontend/src/components/RosterEditModal.vue +++ b/frontend/src/components/RosterEditModal.vue @@ -1,6 +1,6 @@