Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/member result filter #48

Merged
merged 2 commits into from
Mar 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
General Settings
</button>
</h2>
<div class="accordion-collapse collapse show input-group-list input-group-list-flush" id="collapseGeneralSettings">
<div class="accordion-collapse collapse show input-group-list input-group-list-flush" id="collapseGeneralSettings" data-bs-parent="#resultConfigSections">
<InputGroup Label="Name" ValidationFor="() => Vm.Name">
<InputText class="form-control" id="input-name" placeholder="Name..." aria-label="input-name" @bind-Value=Vm.Name data-bs-toggle="tooltip" title="Identifying name of the result config" />
</InputGroup>
Expand Down Expand Up @@ -55,7 +55,7 @@
Filters
</button>
</h2>
<div class="accordion-collapse collapse input-group-list input-group-list-flush" id="collapseFilters">
<div class="accordion-collapse collapse input-group-list input-group-list-flush" id="collapseFilters" data-bs-parent="#resultConfigSections">
<InputGroup Label="Points Filter">
<button class="form-control" type="button" @onclick=PointsFilterClick data-bs-toggle="tooltip" title="Filter rows that can score points. Only rows that fulfill all filter requirements will be given points. Rows are still displayed in the output result set">
@if (Vm.FiltersForPoints.Count == 0)
Expand Down Expand Up @@ -89,7 +89,7 @@
Session Scorings
</button>
</h2>
<div class="accordion-collapse collapse show m-1 mt-2" id="collapseSessionScorings">
<div class="accordion-collapse collapse m-1 mt-2" id="collapseSessionScorings" data-bs-parent="#resultConfigSections">
@foreach(var scoring in @Bind(Vm, x => x.Scorings).Where(x => x.IsCombinedResult == false))
{
<div class="input-group-list mb-3">
Expand All @@ -113,7 +113,7 @@
Combined Result
</button>
</h2>
<div class="accordion-collapse collapse input-group-list input-group-list-flush" id="collapseCombined">
<div class="accordion-collapse collapse input-group-list input-group-list-flush" id="collapseCombined" data-bs-parent="#resultConfigSections">
<div class="input-group" disabled>
<div class="input-group-text">
<InputCheckbox class="form-check-input mt-0" @bind-Value=Vm.CalculateCombinedResult/>
Expand Down Expand Up @@ -147,12 +147,14 @@
return;
}
await Vm.LoadAvailableResultConfigs(Cts.Token);
await Vm.LoadLeagueMembers(Cts.Token);
}

