feat/Log-Information #1
|
@ -40,6 +40,7 @@
|
|||
public const string Metadata = "api/metadata";
|
||||
public const string MetadataById = "api/metadata/{0}";
|
||||
|
||||
public const string Log = "api/logs";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using Http.Core.Constants;
|
||||
using Http.Core.Contexts;
|
||||
using Http.Core.Extensions;
|
||||
using Http.Core.Models;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
|
@ -31,13 +30,13 @@ namespace Http.Core.Handlers
|
|||
return false;
|
||||
|
||||
var expiredTime = UserContext.Instance.ExpiredTime;
|
||||
if (expiredTime != null || expiredTime - DateTime.UtcNow < _requiredRefreshTime)
|
||||
if (expiredTime != null && expiredTime - DateTime.UtcNow < _requiredRefreshTime)
|
||||
{
|
||||
await _refreshSemaphore.WaitAsync();
|
||||
bool refreshSuccess;
|
||||
{
|
||||
expiredTime = UserContext.Instance.ExpiredTime;
|
||||
if (expiredTime != null || expiredTime - DateTime.UtcNow < _requiredRefreshTime)
|
||||
if (expiredTime != null && expiredTime - DateTime.UtcNow < _requiredRefreshTime)
|
||||
refreshSuccess = await RefreshTokenAsync();
|
||||
else
|
||||
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\BooleanToStringConverter.cs" />
|
||||
<Compile Include="Converters\BooleanToVisibilityConverter.cs" />
|
||||
<Compile Include="Converters\LogTimestampConverter.cs" />
|
||||
<Compile Include="Converters\OpacityConverter.cs" />
|
||||
<Compile Include="Converters\UserRoleToBooleanConverter.cs" />
|
||||
<Compile Include="Converters\UserStatusConverter.cs" />
|
||||
|
@ -246,6 +247,7 @@
|
|||
<DependentUpon>UserListPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ViewModels\LogViewModel.cs" />
|
||||
<Compile Include="ViewModels\UpsertMetadataViewModel.cs" />
|
||||
<Compile Include="ViewModels\CategoriesViewModel.cs" />
|
||||
<Compile Include="ViewModels\UpsertCategoryViewModel.cs" />
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<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="People" Content="Users" x:Name="Users" Tag="Users" Background="{ThemeResource SystemAccentColor}"/>
|
||||
<NavigationViewItem Icon="Folder" Content="Logs" x:Name="Logs" Tag="Logs" Background="{ThemeResource SystemAccentColor}"/>
|
||||
<NavigationViewItem Icon="Clock" Content="Logs" x:Name="Logs" Tag="Logs" Background="{ThemeResource SystemAccentColor}"/>
|
||||
</NavigationView.MenuItems>
|
||||
|
||||
<Frame x:Name="ContentFrame"/>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using PCUT.Pages.Logs;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
|
@ -1,14 +1,66 @@
|
|||
<Page
|
||||
x:Class="PCUT.Pages.Logs.LogInformationPage"
|
||||
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.UserGuide"
|
||||
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"
|
||||
mc:Ignorable="d"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
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>
|
||||
|
|
|
@ -1,17 +1,52 @@
|
|||
using Windows.UI.Xaml.Controls;
|
||||
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.Logs
|
||||
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