Skip to content
This repository has been archived by the owner on Apr 8, 2023. It is now read-only.

Commit

Permalink
Show offer matching for everyone but only matching team can actually …
Browse files Browse the repository at this point in the history
…match. (#134)
  • Loading branch information
benjaminsampica authored Jul 30, 2022
1 parent 2bce12d commit 2fcc50e
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 105 deletions.
14 changes: 11 additions & 3 deletions src/Client/Features/OfferMatching/List.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@page "/offermatching"
@using Humanizer

<Title>
@_title
Expand All @@ -14,20 +15,27 @@
<HeaderContent>
<MudTh>Actions</MudTh>
<MudTh>Player Name</MudTh>
<MudTh>Player Position</MudTh>
<MudTh>Position</MudTh>
<MudTh>Team</MudTh>
<MudTh>Offering Team</MudTh>
<MudTh>Offer</MudTh>
<MudTh>Remaining Time</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Actions">
<MudIconButton Icon="@Icons.Filled.Handshake" Color="Color.Primary" Variant="Variant.Outlined" Size="Size.Small" Class="ma-2" OnClick="(e) => MatchPlayerAsync(context.Id)"/>
@if(context.CurrentUserIsOfferMatching)
{
<MudIconButton Icon="@Icons.Filled.Handshake" Color="Color.Primary" Variant="Variant.Outlined" Size="Size.Small" Class="ma-2" OnClick="(e) => MatchPlayerAsync(context.Id)" name="matchButton"/>
}
</MudTd>
<MudTd DataLabel="Player Name">
<NameWithImage Name="@context.Name" ImageUrl="@context.HeadShotUrl" />
</MudTd>
<MudTd DataLabel="Player Position">@context.Position</MudTd>
<MudTd DataLabel="Position">@context.Position</MudTd>
<MudTd DataLabel="Team">@context.Team</MudTd>
<MudTd DataLabel="Offering Team">@context.OfferingTeam</MudTd>
<MudTd DataLabel="Offer">@context.Offer.ToString("C0")</MudTd>
<MudTd DataLabel="Remaining Time">@context.RemainingTime.Humanize(2)</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager />
Expand Down
4 changes: 2 additions & 2 deletions src/Client/Features/OfferMatching/List.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ private async Task MatchPlayerAsync(int playerId)
{
if (await ConfirmDialogService.IsCancelledAsync()) return;

var response = await HttpClient.PostAsJsonAsync(OfferMatchingListRouteFactory.Uri, new MatchPlayerRequest() { PlayerId = playerId });
var response = await HttpClient.PostAsJsonAsync(MatchPlayerRouteFactory.Uri, new MatchPlayerRequest(playerId));

if (response.IsSuccessStatusCode)
{
SnackBar.Add("Successfully retained player.", Severity.Success);
SnackBar.Add("Successfully matched player.", Severity.Success);
await LoadDataAsync();
}
else
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;

namespace DynamoLeagueBlazor.Server.Features;
namespace DynamoLeagueBlazor.Server.Features.Authentication;

[AllowAnonymous]
public class OidcConfigurationController : Controller
{
private readonly ILogger<OidcConfigurationController> _logger;

public OidcConfigurationController(IClientRequestParametersProvider clientRequestParametersProvider, ILogger<OidcConfigurationController> logger)
public OidcConfigurationController(IClientRequestParametersProvider clientRequestParametersProvider)
{
ClientRequestParametersProvider = clientRequestParametersProvider;
_logger = logger;
}

public IClientRequestParametersProvider ClientRequestParametersProvider { get; }
Expand Down
43 changes: 8 additions & 35 deletions src/Server/Features/OfferMatching/List.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ public async Task<OfferMatchingListResult> GetAsync(CancellationToken cancellati
{
return await _mediator.Send(new ListQuery(), cancellationToken);
}
[HttpPost]
public async Task<Unit> PostAsync([FromBody] MatchPlayerRequest request, CancellationToken cancellationToken)
{
return await _mediator.Send(new MatchPlayerCommand(request.PlayerId), cancellationToken);
}
}

public record ListQuery : IRequest<OfferMatchingListResult> { }
Expand All @@ -51,9 +46,8 @@ public async Task<OfferMatchingListResult> Handle(ListQuery request, Cancellatio

var offerMatches = await _dbContext.Players
.Include(p => p.Bids)
.Where(p => p.TeamId == currentUserTeamId
&& p.State == PlayerState.OfferMatching)
.ProjectTo<OfferMatchingListResult.OfferMatchingItem>(_mapper.ConfigurationProvider)
.Where(p => p.State == PlayerState.OfferMatching)
.ProjectTo<OfferMatchingListResult.OfferMatchingItem>(_mapper.ConfigurationProvider, new { currentUserTeamId })
.ToListAsync(cancellationToken);

return new OfferMatchingListResult
Expand All @@ -66,34 +60,13 @@ public class ListMappingProfile : Profile
{
public ListMappingProfile()
{
int currentUserTeamId = 0;

CreateMap<Player, OfferMatchingListResult.OfferMatchingItem>()
.ForMember(d => d.Team, mo => mo.MapFrom(s => s.Team.Name))
.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.GetHighestBidAmount()));
.ForMember(d => d.Offer, mo => mo.MapFrom(s => s.GetHighestBidAmount()))
.ForMember(d => d.CurrentUserIsOfferMatching, mo => mo.MapFrom(s => s.TeamId == currentUserTeamId))
.ForMember(d => d.RemainingTime, mo => mo.MapFrom(s => s.GetRemainingFreeAgencyTime()));
}
}
public record MatchPlayerCommand(int PlayerId) : IRequest { }

public class MatchPlayerHandler : IRequestHandler<MatchPlayerCommand>
{
private readonly ApplicationDbContext _dbContext;

public MatchPlayerHandler(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}

public async Task<Unit> Handle(MatchPlayerCommand request, CancellationToken cancellationToken)
{
var player = (await _dbContext.Players
.AsTracking()
.Include(p => p.Bids)
.SingleAsync(p => p.Id == request.PlayerId, cancellationToken));

player.MatchOffer();

await _dbContext.SaveChangesAsync(cancellationToken);

return Unit.Value;
}

}
71 changes: 71 additions & 0 deletions src/Server/Features/OfferMatching/MatchPlayer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using DynamoLeagueBlazor.Server.Infrastructure.Identity;
using DynamoLeagueBlazor.Shared.Features.OfferMatching;

namespace DynamoLeagueBlazor.Server.Features.OfferMatching;

[ApiController]
[Route(MatchPlayerRouteFactory.Uri)]
public class MatchPlayerController : ControllerBase
{
private readonly IMediator _mediator;

public MatchPlayerController(IMediator mediator)
{
_mediator = mediator;
}

[HttpPost]
public async Task<Unit> PostAsync([FromBody] MatchPlayerRequest request, CancellationToken cancellationToken)
{
return await _mediator.Send(new MatchPlayerCommand(request.PlayerId), cancellationToken);
}
}

public record MatchPlayerCommand(int PlayerId) : IRequest { }

public class MatchPlayerHandler : IRequestHandler<MatchPlayerCommand>
{
private readonly ApplicationDbContext _dbContext;

public MatchPlayerHandler(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}

public async Task<Unit> Handle(MatchPlayerCommand request, CancellationToken cancellationToken)
{
var player = (await _dbContext.Players
.AsTracking()
.Include(p => p.Bids)
.SingleAsync(p => p.Id == request.PlayerId, cancellationToken));

player.MatchOffer();

await _dbContext.SaveChangesAsync(cancellationToken);

return Unit.Value;
}
}

public class MatchPlayerValidator : IMatchPlayerValidator
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ApplicationDbContext _dbContext;

public MatchPlayerValidator(IHttpContextAccessor httpContextAccessor, ApplicationDbContext dbContext)
{
_httpContextAccessor = httpContextAccessor;
_dbContext = dbContext;
}

public async Task<bool> CanOfferMatchAsync(int playerId, CancellationToken cancellationToken)
{
var currentUserTeamId = _httpContextAccessor.HttpContext!.User.GetTeamId();

var canOfferMatch = await _dbContext.Players
.AnyAsync(p => p.TeamId == currentUserTeamId
&& p.Id == playerId, cancellationToken);

return canOfferMatch;
}
}
2 changes: 2 additions & 0 deletions src/Server/Models/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ public Fine AddFine(decimal amount, string reason)

public int GetHighestBidAmount() => Bids.Any() ? Bids.FindHighestBid()!.Amount : Bid.MinimumAmount;

public TimeSpan GetRemainingFreeAgencyTime() => EndOfFreeAgency!.Value.AddDays(3) - DateTime.Now;

private bool IsEligibleForFreeAgencyExtension(int teamId)
{
var isBidByTheSameTeam = teamId == TeamId;
Expand Down
2 changes: 2 additions & 0 deletions src/Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using DynamoLeagueBlazor.Server.Infrastructure.Identity;
using DynamoLeagueBlazor.Shared.Features.Admin.Shared;
using DynamoLeagueBlazor.Shared.Features.FreeAgents;
using DynamoLeagueBlazor.Shared.Features.OfferMatching;
using DynamoLeagueBlazor.Shared.Features.Players;
using DynamoLeagueBlazor.Shared.Infastructure.Identity;
using FluentValidation.AspNetCore;
Expand Down Expand Up @@ -84,6 +85,7 @@
});
builder.Services.AddTransient<IBidValidator, BidValidator>();
builder.Services.AddTransient<IPlayerHeadshotService, PlayerHeadshotService>();
builder.Services.AddTransient<IMatchPlayerValidator, MatchPlayerValidator>();

