From f04ff47228e1a6c399b3d9de4099256656100513 Mon Sep 17 00:00:00 2001 From: Simon Schulze Date: Sun, 12 Feb 2023 00:25:57 +0100 Subject: [PATCH 1/9] First draft for championship settings --- .../Settings/ChampSeasonPreview.razor | 38 +++++ .../Settings/ChampionshipPreview.razor | 23 +++ .../Settings/EditChampionshipModal.razor | 20 +++ .../Settings/EditResultConfigModal.razor | 4 +- .../Settings/EditVoteCategoryModal.razor | 2 +- .../Components/Settings/ResultSettings.razor | 55 ++++--- .../Settings/ResultSettings.razor.css | 3 - src/iRLeagueManager.Web/Services.cs | 6 +- .../ViewModels/ChampSeasonViewModel.cs | 26 ++++ .../ViewModels/ChampionshipViewModel.cs | 48 +++++++ .../ResultConfigSettingsViewModel.cs | 94 ------------ .../ViewModels/ResultConfigViewModel.cs | 40 +++--- .../ViewModels/ResultSettingsViewModel.cs | 135 ++++++++++++++++++ .../iRLeagueManager.Web.csproj | 4 +- 14 files changed, 358 insertions(+), 140 deletions(-) create mode 100644 src/iRLeagueManager.Web/Components/Settings/ChampSeasonPreview.razor create mode 100644 src/iRLeagueManager.Web/Components/Settings/ChampionshipPreview.razor create mode 100644 src/iRLeagueManager.Web/Components/Settings/EditChampionshipModal.razor delete mode 100644 src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor.css create mode 100644 src/iRLeagueManager.Web/ViewModels/ChampSeasonViewModel.cs create mode 100644 src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs delete mode 100644 src/iRLeagueManager.Web/ViewModels/ResultConfigSettingsViewModel.cs create mode 100644 src/iRLeagueManager.Web/ViewModels/ResultSettingsViewModel.cs diff --git a/src/iRLeagueManager.Web/Components/Settings/ChampSeasonPreview.razor b/src/iRLeagueManager.Web/Components/Settings/ChampSeasonPreview.razor new file mode 100644 index 00000000..7c0c5c92 --- /dev/null +++ b/src/iRLeagueManager.Web/Components/Settings/ChampSeasonPreview.razor @@ -0,0 +1,38 @@ +@using iRLeagueManager.Web.ViewModels + +
+
+
@ChampSeason.ChampionshipName
+
+
+
ResultConfigs
+
+ @foreach(var config in ChampSeason.ResultConfigs) + { +
+ @config.Name +
+ } +
+
+
+
Standing Config
+
+ @ChampSeason.StandingConfig?.Name +
+
+
+ + +@code { + [Parameter(CaptureUnmatchedValues=true)] + public IDictionary? AdditionalAttributes { get; set; } + + [Parameter, EditorRequired] + public ChampSeasonViewModel ChampSeason { get; set; } = default!; + + protected override void OnParametersSet() + { + _ = ChampSeason ?? throw BlazorParameterNullException.New(this, ChampSeason); + } +} diff --git a/src/iRLeagueManager.Web/Components/Settings/ChampionshipPreview.razor b/src/iRLeagueManager.Web/Components/Settings/ChampionshipPreview.razor new file mode 100644 index 00000000..609e2a5b --- /dev/null +++ b/src/iRLeagueManager.Web/Components/Settings/ChampionshipPreview.razor @@ -0,0 +1,23 @@ +@using iRLeagueManager.Web.ViewModels + +
+
+
@Championship.Name
+
+
Display Name
+ @Championship.DisplayName +
+ + +@code { + [Parameter(CaptureUnmatchedValues=true)] + public IDictionary? AdditionalAttributes { get; set; } + + [Parameter, EditorRequired] + public ChampionshipViewModel Championship { get; set; } = default!; + + protected override void OnParametersSet() + { + _ = Championship ?? throw BlazorParameterNullException.New(this, Championship); + } +} diff --git a/src/iRLeagueManager.Web/Components/Settings/EditChampionshipModal.razor b/src/iRLeagueManager.Web/Components/Settings/EditChampionshipModal.razor new file mode 100644 index 00000000..4da2d5d6 --- /dev/null +++ b/src/iRLeagueManager.Web/Components/Settings/EditChampionshipModal.razor @@ -0,0 +1,20 @@ +@using iRLeagueApiCore.Common.Models; +@inherits EditModalBase + + + +
+ + + + + + +
+ + +
+ +@code { + +} diff --git a/src/iRLeagueManager.Web/Components/Settings/EditResultConfigModal.razor b/src/iRLeagueManager.Web/Components/Settings/EditResultConfigModal.razor index 8eb9a0d6..157e1306 100644 --- a/src/iRLeagueManager.Web/Components/Settings/EditResultConfigModal.razor +++ b/src/iRLeagueManager.Web/Components/Settings/EditResultConfigModal.razor @@ -134,7 +134,7 @@ -
+@*

