diff --git a/.gitignore b/.gitignore index 32d9c064d..57f13079f 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ DataDir/ # Exclude test reports (local test report, not for the repo) /test-reports +/doc/**/.*.md diff --git a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs index 778f2c4ed..3d135987b 100644 --- a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs +++ b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs @@ -35,6 +35,7 @@ public static class ServiceCollectionHelpers var billingClient = new BillingApiClient(api, () => settings.ApiUrl); var estimateClient = new EstimateApiClient(api, () => settings.ApiUrl); var frontClient = new FrontOfficeApiClient(api, () => settings.ApiUrl); + var resourceUsageClient = new ResourceUsageApiClient(api, () => settings.ApiUrl); var userDirectory = new UserDirectory(userSearchClient); var reverseGeocoding = new NominatimReverseGeocodingService(); @@ -75,6 +76,7 @@ public static class ServiceCollectionHelpers services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); // ViewModels services.AddSingleton(settings); @@ -87,6 +89,7 @@ public static class ServiceCollectionHelpers services.AddSingleton(billingClient); services.AddSingleton(estimateClient); services.AddSingleton(frontClient); + services.AddSingleton(resourceUsageClient); services.AddSingleton(userFilesClient); services.AddSingleton(reverseGeocoding); services.AddSingleton(userDirectory); diff --git a/src/PostIt/PostIt/ViewLocator.cs b/src/PostIt/PostIt/ViewLocator.cs index ffaaadc66..01ecb0c25 100644 --- a/src/PostIt/PostIt/ViewLocator.cs +++ b/src/PostIt/PostIt/ViewLocator.cs @@ -60,6 +60,7 @@ public class ViewLocator : IDataTemplate EstimateListPageViewModel => services.GetRequiredService(), EstimateValidationPageViewModel => services.GetRequiredService(), MyFilesViewModel => services.GetRequiredService(), + ResourceUsagePageViewModel => services.GetRequiredService(), ChatViewModel => services.GetRequiredService(), null => new TextBlock { Text = "No view for " }, _ => new TextBlock { Text = $"No view for {data.GetType().Name}" } diff --git a/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs b/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs index b76f31f27..56a3979e0 100644 --- a/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs @@ -36,12 +36,14 @@ public class HomePageViewModel : ViewModelBase OpenProviderEstimates = new AsyncRelayCommand(() => OpenEstimateListAsync(EstimateListPerspective.Provider)); OpenBlogs = new AsyncRelayCommand(App.PushBlogsPageAsync); OpenMyFiles = new AsyncRelayCommand(OpenMyFilesAsync); + OpenResourceUsage = new AsyncRelayCommand(OpenResourceUsageAsync); OpenChat = new AsyncRelayCommand(OpenChatAsync); } public IAsyncRelayCommand OpenChat { get; } public IAsyncRelayCommand OpenBlogs { get; } + public IAsyncRelayCommand OpenResourceUsage { get; } public IAsyncRelayCommand OpenActivities { get; } public IAsyncRelayCommand OpenProviderRequests { get; } public IAsyncRelayCommand OpenClientEstimates { get; } @@ -138,6 +140,25 @@ public class HomePageViewModel : ViewModelBase await app.PushPageAsync(vm); } + private async Task OpenResourceUsageAsync() + { + var app = (App?)Application.Current; + if (app is null) + { + throw new InvalidOperationException("Application PostIt indisponible."); + } + + var resourceUsageClient = app.ServiceProvider?.GetRequiredService(); + if (resourceUsageClient is null) + { + throw new InvalidOperationException("Client usage indisponible."); + } + + var vm = new ResourceUsagePageViewModel(resourceUsageClient); + await vm.InitializeAsync(); + await app.PushPageAsync(vm); + } + /// /// Avalonia designer constructor. Builds a self-contained VM /// with a freshly-constructed Settings so the XAML preview can diff --git a/src/PostIt/PostIt/ViewModels/Layout/ResourceUsagePageViewModel.cs b/src/PostIt/PostIt/ViewModels/Layout/ResourceUsagePageViewModel.cs new file mode 100644 index 000000000..d1c0d754c --- /dev/null +++ b/src/PostIt/PostIt/ViewModels/Layout/ResourceUsagePageViewModel.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Yavsc.Abstract.Resources; +using Yavsc.Api.Client; + +namespace PostIt.ViewModels; + +public partial class ResourceUsagePageViewModel : ViewModelBase +{ + private readonly ResourceUsageApiClient _client; + + [ObservableProperty] + public partial bool IsBusy { get; set; } + + [ObservableProperty] + public partial bool IsAdminView { get; set; } + + [ObservableProperty] + public partial ResourceUsageSummary? CurrentSummary { get; set; } + + [ObservableProperty] + public partial ObservableCollection Overview { get; set; } = new(); + + public string Heading => IsAdminView ? "Vue d'administration des usages" : "Mon usage des ressources"; + + public override bool CanNavigateNext { get => false; protected set { _ = value; } } + public override bool CanNavigatePrevious { get => true; protected set { _ = value; } } + + public ResourceUsagePageViewModel(ResourceUsageApiClient client) + { + _client = client ?? throw new ArgumentNullException(nameof(client)); + } + + public Task InitializeAsync() => RefreshAsync(); + + [RelayCommand] + public async Task RefreshAsync() + { + IsBusy = true; + try + { + Overview.Clear(); + + try + { + var adminOverview = await _client.GetAdminOverviewAsync().ConfigureAwait(true); + if (adminOverview is { Count: > 0 }) + { + foreach (var item in adminOverview) + { + Overview.Add(item); + } + + CurrentSummary = adminOverview[0]; + IsAdminView = true; + return; + } + } + catch (Exception) + { + // Non-admin users are not allowed on the admin route; fall back + // to the current-user summary below. + } + + var current = await _client.GetCurrentAsync().ConfigureAwait(true); + CurrentSummary = current; + if (current is not null) + { + Overview.Clear(); + Overview.Add(current); + } + + IsAdminView = false; + } + catch (Exception ex) + { + CurrentSummary = null; + Overview.Clear(); + IsAdminView = false; + throw new InvalidOperationException($"Impossible de charger l'usage des ressources : {ex.Message}", ex); + } + finally + { + IsBusy = false; + } + } +} diff --git a/src/PostIt/PostIt/Views/Layout/HomePage.axaml b/src/PostIt/PostIt/Views/Layout/HomePage.axaml index 1de3ece68..f2497086d 100644 --- a/src/PostIt/PostIt/Views/Layout/HomePage.axaml +++ b/src/PostIt/PostIt/Views/Layout/HomePage.axaml @@ -44,5 +44,9 @@ Command="{Binding OpenMyFiles}" HorizontalAlignment="Center" IsEnabled="{Binding SessionStatus.IsLoggedIn}"/> +