builder.Services.Configure<EmailSettings>(builder.Configuration.GetSection(EmailSettings.Email))
.AddSingleton(s => s.GetRequiredService<IOptions<EmailSettings>>().Value);
Expand Down
6 changes: 4 additions & 2 deletions src/Shared/Features/OfferMatching/List.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ public class OfferMatchingItem
public int Id { get; set; }
public string Name { get; set; } = null!;
public string Position { get; set; } = null!;
public string Team { get; set; } = null!;
public string HeadShotUrl { get; set; } = null!;
public string OfferingTeam { get; set; } = null!;
public int Offer { get; set; }
public TimeSpan RemainingTime { get; set; }
public bool CurrentUserIsOfferMatching { get; set; }
}
}

public static class OfferMatchingListRouteFactory
{
public const string Uri = "api/offermatching";
}

}
22 changes: 19 additions & 3 deletions src/Shared/Features/OfferMatching/MatchPlayer.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
namespace DynamoLeagueBlazor.Shared.Features.OfferMatching
namespace DynamoLeagueBlazor.Shared.Features.OfferMatching;

public record MatchPlayerRequest(int PlayerId);

public class MatchPlayerRequestValidator : AbstractValidator<MatchPlayerRequest>
{
public class MatchPlayerRequest
public MatchPlayerRequestValidator(IMatchPlayerValidator offerMatchingValidator)
{
public int PlayerId { get; set; }
RuleFor(x => x.PlayerId)
.GreaterThan(0)
.MustAsync(async (request, value, token) => await offerMatchingValidator.CanOfferMatchAsync(value, token));
}
}

public static class MatchPlayerRouteFactory
{
public const string Uri = "api/matchplayer";
}

public interface IMatchPlayerValidator
{
Task<bool> CanOfferMatchAsync(int playerId, CancellationToken cancellationToken);
}
2 changes: 1 addition & 1 deletion src/Tests/Features/FreeAgents/BidCountdownTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public void CountsDownEverySecondUntilZero()
parameters.Add(p => p.DateTime, DateTime.Now.AddSeconds(4));
});

cut.WaitForState(() => cut.Markup.Contains("1 second"), TimeSpan.FromSeconds(4));
cut.WaitForState(() => cut.Markup.Contains("1 second"), TimeSpan.FromSeconds(5));
cut.WaitForState(() => cut.Markup.Contains("0 seconds"));

cut.Render();
Expand Down
Loading

0 comments on commit 2fcc50e

Please sign in to comment.