Skip to content
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
@@ -1,19 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using CleanArchitecture.Blazor.Application.Features.Tenants.Commands.AddEdit;

namespace CleanArchitecture.Blazor.Application.Features.KeyValues.Commands.AddEdit;

public class AddEditKeyValueCommandValidator : AbstractValidator<AddEditKeyValueCommand>
{
public AddEditKeyValueCommandValidator()
{
RuleFor(v => v.Name).NotNull();
RuleFor(v => v.Text)
.MaximumLength(256)
.NotEmpty();
RuleFor(v => v.Value)
.MaximumLength(256)
.NotEmpty();
RuleFor(v => v.Text).MaximumLength(256).NotEmpty();
RuleFor(v => v.Value).MaximumLength(256).NotEmpty();

}
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
{
var result = await ValidateAsync(ValidationContext<AddEditKeyValueCommand>.CreateWithOptions((AddEditKeyValueCommand)model, x => x.IncludeProperties(propertyName)));
if (result.IsValid)
return Array.Empty<string>();
return result.Errors.Select(e => e.ErrorMessage);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@

using CleanArchitecture.Blazor.Application.Features.KeyValues.DTOs;
using CleanArchitecture.Blazor.Application.Features.KeyValues.Caching;
using CleanArchitecture.Blazor.Application.Features.AuditTrails.DTOs;

namespace CleanArchitecture.Blazor.Application.Features.KeyValues.Queries.PaginationQuery;

public class KeyValuesWithPaginationQuery : PaginationFilter, ICacheableRequest<PaginatedData<KeyValueDto>>
public class KeyValuesWithPaginationQuery : PaginationFilterBase, ICacheableRequest<PaginatedData<KeyValueDto>>
{
[CompareTo("Value", "Text", "Description")] // <-- This filter will be applied to Name or Brand or Description.
[StringFilterOptions(StringFilterOption.Contains)]
public string? Keyword { get; set; }
[OperatorComparison(OperatorType.Equal)]
public Picklist? Picklist { get; set; }
public string CacheKey => $"{nameof(KeyValuesWithPaginationQuery)},{this}";

public MemoryCacheEntryOptions? Options => KeyValueCacheKey.MemoryCacheEntryOptions;
Expand All @@ -32,11 +38,10 @@ IMapper mapper
#pragma warning disable CS8604
public async Task<PaginatedData<KeyValueDto>> Handle(KeyValuesWithPaginationQuery request, CancellationToken cancellationToken)
{

var data = await _context.KeyValues.Where(x=>x.Value.Contains(request.Keyword)|| x.Text.Contains(request.Keyword) || x.Description.Contains(request.Keyword))
.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectTo<KeyValueDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(request.PageNumber, request.PageSize);

var data = await _context.KeyValues.ApplyFilterWithoutPagination(request)
.ProjectTo<KeyValueDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(request.Page, request.PerPage);

return data;
}
Expand Down
91 changes: 51 additions & 40 deletions src/Blazor.Server.UI/Pages/SystemManagement/Dictionaries.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
@using CleanArchitecture.Blazor.Application.Features.KeyValues.Queries.ByName
@using CleanArchitecture.Blazor.Application.Features.KeyValues.Queries.Export
@using CleanArchitecture.Blazor.Application.Features.KeyValues.Queries.GetAll
@using CleanArchitecture.Blazor.Application.Features.KeyValues.Queries.PaginationQuery;

@attribute [Authorize(Policy = Permissions.Dictionaries.View)]
@inject IStringLocalizer<Dictionaries> L
Expand All @@ -23,11 +24,9 @@
<ChildContent>

<MudTable @ref="_table"
T="KeyValueDto"
Items="@_keyValueList"
FixedHeader="true"
FixedFooter="true"
Virtualize="true"
Virtualize="false"
@bind-RowsPerPage="_defaultPageSize"
Height="calc(100vh - 265px)"
Hover="true"
Expand All @@ -46,7 +45,7 @@
Loading="@_loading"
SortLabel="@ConstantString.ORDERBY"
Breakpoint="Breakpoint.Xs"
Filter="new Func<KeyValueDto,bool>(_quickFilter)">
ServerData="@(new Func<TableState, Task<TableData<KeyValueDto>>>(ServerReload))">
<ToolBarContent>
<div class="justify-start pt-3">
<MudText Typo="Typo.h6">@L["Picklist"]</MudText>
Expand Down Expand Up @@ -145,10 +144,11 @@
<MudSpacer />
@if (_canSearch)
{
<MudTextField @bind-Value="_searchString" FullWidth="false"
Placeholder="@(L["Search for picklist"])" Adornment="Adornment.End"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Small" Class="mt-0 mb-3">
</MudTextField>
<MudStack Row="true" AlignItems="AlignItems.Stretch">
<MudEnumSelect TEnum="Picklist?" Placeholder="Search for picklist" Value="@_searchPicklist" Clearable="true" ValueChanged="@(s=>OnSearch(s))" Style="width:160px" FullWidth="true"> </MudEnumSelect>
<MudTextField T="string" ValueChanged="@(s=>OnSearch(s))" Value="@_searchString" Placeholder="Search" Adornment="Adornment.End" Style="width:260px" FullWidth="true"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Small"></MudTextField>
</MudStack>
}
</ToolBarContent>
<ColGroup>
Expand Down Expand Up @@ -195,14 +195,15 @@
</ErrorBoundary>

@code {
private MudTable<KeyValueDto>? _table=default!;
private MudTable<KeyValueDto> _table=default!;
public string Title { get; set; } = "Picklist";
private IList<KeyValueDto> _keyValueList = new List<KeyValueDto>();
private HashSet<KeyValueDto> _selectedItems = new HashSet<KeyValueDto>();
private KeyValueDto _selectedItem { get; set; } = new();
private KeyValueDto _elementBeforeEdit { get; set; } = new();
private bool _canCancelEdit=true;
private string _searchString = string.Empty;
private Picklist? _searchPicklist = null;
private int _defaultPageSize = 15;
private bool _editing;
[CascadingParameter]
Expand All @@ -213,6 +214,7 @@
private IBlazorDownloadFileService _blazorDownloadFileService { get; set; } = null!;
[Inject]
private IMediator _mediator { get; set; } = default!;
private KeyValuesWithPaginationQuery request = new();
private bool _canCreate;
private bool _canSearch;
private bool _canEdit;
Expand All @@ -232,43 +234,46 @@
_canDelete = (await AuthService.AuthorizeAsync(state.User, Permissions.Dictionaries.Delete)).Succeeded;
_canImport = (await AuthService.AuthorizeAsync(state.User, Permissions.Dictionaries.Import)).Succeeded;
_canExport = (await AuthService.AuthorizeAsync(state.User, Permissions.Dictionaries.Export)).Succeeded;
await LoadData();
}
private bool _quickFilter(KeyValueDto dto) => FilterFunc(dto, _searchString);

private bool FilterFunc(KeyValueDto dto, string searchString)
{
if (string.IsNullOrWhiteSpace(searchString))
return true;
if ($"{dto.Name.GetDescription()} {dto.Value} {dto.Text} {dto.Description}".Contains(searchString))
return true;
return false;
}

private async Task LoadData()
private async Task<TableData<KeyValueDto>> ServerReload(TableState state)
{
if(_loading) return;
try{
try
{
_loading = true;
_editing = false;
var cmd = new GetAllKeyValuesQuery();
_keyValueList = (await _mediator.Send(cmd).ConfigureAwait(false)).ToList();
_selectedItems=new HashSet<KeyValueDto>();
_selectedItem = new();
var request = new KeyValuesWithPaginationQuery()
{
Keyword = _searchString,
Picklist = _searchPicklist,
Sort = string.IsNullOrEmpty(state.SortLabel) ? "Id" : state.SortLabel,
SortBy = (state.SortDirection == SortDirection.Ascending ? AutoFilterer.Enums.Sorting.Ascending : AutoFilterer.Enums.Sorting.Descending),
Page = state.Page + 1,
PerPage = state.PageSize
};
var result = await _mediator.Send(request).ConfigureAwait(false);
return new TableData<KeyValueDto>() { TotalItems = result.TotalItems, Items = result.Items };
}
finally
{
_loading = false;
}


}
private async Task OnSearch(string text)
{
_searchString = text;
await _table.ReloadServerData();
}
private async Task OnSearch(Picklist? val)
{
_searchPicklist = val;
await _table.ReloadServerData();
}

private async Task OnRefresh()
{
KeyValueCacheKey.Refresh();
_searchString = string.Empty;
await LoadData();
await _table.ReloadServerData();
}
private async Task OnDeleteChecked()
{
Expand All @@ -284,24 +289,30 @@
{
var command = new DeleteKeyValueCommand(_selectedItems.Select(x => x.Id).ToArray());
var result = await _mediator.Send(command);
await LoadData();
await _table.ReloadServerData();
Snackbar.Add($"{ConstantString.DELETESUCCESS}", MudBlazor.Severity.Info);
}
}
private async Task OnCreate()
{
_editing = true;
_table!.SetEditingItem(null);
var newitem = new KeyValueDto()
var command = new AddEditKeyValueCommand()
{
Name = _selectedItem.Name,
Description = _selectedItem?.Description,
TrackingState = TrackingState.Added
};
_keyValueList.Insert(0, newitem);
await Task.Delay(50);
_table!.SetSelectedItem(newitem);
_table!.SetEditingItem(newitem);
var parameters = new DialogParameters
{
{ nameof(_CreatePicklistDialog.model),command },
};
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small, FullWidth = true };
var dialog = DialogService.Show<_CreatePicklistDialog>
(L["Create a new picklist"], parameters, options);
var state = await dialog.Result;
if (!state.Canceled)
{
await _table.ReloadServerData();
}

}
private void BackupItem(object element)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
@using CleanArchitecture.Blazor.Application.Features.KeyValues.Commands.AddEdit;
@using SixLabors.ImageSharp
@using SixLabors.ImageSharp.Formats
@using SixLabors.ImageSharp.Processing
@inherits MudComponentBase
@inject IStringLocalizer<Dictionaries> L
<MudDialog>
<DialogContent>
<MudForm Model="model" @ref="_form" Validation="@(modelValidator.ValidateValue)">
<MudGrid>
<MudItem xs="12" md="6">
<MudEnumSelect Label="@L["Name"]" @bind-Value="model.Name"
For="@(() => model.Name)"
Required="true"
RequiredError="@L["Name is required!"]">
</MudEnumSelect>
</MudItem>
<MudItem xs="12" md="6">
<MudTextField Label="@L["Description"]" @bind-Value="model.Description"
For="@(() => model.Description)">
</MudTextField>
</MudItem>
<MudItem xs="12" md="6">
<MudTextField Label="@L["Value"]" @bind-Value="model.Value"
For="@(() => model.Value)"
Required="true"
RequiredError="@L["Value is required!"]">
</MudTextField>
</MudItem>
<MudItem xs="12" md="6">
<MudTextField Label="@L["Text"]" @bind-Value="model.Text"
For="@(() => model.Text)"
Required="true"
RequiredError="@L["Text is required!"]">
</MudTextField>
</MudItem>

</MudGrid>
</MudForm>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">@ConstantString.CANCEL</MudButton>
<MudLoadingButton Loading="@_saving" Color="MudBlazor.Color.Primary" OnClick="Submit">@ConstantString.SAVE</MudLoadingButton>
</DialogActions>
</MudDialog>

@code {
MudForm? _form = default!;
bool _saving = false;
[CascadingParameter]
MudDialogInstance MudDialog { get; set; } = default!;
AddEditKeyValueCommandValidator modelValidator = new AddEditKeyValueCommandValidator();
[EditorRequired][Parameter] public AddEditKeyValueCommand model { get; set; } = default!;
[Inject]
private IMediator _mediator { get; set; } = default!;
async Task Submit()
{
try
{
_saving = true;
await _form!.Validate().ConfigureAwait(false);
if (!_form!.IsValid)
return;
var result = await _mediator.Send(model);

if (result.Succeeded)
{
MudDialog.Close(DialogResult.Ok(true));
Snackbar.Add(ConstantString.SAVESUCCESS, MudBlazor.Severity.Info);
}
else
{
Snackbar.Add(result.ErrorMessage, MudBlazor.Severity.Error);
}
}
finally
{
_saving = false;
}
}
void Cancel() => MudDialog.Cancel();
}