-
+ *@ diff --git a/src/iRLeagueManager.Web/Components/Settings/EditVoteCategoryModal.razor b/src/iRLeagueManager.Web/Components/Settings/EditVoteCategoryModal.razor index a287277d..a93fcc11 100644 --- a/src/iRLeagueManager.Web/Components/Settings/EditVoteCategoryModal.razor +++ b/src/iRLeagueManager.Web/Components/Settings/EditVoteCategoryModal.razor @@ -11,11 +11,11 @@ + Vm.DefaultPenalty) /> - Vm.DefaultPenalty) /> diff --git a/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor b/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor index a052f93e..fa5045bf 100644 --- a/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor +++ b/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor @@ -2,28 +2,41 @@ @using iRLeagueApiCore.Common.Models @using iRLeagueManager.Web.ViewModels @inherits MvvmComponentBase -@inject ResultConfigSettingsViewModel vm +@inject ResultSettingsViewModel vm @inject LeagueApiService apiService +@inject SharedStateService sharedState @inject ILogger logger
- Result Configurations + Championships
- @foreach((var config, var index) in @Bind(vm, x => x.ResultsConfigs).Select((x, i) => (x, i))) + @foreach ((var championship, var index) in @Bind(vm, x => x.Championships).Select((x, i) => (x, i))) { -
OnResultConfigClick(config))> - -
}
- +
+
+ Current Season - @sharedState.SeasonName +
+
+ @foreach ((var champSeason, var index) in @Bind(vm, x => x.CurrentChampSeasons).Select((x, i) => (x, i))) + { +
+ +
+ } +
+
@code { @@ -61,6 +74,23 @@ } } + private async Task OnChampionshipClick(ChampionshipViewModel championship) + { + var parameters = new ModalParameters() + .Add(x => x.Model, championship.CopyModel()) + .Add(x => x.OnSubmit, new((x, cancellation) => x.SaveChangesAsync(cancellation))); + var result = await ModalService.Show("Edit Championship", parameters).Result; + if (result.Confirmed && result.Data is ChampionshipModel model) + { + championship.SetModel(model); + } + } + + private Task OnDeleteChampionshipClick(ChampionshipViewModel championship) + { + return Task.CompletedTask; + } + private async Task OnDeleteResultConfigClick(ResultConfigViewModel config) { var parameters = new ModalParameters() @@ -104,10 +134,6 @@ } } }, - StandingConfig = new() - { - WeeksCounted = 8, - }, }; } @@ -117,10 +143,7 @@ { return; } - if (apiService.CurrentLeague != null) - { - await vm.LoadFromLeagueAsync(); - } + await vm.LoadFromCurrentSeasonAsync(); await InvokeAsync(StateHasChanged); } } diff --git a/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor.css b/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor.css deleted file mode 100644 index 709471e6..00000000 --- a/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor.css +++ /dev/null @@ -1,3 +0,0 @@ -.accordion-button-plain::after { - content: none; -} \ No newline at end of file diff --git a/src/iRLeagueManager.Web/Services.cs b/src/iRLeagueManager.Web/Services.cs index e7335a8a..18fce473 100644 --- a/src/iRLeagueManager.Web/Services.cs +++ b/src/iRLeagueManager.Web/Services.cs @@ -29,13 +29,15 @@ public static IServiceCollection AddViewModels(this IServiceCollection services) services.TryAddTransient(); services.TryAddTransient(); services.TryAddTransient(); + services.TryAddTransient(); + services.TryAddTransient(); services.TryAddScoped(); - services.TryAddScoped(); + services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); - services.TryAddScoped(); + services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); return services; diff --git a/src/iRLeagueManager.Web/ViewModels/ChampSeasonViewModel.cs b/src/iRLeagueManager.Web/ViewModels/ChampSeasonViewModel.cs new file mode 100644 index 00000000..3a6b1bbb --- /dev/null +++ b/src/iRLeagueManager.Web/ViewModels/ChampSeasonViewModel.cs @@ -0,0 +1,26 @@ +using iRLeagueApiCore.Common.Models; +using iRLeagueManager.Web.Data; + +namespace iRLeagueManager.Web.ViewModels; + +public sealed class ChampSeasonViewModel : LeagueViewModelBase +{ + public ChampSeasonViewModel(ILoggerFactory loggerFactory, LeagueApiService apiService) : + this(loggerFactory, apiService, new()) + { } + + public ChampSeasonViewModel(ILoggerFactory loggerFactory, LeagueApiService apiService, ChampSeasonModel model) : + base(loggerFactory, apiService, model) + { + } + + public long ChampSeasonId => model.ChampSeasonId; + public long ChampionshipId => model.ChampionshipId; + public long SeasonId => model.SeasonId; + public string ChampionshipName => model.ChampionshipName; + public string SeasonName => model.SeasonName; + + public StandingConfigModel? StandingConfig { get => model.StandingConfig; set => SetP(model.StandingConfig, value => model.StandingConfig = value, value); } + + public ICollection ResultConfigs => model.ResultConfigs; +} \ No newline at end of file diff --git a/src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs b/src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs new file mode 100644 index 00000000..45ea6bce --- /dev/null +++ b/src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs @@ -0,0 +1,48 @@ +using iRLeagueApiCore.Common.Models; +using iRLeagueManager.Web.Data; +using iRLeagueManager.Web.Extensions; +using System; + +namespace iRLeagueManager.Web.ViewModels; + +public sealed class ChampionshipViewModel : LeagueViewModelBase +{ + public ChampionshipViewModel(ILoggerFactory loggerFactory, LeagueApiService apiService) : + this(loggerFactory, apiService, new()) + { } + + public ChampionshipViewModel(ILoggerFactory loggerFactory, LeagueApiService apiService, ChampionshipModel model) : + base(loggerFactory, apiService, model) + { } + + public long ChampionshipId => model.ChampionshipId; + public string Name { get => model.Name; set => SetP(model.Name, value => model.Name = value, value); } + public string DisplayName { get => model.DisplayName; set => SetP(model.DisplayName, value => model.DisplayName = value, value); } + + public async Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + if (ApiService.CurrentLeague is null) + { + return LeagueNullResult(); + } + + try + { + Loading = true; + var request = ApiService.CurrentLeague + .Championships() + .WithId(ChampionshipId) + .Put(model, cancellationToken); + var result = await request; + if (result.Success && result.Content is not null) + { + SetModel(result.Content); + } + return result.ToStatusResult(); + } + finally + { + Loading = false; + } + } +} \ No newline at end of file diff --git a/src/iRLeagueManager.Web/ViewModels/ResultConfigSettingsViewModel.cs b/src/iRLeagueManager.Web/ViewModels/ResultConfigSettingsViewModel.cs deleted file mode 100644 index 4d5e7ce1..00000000 --- a/src/iRLeagueManager.Web/ViewModels/ResultConfigSettingsViewModel.cs +++ /dev/null @@ -1,94 +0,0 @@ -using iRLeagueApiCore.Common.Models; -using iRLeagueManager.Web.Data; -using iRLeagueManager.Web.Extensions; - -namespace iRLeagueManager.Web.ViewModels; - -public sealed class ResultConfigSettingsViewModel : LeagueViewModelBase -{ - public ResultConfigSettingsViewModel(ILoggerFactory loggerFactory, LeagueApiService apiService) : - base(loggerFactory, apiService) - { - resultConfigs = new ObservableCollection(); - } - - private ObservableCollection resultConfigs; - public ObservableCollection ResultsConfigs { get => resultConfigs; set => Set(ref resultConfigs, value); } - - private ResultConfigViewModel? selected; - public ResultConfigViewModel? Selected { get => selected; set => Set(ref selected, value); } - - public async Task LoadFromLeagueAsync() - { - if (ApiService.CurrentLeague == null) - { - return; - } - - var resultConfigsEndpoint = ApiService.CurrentLeague.ResultConfigs(); - var resultConfigsResult = await resultConfigsEndpoint.Get(); - if (resultConfigsResult.Success == false || resultConfigsResult.Content is null) - { - return; - } - - ResultsConfigs = new ObservableCollection( - resultConfigsResult.Content.Select(x => new ResultConfigViewModel(LoggerFactory, ApiService, x))); - } - - public async Task AddConfiguration(ResultConfigModel? newConfig = null) - { - if (ApiService.CurrentLeague is null) - { - return LeagueNullResult(); - } - - try - { - Loading = true; - newConfig ??= new() { Name = "New Config", DisplayName = "New Config" }; - var request = ApiService.CurrentLeague.ResultConfigs() - .Post(newConfig); - var result = await request; - - if (result.Success == true && result.Content is not null) - { - ResultsConfigs.Add(new ResultConfigViewModel(LoggerFactory, ApiService, result.Content)); - } - - return result.ToStatusResult(); - } - finally - { - Loading = false; - } - } - - public async Task DeleteConfiguration(ResultConfigViewModel config) - { - if (ApiService.CurrentLeague is null) - { - return LeagueNullResult(); - } - - try - { - Loading = true; - var request = ApiService.CurrentLeague.ResultConfigs() - .WithId(config.ResultConfigId) - .Delete(); - var result = await request; - - if (result.Success) - { - ResultsConfigs.Remove(config); - } - - return result.ToStatusResult(); - } - finally - { - Loading = false; - } - } -} diff --git a/src/iRLeagueManager.Web/ViewModels/ResultConfigViewModel.cs b/src/iRLeagueManager.Web/ViewModels/ResultConfigViewModel.cs index 7706b7a3..4f6f3d33 100644 --- a/src/iRLeagueManager.Web/ViewModels/ResultConfigViewModel.cs +++ b/src/iRLeagueManager.Web/ViewModels/ResultConfigViewModel.cs @@ -57,25 +57,25 @@ public bool CalculateCombinedResult } } - private StandingConfigurationViewModel? standingConfig; - public StandingConfigurationViewModel? StandingConfig { get => standingConfig; set => Set(ref standingConfig, value); } - public bool CalculateStandings - { - get => StandingConfig is not null; - set - { - if (value && model.StandingConfig is null) - { - model.StandingConfig = new StandingConfigModel(); - StandingConfig = new(LoggerFactory, ApiService, model.StandingConfig); - } - if (value == false && model.StandingConfig is not null) - { - model.StandingConfig = null; - StandingConfig = null; - } - } - } + //private StandingConfigurationViewModel? standingConfig; + //public StandingConfigurationViewModel? StandingConfig { get => standingConfig; set => Set(ref standingConfig, value); } + //public bool CalculateStandings + //{ + // get => StandingConfig is not null; + // set + // { + // if (value && model.StandingConfig is null) + // { + // model.StandingConfig = new StandingConfigModel(); + // StandingConfig = new(LoggerFactory, ApiService, model.StandingConfig); + // } + // if (value == false && model.StandingConfig is not null) + // { + // model.StandingConfig = null; + // StandingConfig = null; + // } + // } + //} private ObservableCollection scorings; public ObservableCollection Scorings { get => scorings; set => SetP(scorings, value => scorings = value, value); } @@ -95,7 +95,7 @@ public override void SetModel(ResultConfigModel model) Scorings = new(model.Scorings.Select(scoringModel => new ScoringViewModel(LoggerFactory, ApiService, scoringModel))); FiltersForPoints = new(model.FiltersForPoints.Select(filter => new ResultFilterViewModel(LoggerFactory, ApiService, filter))); FiltersForResult = new(model.FiltersForResult.Select(filter => new ResultFilterViewModel(LoggerFactory, ApiService, filter))); - StandingConfig = model.StandingConfig is not null ? new StandingConfigurationViewModel(LoggerFactory, ApiService, model.StandingConfig) : null; + //StandingConfig = model.StandingConfig is not null ? new StandingConfigurationViewModel(LoggerFactory, ApiService, model.StandingConfig) : null; } public async Task LoadAvailableResultConfigs(CancellationToken cancellationToken) diff --git a/src/iRLeagueManager.Web/ViewModels/ResultSettingsViewModel.cs b/src/iRLeagueManager.Web/ViewModels/ResultSettingsViewModel.cs new file mode 100644 index 00000000..b997ae8b --- /dev/null +++ b/src/iRLeagueManager.Web/ViewModels/ResultSettingsViewModel.cs @@ -0,0 +1,135 @@ +using iRLeagueApiCore.Common.Models; +using iRLeagueManager.Web.Data; +using iRLeagueManager.Web.Extensions; +using System; + +namespace iRLeagueManager.Web.ViewModels; + +public sealed class ResultSettingsViewModel : LeagueViewModelBase +{ + public ResultSettingsViewModel(ILoggerFactory loggerFactory, LeagueApiService apiService) : + base(loggerFactory, apiService) + { + championships = new(); + currentChampSeasons = new(); + resultConfigs = new(); + } + + private ObservableCollection championships; + public ObservableCollection Championships { get => championships; set => Set(ref championships, value); } + + private ObservableCollection currentChampSeasons; + public ObservableCollection CurrentChampSeasons { get => currentChampSeasons; set => Set(ref currentChampSeasons, value); } + + private ObservableCollection resultConfigs; + public ObservableCollection ResultsConfigs { get => resultConfigs; set => Set(ref resultConfigs, value); } + + private ResultConfigViewModel? selected; + public ResultConfigViewModel? Selected { get => selected; set => Set(ref selected, value); } + + public async Task LoadFromCurrentSeasonAsync(CancellationToken cancellationToken = default) + { + if (ApiService.CurrentLeague is null) + { + return LeagueNullResult(); + } + if (ApiService.CurrentSeason is null) + { + return SeasonNullResult(); + } + + try + { + Loading = true; + + var getChampionships = await ApiService.CurrentLeague + .Championships() + .Get(cancellationToken); + if (getChampionships.Success == false || getChampionships.Content is null) + { + return getChampionships.ToStatusResult(); + } + Championships = new(getChampionships.Content.Select(x => new ChampionshipViewModel(LoggerFactory, ApiService, x))); + + var getChampSeasons = await ApiService.CurrentSeason + .ChampSeasons() + .Get(cancellationToken); + if (getChampSeasons.Success == false || getChampSeasons.Content is null) + { + return getChampSeasons.ToStatusResult(); + } + CurrentChampSeasons = new(getChampSeasons.Content.Select(x => new ChampSeasonViewModel(LoggerFactory, ApiService, x))); + + var getResultConfigs = await ApiService.CurrentLeague + .ResultConfigs() + .Get(cancellationToken); + if (getResultConfigs.Success == false || getResultConfigs.Content is null) + { + return getResultConfigs.ToStatusResult(); + } + ResultsConfigs = new(getResultConfigs.Content.Select(x => new ResultConfigViewModel(LoggerFactory, ApiService, x))); + + return StatusResult.SuccessResult(); + } + finally + { + Loading = false; + } + } + + public async Task AddConfiguration(ResultConfigModel? newConfig = null) + { + if (ApiService.CurrentLeague is null) + { + return LeagueNullResult(); + } + + try + { + Loading = true; + newConfig ??= new() { Name = "New Config", DisplayName = "New Config" }; + var request = ApiService.CurrentLeague.ResultConfigs() + .Post(newConfig); + var result = await request; + + if (result.Success == true && result.Content is not null) + { + ResultsConfigs.Add(new ResultConfigViewModel(LoggerFactory, ApiService, result.Content)); + } + + return result.ToStatusResult(); + } + finally + { + Loading = false; + } + } + + public async Task DeleteConfiguration(ResultConfigViewModel config) + { + if (ApiService.CurrentLeague is null) + { + return LeagueNullResult(); + } + + try + { + Loading = true; + var request = ApiService.CurrentLeague.ResultConfigs() + .WithId(config.ResultConfigId) + .Delete(); + var result = await request; + + if (result.Success) + { + ResultsConfigs.Remove(config); + } + + return result.ToStatusResult(); + } + finally + { + Loading = false; + } + } +} diff --git a/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj b/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj index 7b88a617..af5b63cf 100644 --- a/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj +++ b/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj @@ -126,8 +126,8 @@ - - + + From 88775450899bdcc991c770660ca294ccd22a1fd6 Mon Sep 17 00:00:00 2001 From: Simon Schulze Date: Fri, 17 Feb 2023 22:41:42 +0100 Subject: [PATCH 2/9] Implement champ season settings --- .../Components/ConfirmModal.razor | 10 +- .../Components/EditModalBase.razor.cs | 2 +- .../Settings/ChampionshipPreview.razor | 23 +++ .../Components/Settings/ResultSettings.razor | 50 ++++--- src/iRLeagueManager.Web/Data/ModelHelper.cs | 18 +++ .../ViewModels/ChampSeasonViewModel.cs | 27 ++++ .../ViewModels/ChampionshipViewModel.cs | 133 ++++++++++++++++++ .../ViewModels/LeagueViewModelBase.cs | 5 +- .../ViewModels/ResultSettingsViewModel.cs | 9 -- .../iRLeagueManager.Web.csproj | 6 +- src/iRLeagueManager.Web/wwwroot/css/site.css | 1 + .../wwwroot/css/toggle.css | 64 +++++++++ 12 files changed, 311 insertions(+), 37 deletions(-) create mode 100644 src/iRLeagueManager.Web/Data/ModelHelper.cs create mode 100644 src/iRLeagueManager.Web/wwwroot/css/toggle.css diff --git a/src/iRLeagueManager.Web/Components/ConfirmModal.razor b/src/iRLeagueManager.Web/Components/ConfirmModal.razor index 4594facb..1ea24b49 100644 --- a/src/iRLeagueManager.Web/Components/ConfirmModal.razor +++ b/src/iRLeagueManager.Web/Components/ConfirmModal.razor @@ -6,23 +6,23 @@ @switch (ButtonTypes) { case ButtonTypes.Ok: -
+
-
+
break; case ButtonTypes.OkCancel: -
+
break; case ButtonTypes.YesNo: -
+
-
+
break; diff --git a/src/iRLeagueManager.Web/Components/EditModalBase.razor.cs b/src/iRLeagueManager.Web/Components/EditModalBase.razor.cs index 2d6c8c39..93c689cf 100644 --- a/src/iRLeagueManager.Web/Components/EditModalBase.razor.cs +++ b/src/iRLeagueManager.Web/Components/EditModalBase.razor.cs @@ -10,7 +10,7 @@ namespace iRLeagueManager.Web.Components; -public class EditModalBase : MvvmComponentBase where TViewModel : LeagueViewModelBase +public class EditModalBase : MvvmComponentBase where TViewModel : LeagueViewModelBase where TModel : class { [Inject] protected TViewModel Vm { get; set; } = default!; diff --git a/src/iRLeagueManager.Web/Components/Settings/ChampionshipPreview.razor b/src/iRLeagueManager.Web/Components/Settings/ChampionshipPreview.razor index 609e2a5b..aa10ef8d 100644 --- a/src/iRLeagueManager.Web/Components/Settings/ChampionshipPreview.razor +++ b/src/iRLeagueManager.Web/Components/Settings/ChampionshipPreview.razor @@ -6,6 +6,26 @@
Display Name
@Championship.DisplayName + @if (ChampSeason is not null) + { +
+
+
Results
+
    + @foreach(var config in ChampSeason.ResultConfigs) + { +
  • @config.Name
  • + } +
+
+
+
Standings
+
    +
  • @ChampSeason.StandingConfig?.Name
  • +
+
+
+ }
@@ -16,6 +36,9 @@ [Parameter, EditorRequired] public ChampionshipViewModel Championship { get; set; } = default!; + [Parameter, EditorRequired] + public ChampSeasonViewModel? ChampSeason { get; set; } + protected override void OnParametersSet() { _ = Championship ?? throw BlazorParameterNullException.New(this, Championship); diff --git a/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor b/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor index fa5045bf..7ffa2f2a 100644 --- a/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor +++ b/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor @@ -13,26 +13,20 @@ Championships
- @foreach ((var championship, var index) in @Bind(vm, x => x.Championships).Select((x, i) => (x, i))) + @foreach ((var championship, var index) in @Bind(vm, x => x.Championships).OrderByDescending(x => x.IsActive).Select((x, i) => (x, i))) { + var champSeason = vm.CurrentChampSeasons.FirstOrDefault(x => x.ChampionshipId == championship.ChampionshipId);
OnChampionshipClick(championship))> - - -
- } -
-
-
-
- Current Season - @sharedState.SeasonName -
-
- @foreach ((var champSeason, var index) in @Bind(vm, x => x.CurrentChampSeasons).Select((x, i) => (x, i))) - { -
- + +
+ + +
}
@@ -116,6 +110,26 @@ var result = await ModalService.Show("Add Config", parameters, options).Result; } + private async Task ToggleChampionshipActive(ChampionshipViewModel championship) + { + if (championship.IsActive) + { + var parameters = new ModalParameters() + .Add(x => x.Text, $"Caution. If you deactivate the championship it will remove all settings that you have made for the current season. This may lead to loss of result or standing configurations") + .Add(x => x.ButtonTypes, ButtonTypes.YesNo); + var result = await ModalService.Show("Deactivate Championship Season", parameters).Result; + if (result.Confirmed) + { + await championship.DeactivateForCurrentSeasonAsync(); + } + } + else + { + await championship.ActivateForCurrentSeasonAsync(); + } + await vm.LoadFromCurrentSeasonAsync(); + } + private ResultConfigModel CreateConfigFromTemplate() { return new() diff --git a/src/iRLeagueManager.Web/Data/ModelHelper.cs b/src/iRLeagueManager.Web/Data/ModelHelper.cs new file mode 100644 index 00000000..7d620583 --- /dev/null +++ b/src/iRLeagueManager.Web/Data/ModelHelper.cs @@ -0,0 +1,18 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; + +namespace iRLeagueManager.Web.Data; + +internal static class ModelHelper +{ + public static T? CopyModel(T? model) where T : class + { + if (model is null) + { + return default; + } + + return JsonSerializer.Deserialize(JsonSerializer.Serialize(model)) + ?? throw new InvalidOperationException("Could not copy model"); + } +} diff --git a/src/iRLeagueManager.Web/ViewModels/ChampSeasonViewModel.cs b/src/iRLeagueManager.Web/ViewModels/ChampSeasonViewModel.cs index 3a6b1bbb..312a9786 100644 --- a/src/iRLeagueManager.Web/ViewModels/ChampSeasonViewModel.cs +++ b/src/iRLeagueManager.Web/ViewModels/ChampSeasonViewModel.cs @@ -1,5 +1,6 @@ using iRLeagueApiCore.Common.Models; using iRLeagueManager.Web.Data; +using iRLeagueManager.Web.Extensions; namespace iRLeagueManager.Web.ViewModels; @@ -23,4 +24,30 @@ public ChampSeasonViewModel(ILoggerFactory loggerFactory, LeagueApiService apiSe public StandingConfigModel? StandingConfig { get => model.StandingConfig; set => SetP(model.StandingConfig, value => model.StandingConfig = value, value); } public ICollection ResultConfigs => model.ResultConfigs; + + public async Task SaveChangesAsync(CancellationToken cancellationToken) + { + if (ApiService.CurrentLeague is null) + { + return LeagueNullResult(); + } + + try + { + Loading = true; + var result = await ApiService.CurrentLeague + .ChampSeasons() + .WithId(ChampSeasonId) + .Put(model, cancellationToken); + if (result.Success && result.Content is not null) + { + SetModel(result.Content); + } + return result.ToStatusResult(); + } + finally + { + Loading = false; + } + } } \ No newline at end of file diff --git a/src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs b/src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs index 45ea6bce..b9a12787 100644 --- a/src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs +++ b/src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs @@ -2,6 +2,7 @@ using iRLeagueManager.Web.Data; using iRLeagueManager.Web.Extensions; using System; +using System.Diagnostics.Contracts; namespace iRLeagueManager.Web.ViewModels; @@ -18,6 +19,9 @@ public ChampionshipViewModel(ILoggerFactory loggerFactory, LeagueApiService apiS public long ChampionshipId => model.ChampionshipId; public string Name { get => model.Name; set => SetP(model.Name, value => model.Name = value, value); } public string DisplayName { get => model.DisplayName; set => SetP(model.DisplayName, value => model.DisplayName = value, value); } + public IReadOnlyCollection Seasons => model.Seasons; + public bool IsActive { get => isActive; private set => Set(ref isActive, value); } + private bool isActive; public async Task SaveChangesAsync(CancellationToken cancellationToken = default) { @@ -45,4 +49,133 @@ public async Task SaveChangesAsync(CancellationToken cancellationT Loading = false; } } + + private bool IsChampionshipActive() + { + return Seasons.Any(x => x.SeasonId == ApiService.CurrentSeason?.Id); + } + + public async Task ActivateForCurrentSeasonAsync(CancellationToken cancellationToken = default) + { + if (ApiService.CurrentLeague is null) + { + return LeagueNullResult(); + } + if (ApiService.CurrentSeason is null) + { + return SeasonNullResult(); + } + if (IsChampionshipActive()) + { + isActive = true; + return StatusResult.SuccessResult("Championship already active for current season"); + } + + try + { + Loading = true; + var previousSeasonId = Seasons.LastOrDefault()?.ChampSeasonId; + ChampSeasonModel? previousSeason = default; + if (previousSeasonId != null) + { + var previousSeasonResult = await ApiService.CurrentLeague + .ChampSeasons() + .WithId(previousSeasonId.GetValueOrDefault()) + .Get(cancellationToken); + if (previousSeasonResult.Success && previousSeasonResult.Content is not null) + { + previousSeason = previousSeasonResult.Content; + } + } + var result = await ApiService.CurrentSeason + .Championships() + .WithId(ChampionshipId) + .Post(CreateChampSeason(previousSeason)); + if (result.Success == false) + { + return result.ToStatusResult(); + } + var getChampionshipResult = await ApiService.CurrentLeague + .Championships() + .WithId(ChampionshipId) + .Get(cancellationToken); + if (getChampionshipResult.Success && getChampionshipResult.Content is not null) + { + SetModel(getChampionshipResult.Content); + } + IsActive = IsChampionshipActive(); + return getChampionshipResult.ToStatusResult(); + } + finally + { + Loading = false; + } + } + + public async Task DeactivateForCurrentSeasonAsync(CancellationToken cancellationToken = default) + { + IsActive = false; + + if (ApiService.CurrentLeague is null) + { + return LeagueNullResult(); + } + if (ApiService.CurrentSeason is null) + { + return SeasonNullResult(); + } + + try + { + Loading = true; + var champSeason = Seasons.FirstOrDefault(x => x.SeasonId == ApiService.CurrentSeason.Id); + if (champSeason is null) + { + return StatusResult.SuccessResult(); + } + var result = await ApiService.CurrentLeague + .ChampSeasons() + .WithId(champSeason.ChampSeasonId) + .Delete(); + if (result.Success == false) + { + return result.ToStatusResult(); + } + var getChampionshipResult = await ApiService.CurrentLeague + .Championships() + .WithId(ChampionshipId) + .Get(); + if (getChampionshipResult.Success && getChampionshipResult.Content is not null) + { + SetModel(getChampionshipResult.Content); + } + return getChampionshipResult.ToStatusResult(); + } + finally + { + Loading = false; + } + } + + private ChampSeasonModel CreateChampSeason(ChampSeasonModel? previousChampSeason) + { + // make copies of previous settings + var standingConfig = ModelHelper.CopyModel(previousChampSeason?.StandingConfig); + var resultConfigs = previousChampSeason?.ResultConfigs + .Select(x => ModelHelper.CopyModel(x)) + .NotNull() + .ToList() ?? new(); + var champSeason = new ChampSeasonModel() + { + StandingConfig = standingConfig, + ResultConfigs = resultConfigs, + }; + return champSeason; + } + + public override void SetModel(ChampionshipModel model) + { + base.SetModel(model); + IsActive = IsChampionshipActive(); + } } \ No newline at end of file diff --git a/src/iRLeagueManager.Web/ViewModels/LeagueViewModelBase.cs b/src/iRLeagueManager.Web/ViewModels/LeagueViewModelBase.cs index aba7ea1b..26793738 100644 --- a/src/iRLeagueManager.Web/ViewModels/LeagueViewModelBase.cs +++ b/src/iRLeagueManager.Web/ViewModels/LeagueViewModelBase.cs @@ -90,7 +90,7 @@ protected static StatusResult SeasonNullResult() => StatusResult.FailedResult("Season Null", $"{nameof(LeagueApiService)}.{nameof(LeagueApiService.CurrentSeason)} was null", Array.Empty()); } -public class LeagueViewModelBase : LeagueViewModelBase +public class LeagueViewModelBase : LeagueViewModelBase where TModel : class where TViewModel : class { protected TModel model = default!; @@ -112,7 +112,6 @@ public virtual void SetModel(TModel model) public virtual TModel CopyModel() { - return JsonSerializer.Deserialize(JsonSerializer.Serialize(model)) - ?? throw new InvalidOperationException("Could not copy model"); + return ModelHelper.CopyModel(model)!; } } diff --git a/src/iRLeagueManager.Web/ViewModels/ResultSettingsViewModel.cs b/src/iRLeagueManager.Web/ViewModels/ResultSettingsViewModel.cs index b997ae8b..96b221b7 100644 --- a/src/iRLeagueManager.Web/ViewModels/ResultSettingsViewModel.cs +++ b/src/iRLeagueManager.Web/ViewModels/ResultSettingsViewModel.cs @@ -59,15 +59,6 @@ public async Task LoadFromCurrentSeasonAsync(CancellationToken can return getChampSeasons.ToStatusResult(); } CurrentChampSeasons = new(getChampSeasons.Content.Select(x => new ChampSeasonViewModel(LoggerFactory, ApiService, x))); - - var getResultConfigs = await ApiService.CurrentLeague - .ResultConfigs() - .Get(cancellationToken); - if (getResultConfigs.Success == false || getResultConfigs.Content is null) - { - return getResultConfigs.ToStatusResult(); - } - ResultsConfigs = new(getResultConfigs.Content.Select(x => new ResultConfigViewModel(LoggerFactory, ApiService, x))); return StatusResult.SuccessResult(); } diff --git a/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj b/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj index af5b63cf..b40d691d 100644 --- a/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj +++ b/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj @@ -119,6 +119,10 @@ + + <_ContentIncludedByDefault Remove="wwwroot\css\toggle.css" /> + + @@ -127,7 +131,7 @@ - + diff --git a/src/iRLeagueManager.Web/wwwroot/css/site.css b/src/iRLeagueManager.Web/wwwroot/css/site.css index 76032f7c..3934880b 100644 --- a/src/iRLeagueManager.Web/wwwroot/css/site.css +++ b/src/iRLeagueManager.Web/wwwroot/css/site.css @@ -1,4 +1,5 @@ @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); +@import url(toggle.css); html, body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; diff --git a/src/iRLeagueManager.Web/wwwroot/css/toggle.css b/src/iRLeagueManager.Web/wwwroot/css/toggle.css new file mode 100644 index 00000000..efdb1109 --- /dev/null +++ b/src/iRLeagueManager.Web/wwwroot/css/toggle.css @@ -0,0 +1,64 @@ +@import url(bootstrap/bootstrap.min.css); + +/* The switch - the box around the slider */ +.switch { + position: relative; + display: inline-block; + width: 2.5rem; + height: 1.3rem; +} + + /* Hide default HTML checkbox */ + .switch input { + opacity: 0; + width: 0; + height: 0; + } + +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .2s; + transition: .2s; +} + + .slider:before { + position: absolute; + content: ""; + height: 1rem; + width: 1rem; + left: .15rem; + bottom: .15rem; + background-color: white; + -webkit-transition: .2s; + transition: .2s; + } + +input:checked + .slider { + background-color: #2196F3; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196F3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(1.15rem); + -ms-transform: translateX(1.15rem); + transform: translateX(1.15rem); +} + +/* Rounded sliders */ +.slider.round { + border-radius: 34px; +} + + .slider.round:before { + border-radius: 50%; + } From e8ed25a352cfa2df7ecf0c549eefe8989753541e Mon Sep 17 00:00:00 2001 From: Simon Schulze Date: Sat, 18 Feb 2023 14:33:50 +0100 Subject: [PATCH 3/9] Let API handle activation/deactivation of champ seasons --- .../Components/Settings/ResultSettings.razor | 9 +--- .../ViewModels/ChampionshipViewModel.cs | 48 ++++--------------- .../ViewModels/LeagueViewModelBase.cs | 6 ++- .../iRLeagueManager.Web.csproj | 2 +- 4 files changed, 16 insertions(+), 49 deletions(-) diff --git a/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor b/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor index 7ffa2f2a..04556687 100644 --- a/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor +++ b/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor @@ -114,14 +114,7 @@ { if (championship.IsActive) { - var parameters = new ModalParameters() - .Add(x => x.Text, $"Caution. If you deactivate the championship it will remove all settings that you have made for the current season. This may lead to loss of result or standing configurations") - .Add(x => x.ButtonTypes, ButtonTypes.YesNo); - var result = await ModalService.Show("Deactivate Championship Season", parameters).Result; - if (result.Confirmed) - { - await championship.DeactivateForCurrentSeasonAsync(); - } + await championship.DeactivateForCurrentSeasonAsync(); } else { diff --git a/src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs b/src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs index b9a12787..6e78e184 100644 --- a/src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs +++ b/src/iRLeagueManager.Web/ViewModels/ChampionshipViewModel.cs @@ -74,23 +74,10 @@ public async Task ActivateForCurrentSeasonAsync(CancellationToken try { Loading = true; - var previousSeasonId = Seasons.LastOrDefault()?.ChampSeasonId; - ChampSeasonModel? previousSeason = default; - if (previousSeasonId != null) - { - var previousSeasonResult = await ApiService.CurrentLeague - .ChampSeasons() - .WithId(previousSeasonId.GetValueOrDefault()) - .Get(cancellationToken); - if (previousSeasonResult.Success && previousSeasonResult.Content is not null) - { - previousSeason = previousSeasonResult.Content; - } - } var result = await ApiService.CurrentSeason .Championships() .WithId(ChampionshipId) - .Post(CreateChampSeason(previousSeason)); + .Post(new(), cancellationToken); if (result.Success == false) { return result.ToStatusResult(); @@ -103,7 +90,6 @@ public async Task ActivateForCurrentSeasonAsync(CancellationToken { SetModel(getChampionshipResult.Content); } - IsActive = IsChampionshipActive(); return getChampionshipResult.ToStatusResult(); } finally @@ -116,11 +102,11 @@ public async Task DeactivateForCurrentSeasonAsync(CancellationToke { IsActive = false; - if (ApiService.CurrentLeague is null) + if (CurrentLeague is null) { return LeagueNullResult(); } - if (ApiService.CurrentSeason is null) + if (CurrentSeason is null) { return SeasonNullResult(); } @@ -128,28 +114,28 @@ public async Task DeactivateForCurrentSeasonAsync(CancellationToke try { Loading = true; - var champSeason = Seasons.FirstOrDefault(x => x.SeasonId == ApiService.CurrentSeason.Id); + var champSeason = Seasons.FirstOrDefault(x => x.SeasonId == CurrentSeason.Id); if (champSeason is null) { return StatusResult.SuccessResult(); } - var result = await ApiService.CurrentLeague + var result = await CurrentLeague .ChampSeasons() .WithId(champSeason.ChampSeasonId) - .Delete(); + .Delete(cancellationToken); if (result.Success == false) { return result.ToStatusResult(); } - var getChampionshipResult = await ApiService.CurrentLeague + var getChampionshipResult = await CurrentLeague .Championships() .WithId(ChampionshipId) - .Get(); + .Get(cancellationToken); if (getChampionshipResult.Success && getChampionshipResult.Content is not null) { SetModel(getChampionshipResult.Content); } - return getChampionshipResult.ToStatusResult(); + return result.ToStatusResult(); } finally { @@ -157,22 +143,6 @@ public async Task DeactivateForCurrentSeasonAsync(CancellationToke } } - private ChampSeasonModel CreateChampSeason(ChampSeasonModel? previousChampSeason) - { - // make copies of previous settings - var standingConfig = ModelHelper.CopyModel(previousChampSeason?.StandingConfig); - var resultConfigs = previousChampSeason?.ResultConfigs - .Select(x => ModelHelper.CopyModel(x)) - .NotNull() - .ToList() ?? new(); - var champSeason = new ChampSeasonModel() - { - StandingConfig = standingConfig, - ResultConfigs = resultConfigs, - }; - return champSeason; - } - public override void SetModel(ChampionshipModel model) { base.SetModel(model); diff --git a/src/iRLeagueManager.Web/ViewModels/LeagueViewModelBase.cs b/src/iRLeagueManager.Web/ViewModels/LeagueViewModelBase.cs index 26793738..ceb3524f 100644 --- a/src/iRLeagueManager.Web/ViewModels/LeagueViewModelBase.cs +++ b/src/iRLeagueManager.Web/ViewModels/LeagueViewModelBase.cs @@ -1,4 +1,6 @@ -using iRLeagueManager.Web.Data; +using iRLeagueApiCore.Client.Endpoints.Leagues; +using iRLeagueApiCore.Client.Endpoints.Seasons; +using iRLeagueManager.Web.Data; using iRLeagueManager.Web.Shared; using MvvmBlazor.ViewModel; using System.Runtime.CompilerServices; @@ -19,6 +21,8 @@ public LeagueViewModelBase(ILoggerFactory loggerFactory, LeagueApiService apiSer protected ILogger Logger { get; } protected LeagueApiService ApiService { get; } protected CancellationTokenSource Cts { get; } = new(); + protected ILeagueByNameEndpoint? CurrentLeague => ApiService.CurrentLeague; + protected ISeasonByIdEndpoint? CurrentSeason => ApiService.CurrentSeason; private bool loading; public bool Loading diff --git a/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj b/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj index b40d691d..e6bc6346 100644 --- a/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj +++ b/src/iRLeagueManager.Web/iRLeagueManager.Web.csproj @@ -131,7 +131,7 @@ - + From 24f4559dd9f25f275c61855b641130ac40d2f337 Mon Sep 17 00:00:00 2001 From: Simon Schulze Date: Mon, 27 Feb 2023 20:09:08 +0100 Subject: [PATCH 4/9] Edit champ seasons and result configs --- .../Settings/EditChampSeasonModal.razor | 47 +++++++++++++++++++ .../Settings/EditChampionshipModal.razor | 20 -------- .../Components/Settings/ResultSettings.razor | 14 +++--- .../ViewModels/ChampSeasonViewModel.cs | 37 +++++++++++++++ src/iRLeagueManager.Web/appsettings.json | 4 +- 5 files changed, 93 insertions(+), 29 deletions(-) create mode 100644 src/iRLeagueManager.Web/Components/Settings/EditChampSeasonModal.razor delete mode 100644 src/iRLeagueManager.Web/Components/Settings/EditChampionshipModal.razor diff --git a/src/iRLeagueManager.Web/Components/Settings/EditChampSeasonModal.razor b/src/iRLeagueManager.Web/Components/Settings/EditChampSeasonModal.razor new file mode 100644 index 00000000..fb8f5b87 --- /dev/null +++ b/src/iRLeagueManager.Web/Components/Settings/EditChampSeasonModal.razor @@ -0,0 +1,47 @@ +@using iRLeagueApiCore.Common.Models; +@inherits EditModalBase + + + +
+
+ +
+
+ @foreach(var resultConfig in Vm.ResultConfigViewModels) + { +
+ +
+ } +
+
+ + +
+ +@code { + private IEnumerable ResultConfigs { get; set; } = Array.Empty(); + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + if (firstRender == false) return; + + await Vm.LoadResultConfigs(Cts.Token); + await InvokeAsync(StateHasChanged); + } + + private async Task OnResultConfigClick(ResultConfigViewModel config) + { + var parameters = new ModalParameters() + .Add(x => x.Model, config.CopyModel()) + .Add(x => x.OnSubmit, async (configVm, cancellationToken) => await configVm.SaveChangesAsync(cancellationToken)); + var result = await ModalService.Show("Edit result config", parameters).Result; + if (result.Confirmed && result.Data is ResultConfigModel model) + { + config.SetModel(model); + } + } +} diff --git a/src/iRLeagueManager.Web/Components/Settings/EditChampionshipModal.razor b/src/iRLeagueManager.Web/Components/Settings/EditChampionshipModal.razor deleted file mode 100644 index 4da2d5d6..00000000 --- a/src/iRLeagueManager.Web/Components/Settings/EditChampionshipModal.razor +++ /dev/null @@ -1,20 +0,0 @@ -@using iRLeagueApiCore.Common.Models; -@inherits EditModalBase - - - -
- - - - - - -
- - -
- -@code { - -} diff --git a/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor b/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor index 8fa023c8..6c7d9bd0 100644 --- a/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor +++ b/src/iRLeagueManager.Web/Components/Settings/ResultSettings.razor @@ -16,7 +16,7 @@ @foreach ((var championship, var index) in @Bind(vm, x => x.Championships).OrderByDescending(x => x.IsActive).Select((x, i) => (x, i))) { var champSeason = vm.CurrentChampSeasons.FirstOrDefault(x => x.ChampionshipId == championship.ChampionshipId); -
OnChampionshipClick(championship))> +
{ if (champSeason is not null) await OnChampionshipClick(champSeason); })>