diff --git a/contrib/Makefile b/contrib/Makefile index 4e4ef4df6..79145668f 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -95,15 +95,6 @@ showConfig: @echo CONFIGURATION: $(CONFIGURATION) @echo BASEAPPDIR: $(BASEAPPDIR) -showApiLogs: - @sudo journalctl -u yavscApi.service -S "2 min ago" | tee yavscApi.log - -showOrgLogs: - @sudo journalctl -u yavscOrg.service -S "2 min ago" | tee yavscOrg.log - -showBlogsLogs: - @sudo journalctl -u yavscBlogs.service -S "2 min ago" | tee yavscBlogs.log - clean: @rm -rf generated diff --git a/src/PostIt/PostIt.Tests/EstimateEditionPageViewModelTests.cs b/src/PostIt/PostIt.Tests/EstimateEditionPageViewModelTests.cs deleted file mode 100644 index db70e25fa..000000000 --- a/src/PostIt/PostIt.Tests/EstimateEditionPageViewModelTests.cs +++ /dev/null @@ -1,270 +0,0 @@ -using System.Net.Http; -using PostIt.ViewModels; -using Yavsc; -using Yavsc.Api.Client; - -namespace PostIt.Tests; - -public class EstimateEditionPageViewModelTests -{ - private static BillingQuerySummaryDto SampleQuery() => new() - { - Id = 42, - BillingCode = "Brush", - ActivityCode = "hair", - PerformerId = "perf-1", - ClientId = "cli-1", - Status = QueryStatus.InProgress, - Description = "Coupe simple", - EventDate = new DateTime(2026, 9, 12, 10, 0, 0, DateTimeKind.Utc), - }; - - private static EstimateEditionPageViewModel CreateViewModel(StubEstimateApi api, BillingQuerySummaryDto? query = null) - { - var client = new EstimateApiClient(api, "https://business.example/api/v1/"); - return new EstimateEditionPageViewModel(query ?? SampleQuery(), client); - } - - [Fact] - public void Constructor_prefills_description_and_adds_a_first_line() - { - var api = new StubEstimateApi(); - var vm = CreateViewModel(api); - - Assert.Equal("Coupe simple", vm.EstimateDescription); - Assert.Single(vm.Lines); - Assert.Same(vm.Lines[0], vm.SelectedLine); - Assert.Contains("#42", vm.ContextLabel); - Assert.Contains("cli-1", vm.ContextLabel); - } - - [Fact] - public void AddLine_appends_and_selects_the_new_line() - { - var api = new StubEstimateApi(); - var vm = CreateViewModel(api); - - vm.AddLineCommand.Execute(null); - - Assert.Equal(2, vm.Lines.Count); - Assert.Same(vm.Lines[1], vm.SelectedLine); - } - - [Fact] - public void RemoveLine_removes_the_selected_line() - { - var api = new StubEstimateApi(); - var vm = CreateViewModel(api); - var first = vm.Lines[0]; - - vm.RemoveLineCommand.Execute(null); - - Assert.Empty(vm.Lines); - Assert.Null(vm.SelectedLine); - Assert.False(vm.RemoveLineCommand.CanExecute(null)); - Assert.DoesNotContain(first, vm.Lines); - } - - [Fact] - public void Total_sums_line_totals_and_tracks_edits() - { - var api = new StubEstimateApi(); - var vm = CreateViewModel(api); - - vm.Lines[0].Count = 2; - vm.Lines[0].UnitaryCost = 15.5m; - - Assert.Equal(31m, vm.Total); - Assert.Equal($"{31m:0.00} EUR", vm.TotalLabel); - - vm.AddLineCommand.Execute(null); - vm.Lines[1].Count = 1; - vm.Lines[1].UnitaryCost = 9m; - - Assert.Equal(40m, vm.Total); - } - - [Fact] - public async Task Send_without_title_warns_and_does_not_post() - { - var api = new StubEstimateApi(); - var vm = CreateViewModel(api); - vm.Lines[0].Name = "Coupe"; - vm.Lines[0].Description = "Coupe simple"; - vm.Lines[0].UnitaryCost = 25m; - - await vm.SendCommand.ExecuteAsync(null); - - Assert.Null(api.LastBody); - Assert.Equal(StatusSeverity.Warning, vm.ActionStatus.Severity); - Assert.Contains("titre", vm.ActionStatus.Message); - } - - [Fact] - public async Task Send_without_any_line_warns_and_does_not_post() - { - var api = new StubEstimateApi(); - var vm = CreateViewModel(api); - vm.EstimateTitle = "Devis coupe"; - vm.Lines.Clear(); - - await vm.SendCommand.ExecuteAsync(null); - - Assert.Null(api.LastBody); - Assert.Equal(StatusSeverity.Warning, vm.ActionStatus.Severity); - Assert.Contains("ligne", vm.ActionStatus.Message); - } - - [Fact] - public async Task Send_with_a_blank_line_name_warns_and_does_not_post() - { - var api = new StubEstimateApi(); - var vm = CreateViewModel(api); - vm.EstimateTitle = "Devis coupe"; - vm.Lines[0].Description = "Oubli du nom"; - - await vm.SendCommand.ExecuteAsync(null); - - Assert.Null(api.LastBody); - Assert.Equal(StatusSeverity.Warning, vm.ActionStatus.Severity); - Assert.Contains("nom", vm.ActionStatus.Message); - } - - [Fact] - public async Task Send_posts_the_estimate_payload_to_the_estimate_route() - { - var api = new StubEstimateApi(); - var vm = CreateViewModel(api); - vm.EstimateTitle = " Devis coupe "; - vm.Lines[0].Name = "Coupe"; - vm.Lines[0].Description = "Coupe simple"; - vm.Lines[0].Count = 2.4m; - vm.Lines[0].UnitaryCost = 25m; - - await vm.SendCommand.ExecuteAsync(null); - - Assert.Equal("https://business.example/api/v1/estimate", api.LastPath); - Assert.Equal(HttpMethod.Post, api.LastMethod); - - var payload = Assert.IsType(api.LastBody); - Assert.Equal(42, payload.CommandId); - Assert.Equal("cli-1", payload.ClientId); - Assert.Equal("Brush", payload.CommandType); - Assert.Equal("Devis coupe", payload.Title); - Assert.Equal("Coupe simple", payload.Description); - Assert.Empty(payload.AttachedFiles); - Assert.Empty(payload.AttachedGraphics); - - var line = Assert.Single(payload.Bill); - Assert.Equal("Coupe", line.Name); - Assert.Equal(2, line.Count); - Assert.Equal(25m, line.UnitaryCost); - Assert.Equal("EUR", line.Currency); - } - - [Fact] - public async Task Send_marks_the_page_as_sent_and_disables_resend() - { - var api = new StubEstimateApi(); - var vm = CreateViewModel(api); - vm.EstimateTitle = "Devis coupe"; - vm.Lines[0].Name = "Coupe"; - vm.Lines[0].Description = "Coupe simple"; - vm.Lines[0].UnitaryCost = 25m; - - await vm.SendCommand.ExecuteAsync(null); - - Assert.True(vm.HasSent); - Assert.False(vm.SendCommand.CanExecute(null)); - Assert.Equal("Devis envoyé", vm.SendLabel); - Assert.Equal(StatusSeverity.Info, vm.ActionStatus.Severity); - Assert.Contains("#7", vm.ActionStatus.Message); - } - - [Fact] - public async Task Send_surfaces_server_errors_as_error_status() - { - var api = new StubEstimateApi { Failure = new HttpRequestException("boom", null, System.Net.HttpStatusCode.InternalServerError) }; - var vm = CreateViewModel(api); - vm.EstimateTitle = "Devis coupe"; - vm.Lines[0].Name = "Coupe"; - vm.Lines[0].Description = "Coupe simple"; - - await vm.SendCommand.ExecuteAsync(null); - - Assert.False(vm.HasSent); - Assert.Equal(StatusSeverity.Error, vm.ActionStatus.Severity); - Assert.True(vm.SendCommand.CanExecute(null)); - } - - [Fact] - public async Task Send_accepts_negative_amounts_for_discount_lines() - { - var api = new StubEstimateApi(); - var vm = CreateViewModel(api); - vm.EstimateTitle = "Devis avec remise"; - vm.Lines[0].Name = "Coupe"; - vm.Lines[0].Description = "Coupe simple"; - vm.Lines[0].UnitaryCost = 25m; - - vm.AddLineCommand.Execute(null); - vm.Lines[1].Name = "Remise fidélité"; - vm.Lines[1].Description = "Remise client régulier"; - vm.Lines[1].UnitaryCost = -5m; - - Assert.Equal(20m, vm.Total); - - await vm.SendCommand.ExecuteAsync(null); - - var payload = Assert.IsType(api.LastBody); - Assert.Equal(2, payload.Bill.Count); - Assert.Equal(-5m, payload.Bill[1].UnitaryCost); - Assert.True(vm.HasSent); - } - - private sealed class StubEstimateApi : IYavscApiClient - { - public HttpClient Http { get; } = new(); - public string? LastPath { get; private set; } - public HttpMethod? LastMethod { get; private set; } - public object? LastBody { get; private set; } - public Exception? Failure { get; init; } - - public Task CallAsync(HttpMethod method, string path, object? body = null, CancellationToken ct = default) - { - LastMethod = method; - LastPath = path; - LastBody = body; - - if (Failure is not null) - { - throw Failure; - } - - if (typeof(T) == typeof(EstimateCreatedDto)) - { - var payload = (EstimateDto)body!; - var created = new EstimateCreatedDto { Id = 7, Bill = payload.Bill }; - return Task.FromResult((T)(object)created); - } - - return Task.FromResult(default(T)!); - } - - public Task CallAsync(HttpMethod method, string path, object? body = null, CancellationToken ct = default) - { - LastMethod = method; - LastPath = path; - LastBody = body; - return Task.CompletedTask; - } - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public ValueTask DisposeAsync() => ValueTask.CompletedTask; - } -} diff --git a/src/PostIt/PostIt.Tests/PostItViewModelTests.cs b/src/PostIt/PostIt.Tests/PostItViewModelTests.cs index 8ecc5bb55..821a778dd 100644 --- a/src/PostIt/PostIt.Tests/PostItViewModelTests.cs +++ b/src/PostIt/PostIt.Tests/PostItViewModelTests.cs @@ -23,11 +23,13 @@ public class PostItViewModelTests viewModel.Posts.Add(new BlogPostDto { Id = 3, Title = "Third post", Article = "Search me", AuthorId = "carol" }); viewModel.SearchText = "search"; + viewModel.SearchCommand.Execute(null); Assert.Single(viewModel.FilteredPosts); Assert.Equal(3, viewModel.FilteredPosts[0].Id); viewModel.SearchText = "bob"; + viewModel.SearchCommand.Execute(null); Assert.Single(viewModel.FilteredPosts); Assert.Equal(2, viewModel.FilteredPosts[0].Id); diff --git a/src/PostIt/PostIt.Tests/ProviderOngoingRequestsPageViewModelTests.cs b/src/PostIt/PostIt.Tests/ProviderOngoingRequestsPageViewModelTests.cs index cfb300316..945072870 100644 --- a/src/PostIt/PostIt.Tests/ProviderOngoingRequestsPageViewModelTests.cs +++ b/src/PostIt/PostIt.Tests/ProviderOngoingRequestsPageViewModelTests.cs @@ -126,36 +126,6 @@ public class ProviderOngoingRequestsPageViewModelTests Assert.Equal(ProviderOngoingRequestsPageViewModel.SortByDate, vm.SelectedSortOption); } - [Fact] - public async Task CreateEstimateForSelectedCommand_requires_a_selection() - { - var api = new StubProviderApi(); - var client = new BillingApiClient(api, "https://business.example/api/v1/"); - var estimateClient = new EstimateApiClient(api, "https://business.example/api/v1/"); - var vm = new ProviderOngoingRequestsPageViewModel(client, estimateClient: estimateClient); - - await vm.InitializeAsync(); - - Assert.False(vm.CreateEstimateForSelectedCommand.CanExecute(null)); - - vm.SelectedQuery = vm.Queries[0]; - - Assert.True(vm.CreateEstimateForSelectedCommand.CanExecute(null)); - } - - [Fact] - public async Task CreateEstimateForSelectedCommand_is_disabled_without_estimate_client() - { - var api = new StubProviderApi(); - var client = new BillingApiClient(api, "https://business.example/api/v1/"); - var vm = new ProviderOngoingRequestsPageViewModel(client); - - await vm.InitializeAsync(); - vm.SelectedQuery = vm.Queries[0]; - - Assert.False(vm.CreateEstimateForSelectedCommand.CanExecute(null)); - } - private sealed class StubProviderApi : IYavscApiClient { public HttpClient Http { get; } = new(); diff --git a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs index 6bd54ca20..0ebd2af19 100644 --- a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs +++ b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs @@ -30,7 +30,6 @@ public static class ServiceCollectionHelpers () => settings.ApiUrl, () => settings.Authentication?.Authority); var billingClient = new BillingApiClient(api, () => settings.ApiUrl); - var estimateClient = new EstimateApiClient(api, () => settings.ApiUrl); var userDirectory = new UserDirectory(userSearchClient); var reverseGeocoding = new NominatimReverseGeocodingService(); @@ -61,8 +60,6 @@ public static class ServiceCollectionHelpers services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); - // ViewModels services.AddSingleton(settings); services.AddSingleton(api); @@ -72,7 +69,6 @@ public static class ServiceCollectionHelpers services.AddSingleton(userSearchClient); services.AddSingleton(activityClient); services.AddSingleton(billingClient); - services.AddSingleton(estimateClient); services.AddSingleton(reverseGeocoding); services.AddSingleton(userDirectory); services.AddSingleton(); diff --git a/src/PostIt/PostIt/ViewLocator.cs b/src/PostIt/PostIt/ViewLocator.cs index 6f8c5d902..72bd2559e 100644 --- a/src/PostIt/PostIt/ViewLocator.cs +++ b/src/PostIt/PostIt/ViewLocator.cs @@ -53,7 +53,6 @@ public class ViewLocator : IDataTemplate BillingQueriesPageViewModel => services.GetRequiredService(), BillingQueryDetailsPageViewModel => services.GetRequiredService(), ProviderOngoingRequestsPageViewModel => services.GetRequiredService(), - EstimateEditionPageViewModel => 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/Activity/EstimateEditionPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/EstimateEditionPageViewModel.cs deleted file mode 100644 index 8f007dc08..000000000 --- a/src/PostIt/PostIt/ViewModels/Activity/EstimateEditionPageViewModel.cs +++ /dev/null @@ -1,293 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Avalonia; -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; -using PostIt.Helpers; -using Yavsc.Api.Client; - -namespace PostIt.ViewModels; - -/// -/// Edition d'un devis (Estimate) créé en réponse à une demande -/// client () consultée depuis la -/// page « Mes demandes en cours ». L'envoi poste le devis sur -/// api/v1/estimate; côté serveur, la commande liée -/// () est alors marquée comme -/// validée par le prestataire. -/// -public partial class EstimateEditionPageViewModel : ViewModelBase, IActionStatusViewModel -{ - private readonly EstimateApiClient _estimateClient; - private readonly BillingQuerySummaryDto _query; - - public long QueryId => _query.Id; - public string ClientId => _query.ClientId; - public string BillingCode => _query.BillingCode; - - public string Title => $"Devis — demande #{_query.Id}"; - - public string ContextLabel - => $"Demande #{_query.Id} · {BillingCode} · client {ClientId}"; - - public string QueryDescription => string.IsNullOrWhiteSpace(_query.Description) - ? "(sans description)" - : _query.Description; - - [ObservableProperty] - public partial string EstimateTitle { get; set; } = string.Empty; - - [ObservableProperty] - public partial string EstimateDescription { get; set; } = string.Empty; - - [ObservableProperty] - public partial ObservableCollection Lines { get; set; } = new(); - - [ObservableProperty, NotifyCanExecuteChangedFor(nameof(RemoveLineCommand))] - public partial EstimateLineItemViewModel? SelectedLine { get; set; } - - [ObservableProperty, NotifyCanExecuteChangedFor(nameof(SendCommand))] - public partial bool IsBusy { get; set; } - - /// - /// True une fois le devis accepté par le serveur: l'envoi est - /// désactivé pour éviter les doublons, il ne reste que « Retour ». - /// - [ObservableProperty, NotifyCanExecuteChangedFor(nameof(SendCommand))] - public partial bool HasSent { get; set; } - - [ObservableProperty] - public partial string StatusMessage { get; set; } = "Prêt."; - - [ObservableProperty] - public partial StatusNotice ActionStatus { get; set; } = StatusNotice.Info("Prêt."); - - public decimal Total => Lines.Sum(line => line.LineTotal); - - public string TotalLabel => $"{Total:0.00} {Lines.FirstOrDefault()?.Currency ?? "EUR"}"; - - public string SendLabel => HasSent ? "Devis envoyé" : "Envoyer le devis"; - - public override bool CanNavigateNext - { - get => false; - protected set { _ = value; } - } - - public override bool CanNavigatePrevious - { - get => true; - protected set { _ = value; } - } - - public EstimateEditionPageViewModel(BillingQuerySummaryDto query, EstimateApiClient estimateClient) - { - _query = query ?? throw new ArgumentNullException(nameof(query)); - _estimateClient = estimateClient ?? throw new ArgumentNullException(nameof(estimateClient)); - - EstimateDescription = query.Description ?? string.Empty; - Lines.CollectionChanged += OnLinesCollectionChanged; - - AddLine(); - this.SetInfoStatus("Complétez le devis puis envoyez-le. La demande associée sera validée."); - } - - [RelayCommand] - private void AddLine() - { - var line = new EstimateLineItemViewModel(); - Lines.Add(line); - SelectedLine = line; - } - - private bool CanRemoveLine() => SelectedLine is not null && !IsBusy && !HasSent; - - [RelayCommand(CanExecute = nameof(CanRemoveLine))] - private void RemoveLine() - { - if (SelectedLine is null) - { - return; - } - - var index = Lines.IndexOf(SelectedLine); - Lines.Remove(SelectedLine); - SelectedLine = Lines.Count == 0 - ? null - : Lines[Math.Min(index, Lines.Count - 1)]; - } - - private bool CanSend() => !IsBusy && !HasSent; - - [RelayCommand(CanExecute = nameof(CanSend))] - private async Task SendAsync() - { - if (!TryValidate(out var validationMessage)) - { - this.SetWarningStatus(validationMessage); - return; - } - - IsBusy = true; - try - { - var payload = BuildPayload(); - var created = await _estimateClient.CreateAsync(payload).ConfigureAwait(true); - - HasSent = true; - OnPropertyChanged(nameof(SendLabel)); - this.SetInfoStatus( - $"Devis #{created.Id} envoyé ({created.Bill.Count} ligne(s)). La demande #{QueryId} est validée."); - } - catch (HttpRequestException ex) when (ex.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden) - { - this.SetWarningStatus("Accès refusé à l'API devis (scope 'api'). Déconnectez puis reconnectez-vous."); - } - catch (Exception ex) - { - this.SetErrorStatus($"Erreur lors de l'envoi du devis: {ex.Message}"); - } - finally - { - IsBusy = false; - } - } - - [RelayCommand] - private async Task BackAsync() - { - var app = (App?)Application.Current; - if (app is null) - { - throw new InvalidOperationException("Application PostIt indisponible."); - } - - await app.GoBackAsync().ConfigureAwait(true); - } - - internal EstimateDto BuildPayload() - { - return new EstimateDto - { - CommandId = QueryId, - ClientId = ClientId, - CommandType = BillingCode, - Title = EstimateTitle.Trim(), - Description = EstimateDescription.Trim(), - Bill = Lines.Select(line => new EstimateLineDto - { - Id = line.Id, - Name = line.Name.Trim(), - Description = line.Description.Trim(), - Count = Math.Max(1, (int)Math.Round(line.Count)), - UnitaryCost = line.UnitaryCost, - Currency = string.IsNullOrWhiteSpace(line.Currency) ? "EUR" : line.Currency.Trim(), - }).ToList(), - }; - } - - private bool TryValidate(out string message) - { - if (string.IsNullOrWhiteSpace(EstimateTitle)) - { - message = "Le titre du devis est requis."; - return false; - } - - if (string.IsNullOrWhiteSpace(ClientId)) - { - message = "La demande sélectionnée n'identifie pas de client."; - return false; - } - - if (string.IsNullOrWhiteSpace(BillingCode)) - { - message = "La demande sélectionnée n'a pas de code de facturation."; - return false; - } - - if (Lines.Count == 0) - { - message = "Ajoutez au moins une ligne au devis."; - return false; - } - - foreach (var line in Lines) - { - if (string.IsNullOrWhiteSpace(line.Name)) - { - message = "Chaque ligne doit avoir un nom."; - return false; - } - - if (line.Name.Trim().Length > 256) - { - message = $"Le nom de la ligne « {line.Name.Trim()[..20]}… » dépasse 256 caractères."; - return false; - } - - if (string.IsNullOrWhiteSpace(line.Description)) - { - message = $"La ligne « {line.Name.Trim()} » doit avoir une description."; - return false; - } - - if (line.Description.Trim().Length > 512) - { - message = $"La description de la ligne « {line.Name.Trim()} » dépasse 512 caractères."; - return false; - } - - if (line.Count < 1) - { - message = $"La quantité de la ligne « {line.Name.Trim()} » doit être d'au moins 1."; - return false; - } - } - - message = string.Empty; - return true; - } - - private void OnLinesCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) - { - if (e.OldItems is not null) - { - foreach (var item in e.OldItems.OfType()) - { - item.PropertyChanged -= OnLinePropertyChanged; - } - } - - if (e.NewItems is not null) - { - foreach (var item in e.NewItems.OfType()) - { - item.PropertyChanged += OnLinePropertyChanged; - } - } - - RaiseTotalsChanged(); - } - - private void OnLinePropertyChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName is nameof(EstimateLineItemViewModel.LineTotal) - or nameof(EstimateLineItemViewModel.Currency)) - { - RaiseTotalsChanged(); - } - } - - private void RaiseTotalsChanged() - { - OnPropertyChanged(nameof(Total)); - OnPropertyChanged(nameof(TotalLabel)); - } -} diff --git a/src/PostIt/PostIt/ViewModels/Activity/EstimateLineItemViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/EstimateLineItemViewModel.cs deleted file mode 100644 index 864906cdd..000000000 --- a/src/PostIt/PostIt/ViewModels/Activity/EstimateLineItemViewModel.cs +++ /dev/null @@ -1,35 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; - -namespace PostIt.ViewModels; - -/// -/// Editable estimate line. is exposed as a -/// so it binds directly to -/// NumericUpDown.Value (decimal?); it is rounded back -/// to an integer when the DTO is built. -/// -public partial class EstimateLineItemViewModel : ObservableObject -{ - public long Id { get; set; } - - [ObservableProperty] - public partial string Name { get; set; } = string.Empty; - - [ObservableProperty] - public partial string Description { get; set; } = string.Empty; - - [ObservableProperty, NotifyPropertyChangedFor(nameof(LineTotal))] - [NotifyPropertyChangedFor(nameof(LineTotalLabel))] - public partial decimal Count { get; set; } = 1m; - - [ObservableProperty, NotifyPropertyChangedFor(nameof(LineTotal))] - [NotifyPropertyChangedFor(nameof(LineTotalLabel))] - public partial decimal UnitaryCost { get; set; } - - [ObservableProperty] - public partial string Currency { get; set; } = "EUR"; - - public decimal LineTotal => Count * UnitaryCost; - - public string LineTotalLabel => $"{LineTotal:0.00}"; -} diff --git a/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs index 71e979e66..562f24988 100644 --- a/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs @@ -22,7 +22,6 @@ public partial class ProviderOngoingRequestsPageViewModel : ViewModelBase, IActi public const string SortByStatus = "Statut (en cours d'abord)"; private readonly BillingApiClient _billingClient; - private readonly EstimateApiClient? _estimateClient; private readonly Settings? _settings; private List _allQueries = new(); @@ -43,8 +42,6 @@ public partial class ProviderOngoingRequestsPageViewModel : ViewModelBase, IActi public partial string SelectedSortOption { get; set; } = SortByDate; [ObservableProperty, NotifyCanExecuteChangedFor(nameof(OpenSelectedQueryCommand))] - [NotifyCanExecuteChangedFor(nameof(OpenSelectedEditorCommand))] - [NotifyCanExecuteChangedFor(nameof(CreateEstimateForSelectedCommand))] public partial BillingQuerySummaryDto? SelectedQuery { get; set; } [ObservableProperty] @@ -70,13 +67,9 @@ public partial class ProviderOngoingRequestsPageViewModel : ViewModelBase, IActi protected set { _ = value; } } - public ProviderOngoingRequestsPageViewModel( - BillingApiClient billingClient, - Settings? settings = null, - EstimateApiClient? estimateClient = null) + public ProviderOngoingRequestsPageViewModel(BillingApiClient billingClient, Settings? settings = null) { _billingClient = billingClient ?? throw new ArgumentNullException(nameof(billingClient)); - _estimateClient = estimateClient; _settings = settings; if (_settings is not null) @@ -217,33 +210,6 @@ public partial class ProviderOngoingRequestsPageViewModel : ViewModelBase, IActi } } - private bool CanCreateEstimateForSelected() => SelectedQuery is not null && _estimateClient is not null; - - [RelayCommand(CanExecute = nameof(CanCreateEstimateForSelected))] - public async Task CreateEstimateForSelectedAsync() - { - if (SelectedQuery is null) - { - this.SetWarningStatus("Sélectionnez une demande."); - return; - } - - if (_estimateClient is null) - { - this.SetWarningStatus("Le client devis n'est pas disponible."); - return; - } - - var app = (App?)Application.Current; - if (app is null) - { - throw new InvalidOperationException("Application PostIt indisponible."); - } - - var vm = new EstimateEditionPageViewModel(SelectedQuery, _estimateClient); - await app.PushPageAsync(vm).ConfigureAwait(true); - } - partial void OnFilterTextChanged(string value) { ApplyFilter(); diff --git a/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs b/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs index 78be2e789..8ac05c85e 100644 --- a/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs @@ -96,6 +96,12 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel }); } + [RelayCommand] + internal async Task SearchAsync() { + await RefreshAsync(); + ApplyFilter(); + } + [RelayCommand] internal async Task SaveAsync() { diff --git a/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs b/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs index fd2ff1091..673177863 100644 --- a/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs @@ -65,9 +65,7 @@ public class HomePageViewModel : ViewModelBase throw new InvalidOperationException("Client billing indisponible."); } - var estimateClient = app.ServiceProvider?.GetRequiredService(); - - var vm = new ProviderOngoingRequestsPageViewModel(billingClient, Settings, estimateClient); + var vm = new ProviderOngoingRequestsPageViewModel(billingClient, Settings); await vm.InitializeAsync(); await app.PushPageAsync(vm); } diff --git a/src/PostIt/PostIt/ViewModels/Settings/Settings.cs b/src/PostIt/PostIt/ViewModels/Settings/Settings.cs index 6b6ed5d4f..fee7206ef 100644 --- a/src/PostIt/PostIt/ViewModels/Settings/Settings.cs +++ b/src/PostIt/PostIt/ViewModels/Settings/Settings.cs @@ -346,7 +346,7 @@ public partial class Settings : ViewModelBase // → our overridden dispatcher-safe marshaller below. else lock (_mutationGate) { - var legacyApiUrl = TryReadApiUrl(json); + var legacyApiUrl = TryReadLegacyApiUrl(json); this.Authentication = settings.Authentication; this.DarkMode = settings.DarkMode; this.BlogsApiUrl = !string.IsNullOrWhiteSpace(settings.BlogsApiUrl) @@ -402,7 +402,7 @@ public partial class Settings : ViewModelBase } } - private static string? TryReadApiUrl(string json) + private static string? TryReadLegacyApiUrl(string json) { try { diff --git a/src/PostIt/PostIt/Views/Activity/EstimateEditionPage.axaml b/src/PostIt/PostIt/Views/Activity/EstimateEditionPage.axaml deleted file mode 100644 index 7b25ab226..000000000 --- a/src/PostIt/PostIt/Views/Activity/EstimateEditionPage.axaml +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - - -