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

using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
using CleanArchitecture.Blazor.Application.Features.AuditTrails.Caching;
using CleanArchitecture.Blazor.Application.Features.AuditTrails.DTOs;
using CleanArchitecture.Blazor.Application.Features.Products.Queries;

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

Expand All @@ -13,9 +17,11 @@ public class AuditTrailsWithPaginationQuery : PaginationFilterBase, ICacheableRe
public string? Keyword { get; set; }
[OperatorComparison(OperatorType.Equal)]
public AuditType? AuditType { get; set; }
[CompareTo(typeof(SearchAuditTrailsWithListView),"Id")]
public AuditTrailListView ListView { get; set; }= AuditTrailListView.All;
public override string ToString()
{
return $"Search:{Keyword},Sort:{Sort},SortBy:{SortBy},{Page},{PerPage}";
return $"Listview:{ListView},AuditType:{AuditType},Search:{Keyword},Sort:{Sort},SortBy:{SortBy},{Page},{PerPage}";
}
public string CacheKey => AuditTrailsCacheKey.GetPaginationCacheKey($"{this}");
public MemoryCacheEntryOptions? Options => AuditTrailsCacheKey.MemoryCacheEntryOptions;
Expand Down Expand Up @@ -45,6 +51,42 @@ public async Task<PaginatedData<AuditTrailDto>> Handle(AuditTrailsWithPagination

return data;
}



}
public class SearchAuditTrailsWithListView : FilteringOptionsBaseAttribute
{
public override Expression BuildExpression(Expression expressionBody, PropertyInfo targetProperty, PropertyInfo filterProperty, object value)
{
var today = DateTime.Now.Date;
var start = Convert.ToDateTime(today.ToString("yyyy-MM-dd", CultureInfo.CurrentCulture) + " 00:00:00", CultureInfo.CurrentCulture);
var end = Convert.ToDateTime(today.ToString("yyyy-MM-dd", CultureInfo.CurrentCulture) + " 23:59:59", CultureInfo.CurrentCulture);
var last30days= Convert.ToDateTime(today.AddDays(-30).ToString("yyyy-MM-dd", CultureInfo.CurrentCulture) + " 00:00:00", CultureInfo.CurrentCulture);
//var currentUser = filterProperty.CurrentUser;
var listview = (AuditTrailListView)value;
return listview switch
{
AuditTrailListView.All => expressionBody,
AuditTrailListView.Last30days => Expression.GreaterThanOrEqual(Expression.Property(expressionBody, "DateTime"),
Expression.Constant(last30days, typeof(DateTime)))
.Combine(Expression.LessThanOrEqual(Expression.Property(expressionBody, "DateTime"),
Expression.Constant(end, typeof(DateTime))),
CombineType.And),
AuditTrailListView.CreatedToday => Expression.GreaterThanOrEqual(Expression.Property(expressionBody, "DateTime"),
Expression.Constant(start, typeof(DateTime)))
.Combine(Expression.LessThanOrEqual(Expression.Property(expressionBody, "DateTime"),
Expression.Constant(end, typeof(DateTime))),
CombineType.And),
_ => expressionBody
};
}
}
public enum AuditTrailListView
{
[Description("All")]
All,
[Description("Created Toady")]
CreatedToday,
[Description("View of the last 30 days")]
Last30days,
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ public class KeyValuesWithPaginationQuery : PaginationFilterBase, ICacheableRequ
[StringFilterOptions(StringFilterOption.Contains)]
public string? Keyword { get; set; }
[OperatorComparison(OperatorType.Equal)]
[CompareTo("Name")]
public Picklist? Picklist { get; set; }

public override string ToString()
{
return $"Picklist:{Picklist},Search:{Keyword},Sort:{Sort},SortBy:{SortBy},{Page},{PerPage}";
}
public string CacheKey => $"{nameof(KeyValuesWithPaginationQuery)},{this}";

public MemoryCacheEntryOptions? Options => KeyValueCacheKey.MemoryCacheEntryOptions;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,97 @@
// 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.AuditTrails.Caching;
using CleanArchitecture.Blazor.Application.Features.Documents.Caching;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
using CleanArchitecture.Blazor.Application.Features.Loggers.Caching;
using CleanArchitecture.Blazor.Application.Features.Loggers.DTOs;

namespace CleanArchitecture.Blazor.Application.Logs.Queries.PaginationQuery;

public class LogsWithPaginationQuery : PaginationFilter, ICacheableRequest<PaginatedData<LogDto>>
public class LogsWithPaginationQuery : PaginationFilterBase, ICacheableRequest<PaginatedData<LogDto>>
{
[CompareTo("Message", "Exception", "UserName", "ClientIP")]
[StringFilterOptions(StringFilterOption.Contains)]
public string? Keyword { get; set; }

[CompareTo(typeof(SearchLogsWithListView), "Id")]
public LogListView ListView { get; set; } = LogListView.All;
public override string ToString()
{
return $"Listview:{ListView},Search:{Keyword},Sort:{Sort},SortBy:{SortBy},{Page},{PerPage}";
}
public string CacheKey => LogsCacheKey.GetPaginationCacheKey($"{this}");
public MemoryCacheEntryOptions? Options => LogsCacheKey.MemoryCacheEntryOptions;
}
public class LogsQueryHandler : IRequestHandler<LogsWithPaginationQuery, PaginatedData<LogDto>>
{
private readonly ICurrentUserService _currentUserService;
private readonly IApplicationDbContext _context;
private readonly IMapper _mapper;

public LogsQueryHandler(
ICurrentUserService currentUserService,
IApplicationDbContext context,
IMapper mapper
)
{
_currentUserService = currentUserService;
_context = context;
_mapper = mapper;
}
public async Task<PaginatedData<LogDto>> Handle(LogsWithPaginationQuery request, CancellationToken cancellationToken)
{


var data = await _context.Loggers
.Where(x=>x.Message!.Contains(request.Keyword) || x.Exception!.Contains(request.Keyword))
.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectTo<LogDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(request.PageNumber, request.PageSize);
var data = await _context.Loggers.ApplyFilterWithoutPagination(request)
.ProjectTo<LogDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(request.Page, request.PerPage);

return data;
}



}
public class SearchLogsWithListView : FilteringOptionsBaseAttribute
{
public override Expression BuildExpression(Expression expressionBody, PropertyInfo targetProperty, PropertyInfo filterProperty, object value)
{
var today = DateTime.Now.Date;
var start = Convert.ToDateTime(today.ToString("yyyy-MM-dd", CultureInfo.CurrentCulture) + " 00:00:00", CultureInfo.CurrentCulture);
var end = Convert.ToDateTime(today.ToString("yyyy-MM-dd", CultureInfo.CurrentCulture) + " 23:59:59", CultureInfo.CurrentCulture);
var last30days = Convert.ToDateTime(today.AddDays(-30).ToString("yyyy-MM-dd", CultureInfo.CurrentCulture) + " 00:00:00", CultureInfo.CurrentCulture);
var listview = (LogListView)value;
return listview switch
{
LogListView.All => expressionBody,
LogListView.Information => Expression.Equal(Expression.Property(expressionBody, "Level"),Expression.Constant("Information")),
LogListView.Warning => Expression.Equal(Expression.Property(expressionBody, "Level"), Expression.Constant("Warning")),
LogListView.Fatal => Expression.Equal(Expression.Property(expressionBody, "Level"), Expression.Constant("Fatal")),
LogListView.Error => Expression.Equal(Expression.Property(expressionBody, "Level"), Expression.Constant("Error")),
LogListView.Last30days => Expression.GreaterThanOrEqual(Expression.Property(expressionBody, "TimeStamp"),
Expression.Constant(last30days, typeof(DateTime)))
.Combine(Expression.LessThanOrEqual(Expression.Property(expressionBody, "TimeStamp"),
Expression.Constant(end, typeof(DateTime))),
CombineType.And),
LogListView.CreatedToday => Expression.GreaterThanOrEqual(Expression.Property(expressionBody, "TimeStamp"),
Expression.Constant(start, typeof(DateTime)))
.Combine(Expression.LessThanOrEqual(Expression.Property(expressionBody, "TimeStamp"),
Expression.Constant(end, typeof(DateTime))),
CombineType.And),
_ => expressionBody
};
}
}
public enum LogListView
{
[Description("All")]
All,
[Description("Created Toady")]
CreatedToday,
[Description("View of the last 30 days")]
Last30days,
[Description("View of Information")]
Information,
[Description("View of Warning")]
Warning,
[Description("View of Error")]
Error,
[Description("View of Fatal")]
Fatal,
}
49 changes: 25 additions & 24 deletions src/Blazor.Server.UI/Pages/SystemManagement/AuditTrails.razor
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
<MudIcon Icon="@Icons.Material.Filled.ReceiptLong" Size="Size.Large" />
<div class="d-flex flex-column">
<MudText Typo="Typo.caption">@L[Title]</MudText>

<MudEnumSelect Style="min-width:220px" TEnum="AuditTrailListView" ValueChanged="OnChangedListView" Value="_query.ListView" Dense="true" Label="List View">
</MudEnumSelect>
</div>
</div>
<div class="flex-grow-1" />
Expand All @@ -42,13 +43,13 @@
StartIcon="@Icons.Material.Filled.Refresh" IconColor="Color.Surface" Color="Color.Primary"
Style="margin-right: 5px;">@ConstantString.REFRESH</MudButton>
</div>
<MudStack Row="true" AlignItems="AlignItems.Stretch">
<MudEnumSelect TEnum="AuditType?" Placeholder="Search for audit type" Value="@_searchAuditType" 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"

<MudStack Row="true" AlignItems="AlignItems.Stretch">
<MudEnumSelect TEnum="AuditType?" Placeholder="Search for audit type" Value="@_query.AuditType" Clearable="true" ValueChanged="@(s=>OnSearch(s))" Style="width:160px" FullWidth="true"> </MudEnumSelect>
<MudTextField T="string" ValueChanged="@(s=>OnSearch(s))" Value="@_query.Keyword" Placeholder="Search" Adornment="Adornment.End" Style="width:260px" FullWidth="true"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Small"></MudTextField>
</MudStack>
</MudStack>


</div>
</div>
Expand Down Expand Up @@ -110,7 +111,7 @@
<code class="docs-code docs-code-primary">
@context.OldValues
</code>


<MudText>New Value</MudText>
<code class="docs-code docs-code-primary">
Expand Down Expand Up @@ -140,50 +141,50 @@
</ErrorBoundary>
@code {
public string Title { get; private set; } = "Audit Trails";
private string _searchString = string.Empty;
private AuditType? _searchAuditType = null;
private MudTable<AuditTrailDto> _table = default!;
private bool _loading;
private int _defaultPageSize = 15;
[Inject]
private IMediator _mediator { get; set; } = default!;

private AuditTrailsWithPaginationQuery _query = new();
private async Task<TableData<AuditTrailDto>> ServerReload(TableState state)
{
try
{
_loading = true;
var request = new AuditTrailsWithPaginationQuery()
{
Keyword = _searchString,
AuditType = _searchAuditType,
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);

_query.Sort = string.IsNullOrEmpty(state.SortLabel) ? "Id" : state.SortLabel;
_query.SortBy = (state.SortDirection == SortDirection.Ascending ? AutoFilterer.Enums.Sorting.Ascending : AutoFilterer.Enums.Sorting.Descending);
_query.Page = state.Page + 1;
_query.PerPage = state.PageSize;

var result = await _mediator.Send(_query).ConfigureAwait(false);
return new TableData<AuditTrailDto>() { TotalItems = result.TotalItems, Items = result.Items };
}
finally
{
_loading = false;
}
}
private async Task OnChangedListView(AuditTrailListView listview)
{
_query.ListView = listview;
await _table.ReloadServerData();
}
private async Task OnSearch(string text)
{
_searchString = text;
_query.Keyword = text;
await _table.ReloadServerData();
}
private async Task OnSearch(AuditType? val)
{
_searchAuditType = val;
_query.AuditType = val;
await _table.ReloadServerData();
}
private async Task OnRefresh()
{
AuditTrailsCacheKey.Refresh();
_searchString = string.Empty;
_query.Keyword = string.Empty;
await _table.ReloadServerData();
}
private Task OnShowDetail(AuditTrailDto dto)
Expand Down
41 changes: 20 additions & 21 deletions src/Blazor.Server.UI/Pages/SystemManagement/Dictionaries.razor
Original file line number Diff line number Diff line change
Expand Up @@ -129,27 +129,26 @@
}
</MudHidden>
<MudHidden Breakpoint="Breakpoint.SmAndDown" Invert="true">
<MudMenu AnchorOrigin="Origin.BottomLeft" StartIcon="@Icons.Material.Filled.KeyboardCommandKey" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@L["Action"]" Color="Color.Primary" Variant="Variant.Filled">
<MudMenuItem Disabled="@_loading" OnClick="@(()=>OnRefresh())">@ConstantString.REFRESH</MudMenuItem>
@if (_canCreate)
{
<MudMenuItem Disabled="@_editing" OnClick="OnCreate">@ConstantString.CREATE</MudMenuItem>
}
@if (_canDelete)
{
<MudMenuItem Disabled="@(!(_selectedItems.Count>0) || _editing)" OnClick="OnDeleteChecked">@ConstantString.DELETE</MudMenuItem>
}
@if (_canImport)
{
<InputFile id="importdataInput" OnChange="OnImportData" hidden accept=".xlsx" />
<MudMenuItem for="importdataInput"
HtmlTag="label">@ConstantString.IMPORT</MudMenuItem>
}
@if (_canExport)
{
<MudMenuItem OnClick="OnExport">@ConstantString.EXPORT</MudMenuItem>
}
</MudMenu>
@if (_canCreate)
{
<MudButton DisableElevation Variant="Variant.Outlined" Color="Color.Primary"
Disabled="@_editing"
StartIcon="@Icons.Material.Filled.Add"
Size="Size.Small"
OnClick="OnCreate"
Style="margin-right: 4px; margin-bottom:4px"
IconColor="Color.Surface">@ConstantString.CREATE</MudButton>
}
@if (_canDelete)
{
<MudButton DisableElevation Variant="Variant.Outlined" Color="Color.Secondary"
StartIcon="@Icons.Material.Filled.Delete"
Disabled="@(!(_selectedItems.Count>0) || _editing)"
OnClick="OnDeleteChecked"
Size="Size.Small"
Style="margin-right: 4px; margin-bottom:4px"
IconColor="Color.Surface">@ConstantString.DELETE</MudButton>
}
</MudHidden>
</div>
@if (_canSearch)
Expand Down
Loading