feat/Log-Information #1
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
|
@ -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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue