diff --git a/src/Client/Features/OfferMatching/List.razor b/src/Client/Features/OfferMatching/List.razor index c162c8e..79b133d 100644 --- a/src/Client/Features/OfferMatching/List.razor +++ b/src/Client/Features/OfferMatching/List.razor @@ -20,7 +20,7 @@ - + diff --git a/src/Client/Features/OfferMatching/List.razor.cs b/src/Client/Features/OfferMatching/List.razor.cs index dbd5a67..6113b19 100644 --- a/src/Client/Features/OfferMatching/List.razor.cs +++ b/src/Client/Features/OfferMatching/List.razor.cs @@ -1,14 +1,15 @@ -using System.Net.Http.Json; -using DynamoLeagueBlazor.Shared.Features.OfferMatching; +using DynamoLeagueBlazor.Shared.Features.OfferMatching; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; +using MudBlazor; +using System.Net.Http.Json; namespace DynamoLeagueBlazor.Client.Features.OfferMatching; public sealed partial class List : IDisposable { [Inject] private HttpClient HttpClient { get; set; } = null!; - + [Inject] private ISnackbar SnackBar { get; set; } = null!; private const string _title = "Offer Matching"; private bool _loading; private readonly CancellationTokenSource _cts = new(); @@ -19,7 +20,7 @@ protected override async Task OnInitializedAsync() try { _loading = true; - _result = await HttpClient.GetFromJsonAsync(OfferMatchingListRouteFactory.Uri, _cts.Token) ?? new(); + await LoadDataAsync(); } catch (AccessTokenNotAvailableException exception) { @@ -30,7 +31,25 @@ protected override async Task OnInitializedAsync() _loading = false; } } + private async Task LoadDataAsync() + { + _result = await HttpClient.GetFromJsonAsync(OfferMatchingListRouteFactory.Uri, _cts.Token) ?? new(); + } + + private async Task MatchPlayerAsync(int playerId, int amount) + { + var response = await HttpClient.PostAsJsonAsync(OfferMatchingListRouteFactory.Uri, new MatchPlayerRequest() { PlayerId = playerId }); + if (response.IsSuccessStatusCode) + { + SnackBar.Add("Successfully retained player.", Severity.Success); + await LoadDataAsync(); + } + else + { + SnackBar.Add("Something went wrong...", Severity.Error); + } + } public void Dispose() { _cts.Cancel(); diff --git a/src/Server/Features/FreeAgents/List.cs b/src/Server/Features/FreeAgents/List.cs index 36041dc..7382c6d 100644 --- a/src/Server/Features/FreeAgents/List.cs +++ b/src/Server/Features/FreeAgents/List.cs @@ -75,11 +75,11 @@ public ListMappingProfile() .ForMember(d => d.Team, mo => mo.MapFrom(s => s.Team != null ? s.Team.Name : string.Empty)) .ForMember(d => d.CurrentUserIsHighestBidder, mo => mo.MapFrom(s => s.Bids.Any() && - s.Bids.GetHighestBidder().TeamId == currentUserTeamId) + s.Bids.GetHighestBid().TeamId == currentUserTeamId) ) .ForMember(d => d.BiddingEnds, mo => mo.MapFrom(s => s.EndOfFreeAgency!.Value)) .ForMember(d => d.HighestBid, mo => mo.MapFrom(s => s.Bids.Any() - ? s.Bids.GetHighestBidder().Amount : 0) + ? s.Bids.GetHighestBid().Amount : 0) ); } } diff --git a/src/Server/Features/OfferMatching/List.cs b/src/Server/Features/OfferMatching/List.cs index 6906ea5..8022f94 100644 --- a/src/Server/Features/OfferMatching/List.cs +++ b/src/Server/Features/OfferMatching/List.cs @@ -26,6 +26,11 @@ public async Task GetAsync(CancellationToken cancellati { return await _mediator.Send(new ListQuery(), cancellationToken); } + [HttpPost] + public async Task PostAsync([FromBody] MatchPlayerRequest request, CancellationToken cancellationToken) + { + return await _mediator.Send(new MatchPlayerCommand(request.PlayerId), cancellationToken); + } } public record ListQuery : IRequest { } @@ -62,17 +67,42 @@ public async Task Handle(ListQuery request, Cancellatio }; } } - public class ListMappingProfile : Profile { + private const int _minimumBid = 1; public ListMappingProfile() { CreateMap() .ForMember(d => d.OfferingTeam, mo => mo.MapFrom(s => s.Team != null ? s.Team.Name : string.Empty)) .ForMember(d => d.Offer, mo => mo.MapFrom(s => s.Bids.Any() - ? s.Bids.GetHighestBidder().Amount.ToString("C0") - : string.Empty) + ? s.Bids.GetHighestBid().Amount : _minimumBid) ); } } +public record MatchPlayerCommand(int PlayerId) : IRequest { } + +public class MatchPlayerHandler : IRequestHandler +{ + private readonly ApplicationDbContext _dbContext; + + public MatchPlayerHandler(ApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle(MatchPlayerCommand request, CancellationToken cancellationToken) + { + var player = (await _dbContext.Players + .AsTracking() + .Include(p => p.Bids) + .SingleAsync(p => p.Id == request.PlayerId, cancellationToken)); + player.ContractValue = player.Bids.GetHighestBid().Amount; + player.SetToUnsigned(); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return Unit.Value; + } + +} diff --git a/src/Server/Models/Bid.cs b/src/Server/Models/Bid.cs index bb764e0..1608f6a 100644 --- a/src/Server/Models/Bid.cs +++ b/src/Server/Models/Bid.cs @@ -20,6 +20,6 @@ public Bid(int amount, int teamId, int playerId) public static class BidExtensions { - public static Bid GetHighestBidder(this ICollection bids) + public static Bid GetHighestBid(this ICollection bids) => bids.OrderByDescending(b => b.Amount).First(); } diff --git a/src/Shared/Features/OfferMatching/List.cs b/src/Shared/Features/OfferMatching/List.cs index c80977d..9e53d8e 100644 --- a/src/Shared/Features/OfferMatching/List.cs +++ b/src/Shared/Features/OfferMatching/List.cs @@ -11,7 +11,7 @@ public class OfferMatchingItem public string Position { get; set; } public string HeadShotUrl { get; set; } public string OfferingTeam { get; set; } - public string Offer { get; set; } + public int Offer { get; set; } } } diff --git a/src/Shared/Features/OfferMatching/MatchPlayer.cs b/src/Shared/Features/OfferMatching/MatchPlayer.cs new file mode 100644 index 0000000..c6cb874 --- /dev/null +++ b/src/Shared/Features/OfferMatching/MatchPlayer.cs @@ -0,0 +1,7 @@ +namespace DynamoLeagueBlazor.Shared.Features.OfferMatching +{ + public class MatchPlayerRequest + { + public int PlayerId { get; set; } + } +} diff --git a/src/Tests/FakeFactory.cs b/src/Tests/FakeFactory.cs index fcb8401..ef40fc1 100644 --- a/src/Tests/FakeFactory.cs +++ b/src/Tests/FakeFactory.cs @@ -1,5 +1,4 @@ -using AutoBogus; -using DynamoLeagueBlazor.Server.Infrastructure.Identity; +using DynamoLeagueBlazor.Server.Infrastructure.Identity; using DynamoLeagueBlazor.Server.Models; using DynamoLeagueBlazor.Shared.Enums; diff --git a/src/Tests/Features/Fines/ManageFineTests.cs b/src/Tests/Features/Fines/ManageFineTests.cs index 5ce599d..c7f2a3e 100644 --- a/src/Tests/Features/Fines/ManageFineTests.cs +++ b/src/Tests/Features/Fines/ManageFineTests.cs @@ -1,5 +1,4 @@ -using AutoBogus; -using DynamoLeagueBlazor.Server.Models; +using DynamoLeagueBlazor.Server.Models; using DynamoLeagueBlazor.Shared.Features.Fines; using System.Net.Http.Json; diff --git a/src/Tests/Features/FreeAgents/AddBidTests.cs b/src/Tests/Features/FreeAgents/AddBidTests.cs index 6bd224e..788a474 100644 --- a/src/Tests/Features/FreeAgents/AddBidTests.cs +++ b/src/Tests/Features/FreeAgents/AddBidTests.cs @@ -1,5 +1,4 @@ -using AutoBogus; -using DynamoLeagueBlazor.Server.Models; +using DynamoLeagueBlazor.Server.Models; using DynamoLeagueBlazor.Shared.Features.FreeAgents; using Microsoft.Extensions.DependencyInjection; using System.Net.Http.Json; diff --git a/src/Tests/Features/OfferMatching/ListTests.cs b/src/Tests/Features/OfferMatching/ListTests.cs index 042bd9a..4ffa0dc 100644 --- a/src/Tests/Features/OfferMatching/ListTests.cs +++ b/src/Tests/Features/OfferMatching/ListTests.cs @@ -1,7 +1,7 @@ -using System.Net.Http.Json; -using AutoBogus; -using DynamoLeagueBlazor.Client.Features.OfferMatching; +using DynamoLeagueBlazor.Client.Features.OfferMatching; +using DynamoLeagueBlazor.Server.Models; using DynamoLeagueBlazor.Shared.Features.OfferMatching; +using System.Net.Http.Json; namespace DynamoLeagueBlazor.Tests.Features.OfferMatching; @@ -64,7 +64,32 @@ public async Task GivenAnyAuthenticatedUser_WhenThereIsOnePlayerWhoIsInOfferMatc freeAgent.Position.Should().Be(mockPlayer.Position); freeAgent.HeadShotUrl.Should().Be(mockPlayer.HeadShotUrl); freeAgent.OfferingTeam.Should().Be(mockTeam.Name); - freeAgent.Offer.Should().Be(bidAmount.ToString("C0")); + freeAgent.Offer.Should().Be(bidAmount); + } + [Fact] + public async Task GivenAnyAuthenticatedUser_WhenPlayerIsMatched_ThenPlayerIsMovedToUnsignedStatus() + { + var application = CreateUserAuthenticatedApplication(); + var team = CreateFakeTeam(); + await application.AddAsync(team); + + var player = CreateFakePlayer(); + player.YearContractExpires = DateTime.MaxValue.Year; + player.AddBid(int.MaxValue, team.Id); + await application.AddAsync(player); + + var request = AutoFaker.Generate(); + request.PlayerId = player.Id; + var client = application.CreateClient(); + + await client.PostAsJsonAsync(OfferMatchingListRouteFactory.Uri, request); + + var result = await application.FirstOrDefaultAsync(); + result!.Rostered.Should().Be(false); + result.YearContractExpires.Should().Be(null); + result.EndOfFreeAgency.Should().Be(null); + result.YearAcquired.Should().Be(DateTime.Today.Year); + result.ContractValue.Should().Be(int.MaxValue); } } diff --git a/src/Tests/Features/Players/AddFineTests.cs b/src/Tests/Features/Players/AddFineTests.cs index e89b771..00b67f2 100644 --- a/src/Tests/Features/Players/AddFineTests.cs +++ b/src/Tests/Features/Players/AddFineTests.cs @@ -1,5 +1,4 @@ -using AutoBogus; -using DynamoLeagueBlazor.Server.Models; +using DynamoLeagueBlazor.Server.Models; using DynamoLeagueBlazor.Shared.Features.Players; using DynamoLeagueBlazor.Shared.Utilities; using Microsoft.Extensions.DependencyInjection; diff --git a/src/Tests/Features/Teams/SignPlayerTests.cs b/src/Tests/Features/Teams/SignPlayerTests.cs index 3491f89..4aae67e 100644 --- a/src/Tests/Features/Teams/SignPlayerTests.cs +++ b/src/Tests/Features/Teams/SignPlayerTests.cs @@ -1,5 +1,4 @@ -using AutoBogus; -using DynamoLeagueBlazor.Client.Features.Teams; +using DynamoLeagueBlazor.Client.Features.Teams; using DynamoLeagueBlazor.Server.Models; using DynamoLeagueBlazor.Shared.Enums; using DynamoLeagueBlazor.Shared.Features.Teams;