private async Task PointsFilterClick()
{
var parameters = new ModalParameters()
.Add(nameof(EditResultFilterModal.Model), Vm.GetModel().FiltersForPoints);
var parameters = new ModalParameters<EditResultFilterModal>()
.Add(x => x.Model, Vm.GetModel().FiltersForPoints)
.Add(x => x.LeagueMembers, Vm.LeagueMembers);
var options = new ModalOptions()
{
DisableBackgroundCancel = true,
Expand All @@ -167,8 +169,9 @@

private async Task ResultFilterClick()
{
var parameters = new ModalParameters()
.Add(nameof(EditResultFilterModal.Model), Vm.GetModel().FiltersForResult);
var parameters = new ModalParameters<EditResultFilterModal>()
.Add(x => x.Model, Vm.GetModel().FiltersForResult)
.Add(x => x.LeagueMembers, Vm.LeagueMembers);
var options = new ModalOptions()
{
DisableBackgroundCancel = true,
Expand Down
160 changes: 116 additions & 44 deletions src/iRLeagueManager.Web/Components/Settings/EditResultFilterModal.razor
Original file line number Diff line number Diff line change
Expand Up @@ -18,54 +18,97 @@
<span class="oi oi-trash"/>
</button>
</InputGroup>
<InputGroup Label="Type">
<InputSelect class="form-select" id="input-type-select" aria-label="input-type-select" @bind-Value=filter.Comparator>
@foreach(var compType in Enum.GetValues<ComparatorType>())
{
<option value=@compType>@compType.ToString()</option>
}
<InputGroup Label="Action">
<InputSelect class="form-select" id="input-action-select" aria-label="input-action-select" @bind-Value=filter.Action>
<option value="@MatchedValueAction.Keep">Keep</option>
<option value="@MatchedValueAction.Remove">Remove</option>
</InputSelect>
</InputGroup>
<InputGroup Label="@(@Bind(filter, x => x.Comparator) == ComparatorType.InList ? "Values" : "Value")">
@{
var propertyType = GetColumnPropertyType(filter.ColumnPropertyName);
}
@if (propertyType is null)
{
<label class="form-control disabled" type="text" />
}
else if (filter.Comparator == ComparatorType.InList)
{
<BlazoredTypeahead
@bind-Values=filter.FilterValues
SearchMethod=@(str => Task.FromResult((IEnumerable<string>)Array.Empty<string>()))
AddItemOnEmptyResultMethod=@(str => Task.FromResult(str))
DeleteItemsOnBackspace=false
class="form-control">
<SelectedTemplate Context=value>
@value
@if (filter.FilterType == FilterType.Member)
{
<InputGroup Label="Members">
<BlazoredTypeahead @bind-Values=filter.FilterValues
class="form-control p-0"
style="border-top-right-radius:0; border-top-left-radius:0;"
EnableDropDown=true
SearchMethod=SearchMembers
ConvertMethod="@(item => item.MemberId.ToString())"
MinimumLength=1
MaximumSuggestions=100
DeleteItemsOnBackspace=false
placeholder="Search driver by name...">

<SelectedTemplate Context=memberId>
@{var member = LeagueMembers.FirstOrDefault(x => x.MemberId.ToString() == memberId); }
@if (member is null)
{
@memberId
}
else
{
@member.FirstName @member.LastName
}
</SelectedTemplate>
<ResultTemplate Context=value>
@value
<ResultTemplate Context=member>
@member.FirstName @member.LastName (@member.IRacingId)
</ResultTemplate>
<NotFoundTemplate Context=value>
@value
<NotFoundTemplate Context=name>
<span>Driver <b>@name</b> Not found!</span>
</NotFoundTemplate>
</BlazoredTypeahead>
}
else if (propertyType == typeof(string))
{
<InputText class="form-control" @bind-Value=filter.Value />
}
else if (propertyType == typeof(double) || propertyType == typeof(double?) || propertyType == typeof(int) || propertyType == typeof(int?))
{
<input type="number" class="form-control" [email protected] @onchange=@((ChangeEventArgs args) => filter.Value = args.Value?.ToString() ?? string.Empty) />
}
else
{
<label class="form-control">Not supported</label>
}
</InputGroup>
</InputGroup>
}
else if (filter.FilterType == FilterType.ColumnProperty)
{
<InputGroup Label="Type">
<InputSelect class="form-select" id="input-type-select" aria-label="input-type-select" @bind-Value=filter.Comparator>
@foreach (var compType in Enum.GetValues<ComparatorType>())
{
<option value=@compType>@compType.ToString()</option>
}
</InputSelect>
</InputGroup>
<InputGroup Label="@(@Bind(filter, x => x.Comparator) == ComparatorType.InList ? "Values" : "Value")">
@{
var propertyType = GetColumnPropertyType(filter.ColumnPropertyName);
}
@if (propertyType is null)
{
<label class="form-control disabled" type="text" />
}
else if (filter.Comparator == ComparatorType.InList)
{
<BlazoredTypeahead
@bind-Values=filter.FilterValues
SearchMethod=@(str => Task.FromResult((IEnumerable<string>)Array.Empty<string>()))
AddItemOnEmptyResultMethod=@(str => Task.FromResult(str))
DeleteItemsOnBackspace=false
class="form-control">
<SelectedTemplate Context=value>
@value
</SelectedTemplate>
<ResultTemplate Context=value>
@value
</ResultTemplate>
<NotFoundTemplate Context=value>
@value
</NotFoundTemplate>
</BlazoredTypeahead>
}
else if (propertyType == typeof(string))
{
<InputText class="form-control" @bind-Value=filter.Value />
}
else if (propertyType == typeof(double) || propertyType == typeof(double?) || propertyType == typeof(int) || propertyType == typeof(int?))
{
<input type="number" class="form-control" [email protected] @onchange=@((ChangeEventArgs args) => filter.Value = args.Value?.ToString() ?? string.Empty) />
}
else
{
<label class="form-control">Not supported</label>
}
</InputGroup>
}
</div>
</EditForm>
}
Expand All @@ -81,6 +124,9 @@
[Parameter]
public IEnumerable<ResultFilterModel> Model { get; set; } = default!;

[Parameter]
public IEnumerable<MemberModel> LeagueMembers { get; set; } = default!;

private static IEnumerable<string> ColumnNames { get; } = new[]
{
nameof(ResultRowModel.Car),
Expand All @@ -94,12 +140,38 @@
nameof(ResultRowModel.NewIrating),
nameof(ResultRowModel.OldSafetyRating),
nameof(ResultRowModel.NewSafetyRating),
"Member",
};

private async Task<IEnumerable<MemberModel>> SearchMembers(string searchString)
{
if (string.IsNullOrEmpty(searchString))
{
return await Task.FromResult(LeagueMembers);
}

var terms = searchString.ToLower().Split(',', ' ', ';')
.Where(x => string.IsNullOrWhiteSpace(x) == false)
.ToArray();
return await Task.FromResult(LeagueMembers
.Where(x => MatchMemberSearchTerms(x, terms)));
}

private bool MatchMemberSearchTerms(MemberModel member, params string[] terms)
{
var searchName = member.FirstName + member.LastName;
var iracingId = member.IRacingId.ToString();
return terms
.Any(x => searchName.Contains(x, StringComparison.OrdinalIgnoreCase) ||
iracingId.Contains(x)
);
}

protected override void OnParametersSet()
{
_ = ModalInstance ?? throw BlazorParameterNullException.New(this, ModalInstance);
_ = Model ?? throw BlazorParameterNullException.New(this, Model);
BlazorParameterNullException.ThrowIfNull(this, ModalInstance);
BlazorParameterNullException.ThrowIfNull(this, Model);
BlazorParameterNullException.ThrowIfNull(this, LeagueMembers);
}

protected override void OnAfterRender(bool firstRender)
Expand Down
27 changes: 27 additions & 0 deletions src/iRLeagueManager.Web/ViewModels/ResultConfigViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ public bool CalculateCombinedResult
private ObservableCollection<ResultConfigModel> availableResultConfigs;
public ObservableCollection<ResultConfigModel> AvailableResultConfigs { get => availableResultConfigs; set => Set(ref availableResultConfigs, value); }

private ObservableCollection<MemberModel> leagueMembers;
public ObservableCollection<MemberModel> LeagueMembers { get => leagueMembers; set => Set(ref leagueMembers, value); }

public override void SetModel(ResultConfigModel model)
{
base.SetModel(model);
Expand Down Expand Up @@ -123,6 +126,30 @@ public async Task<StatusResult> LoadAvailableResultConfigs(CancellationToken can
return result.ToStatusResult();
}

public async Task<StatusResult> LoadLeagueMembers(CancellationToken cancellationToken = default)
{
if (CurrentLeague is null)
{
return LeagueNullResult();
}

try
{
Loading = true;
var result = await CurrentLeague.Members()
.Get(cancellationToken);
if (result.Success && result.Content is not null)
{
LeagueMembers = new(result.Content);
}
return result.ToStatusResult();
}
finally
{
Loading = false;
}
}

public async Task<StatusResult> SaveChangesAsync(CancellationToken cancellationToken)
{
if (ApiService.CurrentLeague is null)
Expand Down
31 changes: 29 additions & 2 deletions src/iRLeagueManager.Web/ViewModels/ResultFilterViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,36 @@ public ResultFilterViewModel(ILoggerFactory loggerFactory, LeagueApiService apiS

public long LeagueId => model.LeagueId;
public long ResultsFilterId => model.FilterOptionId;
public string ColumnPropertyName { get => model.ColumnPropertyName; set => SetP(model.ColumnPropertyName, value => model.ColumnPropertyName = value, value); }
public string ColumnPropertyName
{
get => model.ColumnPropertyName;
set
{
if (SetP(model.ColumnPropertyName, value => model.ColumnPropertyName = value, value))
{
UpdateFilterType();
}
}

}
public ComparatorType Comparator { get => model.Comparator; set => SetP(model.Comparator, value => model.Comparator = value, value); }
public FilterType FilterType { get => model.FilterType; set => SetP(model.FilterType, value => model.FilterType = value, value); }
public FilterType FilterType { get => model.FilterType; private set => SetP(model.FilterType, value => model.FilterType = value, value); }
public IList<string> FilterValues { get => (IList<string>)model.FilterValues; set => SetP(model.FilterValues, value => model.FilterValues = value, value); }
public string Value { get => model.FilterValues.FirstOrDefault() ?? string.Empty; set => SetP(model.FilterValues.FirstOrDefault() ?? string.Empty, value => model.FilterValues = new[] { value }.ToList(), value); }
public MatchedValueAction Action { get => model.Action; set => SetP(model.Action, value => model.Action = value, value); }

public override void SetModel(ResultFilterModel model)
{
base.SetModel(model);
UpdateFilterType();
}

private void UpdateFilterType()
{
FilterType = ColumnPropertyName switch
{
"Member" => FilterType.Member,
_ => FilterType.ColumnProperty,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public ResultFiltersViewModel(ILoggerFactory loggerFactory, LeagueApiService api
}

private ObservableCollection<ResultFilterViewModel> filters;
public ObservableCollection<ResultFilterViewModel> Filters { get => filters; set => SetP(filters, value => filters = value, value); }
public ObservableCollection<ResultFilterViewModel> Filters { get => filters; set => Set(ref filters, value); }

public void SetModel(IEnumerable<ResultFilterModel> model)
{
Expand Down
4 changes: 2 additions & 2 deletions src/iRLeagueManager.Web/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
}
},
"AllowedHosts": "*",
//"APIServer": "http://localhost:5000",
"APIServer": "https://irleaguemanager.net/api/",
"APIServer": "http://localhost:5000",
//"APIServer": "https://irleaguemanager.net/api/",
"DefaultUser": "testuser",
"DefaultPassword": "TestPass123!"
}
6 changes: 3 additions & 3 deletions src/iRLeagueManager.Web/iRLeagueManager.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>0.5.2</Version>
<Version>0.5.3-dev.1</Version>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>aspnet-iRLeagueManager.Web-2B05F9DC-55A3-49D1-BD64-31507000EDF3</UserSecretsId>
Expand Down Expand Up @@ -130,8 +130,8 @@
<ItemGroup>
<PackageReference Include="Blazored.LocalStorage" Version="4.3.0" />
<PackageReference Include="Blazored.Modal" Version="7.1.0" />
<PackageReference Include="iRLeagueApiCore.Client" Version="0.5.0" />
<PackageReference Include="iRLeagueApiCore.Common" Version="0.5.1-dev.1" />
<PackageReference Include="iRLeagueApiCore.Client" Version="0.5.2-dev.1" />
<PackageReference Include="iRLeagueApiCore.Common" Version="0.5.2-dev.2" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="6.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="6.0.3" />
Expand Down