feat/Log-Information (#1)

Co-authored-by: drake.pham <drake.pham@icetea.io>
Co-authored-by: kwan.nguyen <kwan.nguyen@vatek.asia>
Reviewed-on: #1
This commit is contained in:
alexhoang 2024-10-25 04:53:49 +00:00
parent fa53c519c0
commit 2b925a8ffa
10 changed files with 245 additions and 3 deletions

View File

@ -40,6 +40,7 @@
public const string Metadata = "api/metadata"; public const string Metadata = "api/metadata";
public const string MetadataById = "api/metadata/{0}"; public const string MetadataById = "api/metadata/{0}";
public const string Log = "api/logs";
} }
} }
} }

View File

@ -1,7 +1,6 @@
using Http.Core.Constants; using Http.Core.Constants;
using Http.Core.Contexts; using Http.Core.Contexts;
using Http.Core.Extensions; using Http.Core.Extensions;
using Http.Core.Models;
using System; using System;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
@ -31,13 +30,13 @@ namespace Http.Core.Handlers
return false; return false;
var expiredTime = UserContext.Instance.ExpiredTime; var expiredTime = UserContext.Instance.ExpiredTime;
if (expiredTime != null || expiredTime - DateTime.UtcNow < _requiredRefreshTime) if (expiredTime != null && expiredTime - DateTime.UtcNow < _requiredRefreshTime)
{ {
await _refreshSemaphore.WaitAsync(); await _refreshSemaphore.WaitAsync();
bool refreshSuccess; bool refreshSuccess;
{ {
expiredTime = UserContext.Instance.ExpiredTime; expiredTime = UserContext.Instance.ExpiredTime;
if (expiredTime != null || expiredTime - DateTime.UtcNow < _requiredRefreshTime) if (expiredTime != null && expiredTime - DateTime.UtcNow < _requiredRefreshTime)
refreshSuccess = await RefreshTokenAsync(); refreshSuccess = await RefreshTokenAsync();
else else
refreshSuccess = true; // Some other thread already refreshed token refreshSuccess = true; // Some other thread already refreshed token

13
PCUT/PCUT.Entities/Log.cs Normal file
View File

@ -0,0 +1,13 @@
using Newtonsoft.Json;
using System;
namespace PCUT.Entities
{
public class Log
{
[JsonProperty("datetime")]
public DateTime Timestamp { get; set; }
[JsonProperty("content")]
public string Content { get; set; }
}
}

View File

@ -0,0 +1,26 @@
using System;
using Windows.UI.Xaml.Data;
namespace PCUT.Converters
{
internal class LogTimestampConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
switch (value)
{
case DateTime timeValue:
{
return $"[{timeValue.ToString("yyyy-MM-dd HH:mm:ss.fff K")}]";
}
default:
return string.Empty;
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@ -137,6 +137,7 @@
<Compile Include="Converters\BooleanInverter.cs" /> <Compile Include="Converters\BooleanInverter.cs" />
<Compile Include="Converters\BooleanToStringConverter.cs" /> <Compile Include="Converters\BooleanToStringConverter.cs" />
<Compile Include="Converters\BooleanToVisibilityConverter.cs" /> <Compile Include="Converters\BooleanToVisibilityConverter.cs" />
<Compile Include="Converters\LogTimestampConverter.cs" />
<Compile Include="Converters\OpacityConverter.cs" /> <Compile Include="Converters\OpacityConverter.cs" />
<Compile Include="Converters\UserRoleToBooleanConverter.cs" /> <Compile Include="Converters\UserRoleToBooleanConverter.cs" />
<Compile Include="Converters\UserStatusConverter.cs" /> <Compile Include="Converters\UserStatusConverter.cs" />
@ -194,6 +195,9 @@
<Compile Include="Pages\CategoriesManagement\CategoriesViewPage.xaml.cs"> <Compile Include="Pages\CategoriesManagement\CategoriesViewPage.xaml.cs">
<DependentUpon>CategoriesViewPage.xaml</DependentUpon> <DependentUpon>CategoriesViewPage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\Logs\LogInformationPage.xaml.cs">
<DependentUpon>LogInformationPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\UserGuide\UserGuidePage.xaml.cs"> <Compile Include="Pages\UserGuide\UserGuidePage.xaml.cs">
<DependentUpon>UserGuidePage.xaml</DependentUpon> <DependentUpon>UserGuidePage.xaml</DependentUpon>
</Compile> </Compile>
@ -243,6 +247,7 @@
<DependentUpon>UserListPage.xaml</DependentUpon> <DependentUpon>UserListPage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ViewModels\LogViewModel.cs" />
<Compile Include="ViewModels\UpsertMetadataViewModel.cs" /> <Compile Include="ViewModels\UpsertMetadataViewModel.cs" />
<Compile Include="ViewModels\CategoriesViewModel.cs" /> <Compile Include="ViewModels\CategoriesViewModel.cs" />
<Compile Include="ViewModels\UpsertCategoryViewModel.cs" /> <Compile Include="ViewModels\UpsertCategoryViewModel.cs" />
@ -361,6 +366,10 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Pages\Logs\LogInformationPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Pages\UserGuide\UserGuidePage.xaml"> <Page Include="Pages\UserGuide\UserGuidePage.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>

View File

@ -29,6 +29,7 @@
<NavigationViewItem Icon="Document" Content="Categories" x:Name="Categories" Tag="Categories" Background="{ThemeResource SystemAccentColor}"/> <NavigationViewItem Icon="Document" Content="Categories" x:Name="Categories" Tag="Categories" Background="{ThemeResource SystemAccentColor}"/>
<NavigationViewItem Icon="Folder" Content="Files" x:Name="Files" Tag="Files" Background="{ThemeResource SystemAccentColor}"/> <NavigationViewItem Icon="Folder" Content="Files" x:Name="Files" Tag="Files" Background="{ThemeResource SystemAccentColor}"/>
<NavigationViewItem Icon="People" Content="Users" x:Name="Users" Tag="Users" Background="{ThemeResource SystemAccentColor}"/> <NavigationViewItem Icon="People" Content="Users" x:Name="Users" Tag="Users" Background="{ThemeResource SystemAccentColor}"/>
<NavigationViewItem Icon="Clock" Content="Logs" x:Name="Logs" Tag="Logs" Background="{ThemeResource SystemAccentColor}"/>
</NavigationView.MenuItems> </NavigationView.MenuItems>
<Frame x:Name="ContentFrame"/> <Frame x:Name="ContentFrame"/>

View File

@ -53,6 +53,9 @@ namespace PCUT.Pages
case "Users": case "Users":
ContentFrame.Navigate(typeof(UserListPage)); ContentFrame.Navigate(typeof(UserListPage));
break; break;
case "Logs":
ContentFrame.Navigate(typeof(LogInformationPage));
break;
} }
sender.IsPaneOpen = true; sender.IsPaneOpen = true;
} }

View File

@ -0,0 +1,66 @@
<Page
x:Class="PCUT.Pages.LogInformationPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PCUT.Pages"
xmlns:localCV="using:PCUT.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodels="using:PCUT.ViewModels"
mc:Ignorable="d">
<Page.Resources>
<localCV:LogTimestampConverter x:Key="timestampConverter"/>
</Page.Resources>
<Page.DataContext>
<viewmodels:LogViewModel/>
</Page.DataContext>
<Page.Background>
<SolidColorBrush Color="White" />
</Page.Background>
<Page.Transitions>
<TransitionCollection>
<EntranceThemeTransition />
</TransitionCollection>
</Page.Transitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.9*"/>
<RowDefinition Height="0.1*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="1">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
<Button x:Name="btnFirst" Content="First" Click="btnFirst_Click"/>
<Button x:Name="btnPrevious" Content="Previous" Click="btnPrevious_Click"/>
<TextBlock x:Name="txtPageNumber" VerticalAlignment="Center" Margin="10">
<Run Text="{Binding Pagination.Page, Mode=OneWay}" />
<Run Text="/" />
<Run Text="{Binding Pagination.TotalPage, Mode=OneWay}" />
</TextBlock>
<Button x:Name="btnNext" Content="Next" Click="btnNext_Click"/>
<Button x:Name="btnLast" Content="Last" Click="btnLast_Click"/>
<ComboBox
VerticalAlignment="Center"
ItemsSource="{Binding Pagination.AvailablePageSizes}"
SelectedItem="{Binding Pagination.PageSize, Mode=TwoWay}" />
<TextBlock VerticalAlignment="Center" Text="records/page"/>
<TextBlock VerticalAlignment="Center">
<Run Text=" (total" />
<Run Text="{Binding Pagination.TotalRecords, Mode=TwoWay}" />
<Run Text="records)" />
</TextBlock>
</StackPanel>
</Grid>
<ListView Grid.Row="0" ItemsSource="{Binding Logs}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Timestamp, Converter={StaticResource timestampConverter}}"/>
<Run Text="{Binding Content}"/>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>

View File

@ -0,0 +1,52 @@
using PCUT.ViewModels;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
namespace PCUT.Pages
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class LogInformationPage : Page
{
public LogViewModel ViewModels { get; set; }
public LogInformationPage()
{
this.InitializeComponent();
ViewModels = DataContext as LogViewModel;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ViewModels.Pagination.Page = 1;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
ViewModels.Pagination.Dispose();
}
private void btnFirst_Click(object sender, RoutedEventArgs e)
{
ViewModels.Pagination.Page = 1;
}
private void btnLast_Click(object sender, RoutedEventArgs e)
{
ViewModels.Pagination.Page = ViewModels.Pagination.TotalPage;
}
private void btnPrevious_Click(object sender, RoutedEventArgs e)
{
ViewModels.Pagination.Page--;
}
private void btnNext_Click(object sender, RoutedEventArgs e)
{
ViewModels.Pagination.Page++;
}
}
}

View File

@ -0,0 +1,72 @@
using Http.Core;
using Http.Core.Extensions;
using PCUT.Entities;
using PCUT.Entities.ApiResponse;
using PCUT.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using static Http.Core.Constants.HttpConstants;
namespace PCUT.ViewModels
{
public class LogViewModel : NotificationBase
{
public PaginationViewModel Pagination { get; } = new PaginationViewModel(withSearch: false);
public ObservableCollection<Log> Logs { get; }
public LogViewModel()
{
Logs = new ObservableCollection<Log>();
Pagination.PageChanged += OnPageChanged;
}
private async void OnPageChanged(object sender, PageChangedEventArgs args)
{
await LoadLogsAsync(args.Page, args.PageSize);
}
public async Task LoadLogsAsync(int page, int pageSize)
{
var url = CreateFilterUrl(page, pageSize);
using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient))
{
try
{
var response = await httpClient.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var apiResponse = await response.DeserializeObjectAsync<PaginationResponse<IEnumerable<Log>>>();
var data = apiResponse.Data;
Pagination.Page = apiResponse.Pagination.Page;
Pagination.TotalPage = apiResponse.Pagination.TotalPages;
Pagination.TotalRecords = apiResponse.Pagination.TotalRecords;
Logs.Clear();
foreach (var log in data)
Logs.Add(log);
}
}
catch (Exception ex)
{
}
}
}
private string CreateFilterUrl(int page, int pageSize/*, string searchText*/)
{
var builder = PathBuilder.FromRouteTemplate(Api.Log)
.AddQuery("page", page.ToString())
.AddQuery("pageSize", pageSize.ToString());
//if (!string.IsNullOrEmpty(searchText))
//{
// var filter = new StringBuilder().AppendFilter("username", "cn", searchText).BuildFilter().ToString();
// builder.AddQuery("filter", filter);
//}
return builder.Build();
}
}
}