diff --git a/src/Client/Features/Admin/StartSeason.razor.cs b/src/Client/Features/Admin/StartSeason.razor.cs index 699a565..37ce9c3 100644 --- a/src/Client/Features/Admin/StartSeason.razor.cs +++ b/src/Client/Features/Admin/StartSeason.razor.cs @@ -1,4 +1,5 @@ -using DynamoLeagueBlazor.Shared.Features.Admin; +using DynamoLeagueBlazor.Client.Shared.Components; +using DynamoLeagueBlazor.Shared.Features.Admin; using DynamoLeagueBlazor.Shared.Infastructure.Identity; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components; @@ -12,6 +13,8 @@ public sealed partial class StartSeason : IDisposable { [Inject] private HttpClient HttpClient { get; set; } = null!; [Inject] private ISnackbar SnackBar { get; set; } = null!; + [Inject] private IConfirmDialogService ConfirmDialogService { get; set; } = null!; + private bool _isDisabled = true; private const string _title = "Start Season"; @@ -24,6 +27,8 @@ protected override async Task OnInitializedAsync() private async Task StartSeasonAsync() { + if (await ConfirmDialogService.IsCancelledAsync()) return; + await HttpClient.PostAsync(StartSeasonRouteFactory.Uri, null); SnackBar.Add("A new season has begun!", Severity.Success); diff --git a/src/Client/Features/OfferMatching/List.razor b/src/Client/Features/OfferMatching/List.razor index 6ab155d..9c574cd 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 6113b19..65f02ab 100644 --- a/src/Client/Features/OfferMatching/List.razor.cs +++ b/src/Client/Features/OfferMatching/List.razor.cs @@ -1,4 +1,5 @@ -using DynamoLeagueBlazor.Shared.Features.OfferMatching; +using DynamoLeagueBlazor.Client.Shared.Components; +using DynamoLeagueBlazor.Shared.Features.OfferMatching; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using MudBlazor; @@ -10,6 +11,8 @@ public sealed partial class List : IDisposable { [Inject] private HttpClient HttpClient { get; set; } = null!; [Inject] private ISnackbar SnackBar { get; set; } = null!; + [Inject] private IConfirmDialogService ConfirmDialogService { get; set; } = null!; + private const string _title = "Offer Matching"; private bool _loading; private readonly CancellationTokenSource _cts = new(); @@ -36,8 +39,10 @@ private async Task LoadDataAsync() _result = await HttpClient.GetFromJsonAsync(OfferMatchingListRouteFactory.Uri, _cts.Token) ?? new(); } - private async Task MatchPlayerAsync(int playerId, int amount) + private async Task MatchPlayerAsync(int playerId) { + if (await ConfirmDialogService.IsCancelledAsync()) return; + var response = await HttpClient.PostAsJsonAsync(OfferMatchingListRouteFactory.Uri, new MatchPlayerRequest() { PlayerId = playerId }); if (response.IsSuccessStatusCode) diff --git a/src/Client/Features/Players/AddFine.razor b/src/Client/Features/Players/AddFine.razor index 49c0482..50dd63b 100644 --- a/src/Client/Features/Players/AddFine.razor +++ b/src/Client/Features/Players/AddFine.razor @@ -1,14 +1,14 @@ @if(_fineDetail is null) { - - - - - - - - + + + + + + + + } else { diff --git a/src/Client/Features/Players/AddFine.razor.cs b/src/Client/Features/Players/AddFine.razor.cs index 3988d11..d9f6e34 100644 --- a/src/Client/Features/Players/AddFine.razor.cs +++ b/src/Client/Features/Players/AddFine.razor.cs @@ -29,7 +29,7 @@ private async Task GetPlayerFineDetailsAsync() { try { - _fineDetail = await HttpClient.GetFromJsonAsync(FineDetailRouteFactory.Create(PlayerId), _cts.Token) ?? new(); + _fineDetail = await HttpClient.GetFromJsonAsync(FineDetailRouteFactory.Create(PlayerId), _cts.Token); } catch (AccessTokenNotAvailableException exception) { diff --git a/src/Client/Features/Teams/Detail.razor.cs b/src/Client/Features/Teams/Detail.razor.cs index 17bec0a..f2a4db0 100644 --- a/src/Client/Features/Teams/Detail.razor.cs +++ b/src/Client/Features/Teams/Detail.razor.cs @@ -1,4 +1,5 @@ -using DynamoLeagueBlazor.Shared.Features.Teams; +using DynamoLeagueBlazor.Client.Shared.Components; +using DynamoLeagueBlazor.Shared.Features.Teams; using DynamoLeagueBlazor.Shared.Infastructure.Identity; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; @@ -13,6 +14,7 @@ public sealed partial class Detail : IDisposable { [Inject] private HttpClient HttpClient { get; set; } = null!; [Inject] private IDialogService DialogService { get; set; } = null!; + [Inject] private IConfirmDialogService ConfirmDialogService { get; set; } = null!; [Inject] private ISnackbar Snackbar { get; set; } = null!; [CascadingParameter] public Task AuthenticationStateTask { get; set; } = null!; [Parameter, EditorRequired] public int TeamId { get; set; } @@ -55,7 +57,7 @@ private void ShowRosteredPlayers() { _playersToDisplay = _result!.RosteredPlayers; _playerTableHeader = "Rostered Players"; - _onPlayerTableActionClick = DropPlayerAsync; + _onPlayerTableActionClick = UnrosterPlayerAsync; _tableActionIcon = Icons.Outlined.PersonRemove; } @@ -74,15 +76,17 @@ private void ShowUnsignedPlayers() _tableActionIcon = Icons.Filled.AssignmentLate; } - private async Task DropPlayerAsync(int playerId) + private async Task UnrosterPlayerAsync(int playerId) { + if (await ConfirmDialogService.IsCancelledAsync()) return; + try { var response = await HttpClient.PostAsJsonAsync(DropPlayerRouteFactory.Uri, new DropPlayerRequest { PlayerId = playerId }, _cts.Token); if (response.IsSuccessStatusCode) { - Snackbar.Add("Successfully dropped player.", Severity.Success); + Snackbar.Add("Successfully unrostered player.", Severity.Success); await LoadDataAsync(); ShowRosteredPlayers(); @@ -100,13 +104,13 @@ private async Task DropPlayerAsync(int playerId) private async Task OpenSignPlayerDialogAsync(int playerId) { - var maxWidth = new DialogOptions() { MaxWidth = MaxWidth.Small, FullWidth = true }; + var dialogOptions = new DialogOptions() { MaxWidth = MaxWidth.Small, FullWidth = true }; var parameters = new DialogParameters { { nameof(SignPlayer.PlayerId), playerId } }; - var dialog = DialogService.Show("Sign Player", parameters, maxWidth); + var dialog = DialogService.Show("Sign Player", parameters, dialogOptions); var result = await dialog.Result; if (!result.Cancelled) diff --git a/src/Client/Program.cs b/src/Client/Program.cs index 475889a..c2e99ce 100644 --- a/src/Client/Program.cs +++ b/src/Client/Program.cs @@ -2,6 +2,7 @@ using DynamoLeagueBlazor.Client.Areas.Identity; using DynamoLeagueBlazor.Client.Features.Admin; using DynamoLeagueBlazor.Client.Features.FreeAgents; +using DynamoLeagueBlazor.Client.Shared.Components; using DynamoLeagueBlazor.Shared.Features.Admin.Shared; using DynamoLeagueBlazor.Shared.Features.FreeAgents; using DynamoLeagueBlazor.Shared.Infastructure.Identity; @@ -37,10 +38,12 @@ builder.Services.AddMudServices(config => { config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomCenter; + config.SnackbarConfiguration.PreventDuplicates = false; }); builder.Services.AddTransient(); builder.Services.AddTransient(); + builder.Services.AddScoped(); var host = builder.Build(); diff --git a/src/Client/Shared/Components/ConfirmDialog.razor b/src/Client/Shared/Components/ConfirmDialog.razor new file mode 100644 index 0000000..b2871eb --- /dev/null +++ b/src/Client/Shared/Components/ConfirmDialog.razor @@ -0,0 +1,15 @@ + + + + Confirm Your Action + + + + Are you sure you want to perform this action? It is irreversable! + + + + Confirm + + + \ No newline at end of file diff --git a/src/Client/Shared/Components/ConfirmDialog.razor.cs b/src/Client/Shared/Components/ConfirmDialog.razor.cs new file mode 100644 index 0000000..63b4bb2 --- /dev/null +++ b/src/Client/Shared/Components/ConfirmDialog.razor.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Components; +using MudBlazor; + +namespace DynamoLeagueBlazor.Client.Shared.Components; + +public partial class ConfirmDialog +{ + [CascadingParameter] MudDialogInstance MudDialog { get; set; } = null!; + + private void Confirm() => MudDialog.Close(DialogResult.Ok(true)); +} + +public interface IConfirmDialogService +{ + Task IsCancelledAsync(); +} + +public class ConfirmDialogService : IConfirmDialogService +{ + private readonly IDialogService _dialogService; + + public ConfirmDialogService(IDialogService dialogService) + { + _dialogService = dialogService; + } + + public async Task IsCancelledAsync() + { + var dialog = _dialogService.Show(); + var result = await dialog.Result; + + return result.Cancelled; + } +} \ No newline at end of file diff --git a/src/Server/Models/Player.cs b/src/Server/Models/Player.cs index da3363a..19d1372 100644 --- a/src/Server/Models/Player.cs +++ b/src/Server/Models/Player.cs @@ -19,7 +19,7 @@ public Player() _machine.Configure(PlayerState.Rostered) .OnEntryFrom(_rosteredTrigger, (yearContractExpires, contractValue) => SetToRostered(yearContractExpires, contractValue)) - .Permit(PlayerStateTrigger.DroppedByTeam, PlayerState.Unrostered) + .Permit(PlayerStateTrigger.UnrosteredByTeam, PlayerState.Unrostered) .Permit(PlayerStateTrigger.NewSeasonStarted, PlayerState.FreeAgent); _machine.Configure(PlayerState.FreeAgent) @@ -53,7 +53,7 @@ public Player(string name, string position, string headShotUrl) : this() public ICollection Bids { get; private set; } = new HashSet(); public ICollection Fines { get; private set; } = new HashSet(); - private enum PlayerStateTrigger { NewSeasonStarted, BiddingEnded, OfferMatchedByTeam, MatchExpired, SignedByTeam, DroppedByTeam } + private enum PlayerStateTrigger { NewSeasonStarted, BiddingEnded, OfferMatchedByTeam, MatchExpired, SignedByTeam, UnrosteredByTeam } public enum PlayerState { FreeAgent, OfferMatching, Unsigned, Rostered, Unrostered } public Player SetToUnrostered() @@ -104,7 +104,7 @@ public Bid AddBid(int amount, int teamIdOfBidder) return bid; } - public void DropFromCurrentTeam() => _machine.Fire(PlayerStateTrigger.DroppedByTeam); + public void DropFromCurrentTeam() => _machine.Fire(PlayerStateTrigger.UnrosteredByTeam); private void GrantExtensionToFreeAgency() { diff --git a/src/Tests/UITestBase.cs b/src/Tests/UITestBase.cs index 708b06f..09d555c 100644 --- a/src/Tests/UITestBase.cs +++ b/src/Tests/UITestBase.cs @@ -1,4 +1,5 @@ using Bunit.TestDoubles; +using DynamoLeagueBlazor.Client.Shared.Components; using DynamoLeagueBlazor.Shared.Infastructure.Identity; using Microsoft.Extensions.DependencyInjection; using MockHttp.Json; @@ -25,6 +26,7 @@ public UITestBase() testContext.Services.AddMudServices(); testContext.Services.AddSingleton(mockSnackBar.Object); + testContext.Services.AddSingleton(Mock.Of()); testContext.JSInterop.SetupVoid(); var mockHttpHandler = new MockHttpHandler();