postit: persist settings save and apply ApiUrl changes without restart #51
8 changed files with 444 additions and 31 deletions
commit
dc42b6ed0c
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -4,13 +4,23 @@
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
* [PostIt] Ajout d'un `BillingQueryDetailsPageViewModel` et de sa page associee pour afficher le detail d'une commande billing depuis l'historique.
|
||||||
|
* [PostIt] Ajout d'un mode detail avec section metier (statut, date, description, motif, infos) et section technique repliable (code, client, provision, lieu, prestations).
|
||||||
|
* [PostIt] Ajout d'un badge de statut enrichi (couleur + pictogramme) sur le detail d'une commande pour visualiser l'etat en un coup d'oeil.
|
||||||
|
* [PostIt] Ajout d'un bloc d'actions rapide en tete du detail (`Retour`, `Ouvrir en edition`) pour eviter le scroll jusqu'au bas de page.
|
||||||
|
* [PostIt] Ajout d'un style monospace sur les metadonnees techniques (code billing, client, provision, lieu, prestations) pour faciliter la lecture des identifiants et valeurs brutes.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
* [PostIt] Le bouton d'ouverture depuis la liste billing ouvre maintenant une page de detail dediee avant l'eventuelle edition.
|
||||||
|
* [PostIt] Amelioration UX des pages billing: badges de statut colores, actions remontees en haut de page, et typographie monospace sur les metadonnees techniques.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* [Yavsc.Api] Correction d'un 500 sur le refresh du catalogue d'activites lorsque `Activity.Description` est `NULL` en base (nullabilite explicite + projection null-safe + gardes sur codes vides).
|
* [Yavsc.Api] Correction d'un 500 sur le refresh du catalogue d'activites lorsque `Activity.Description` est `NULL` en base (nullabilite explicite + projection null-safe + gardes sur codes vides).
|
||||||
* [Yavsc.Api] Correction des erreurs 400/500 sur les routes billing (`Rdv`, `Brush`, `MBrush`) en imposant `ClientId` depuis l'utilisateur authentifie et en ignorant les champs server-owned lors de la validation modele.
|
* [Yavsc.Api] Correction des erreurs 400/500 sur les routes billing (`Rdv`, `Brush`, `MBrush`) en imposant `ClientId` depuis l'utilisateur authentifie et en ignorant les champs server-owned lors de la validation modele.
|
||||||
* [Yavsc.Api] Correction du `PUT /api/v1/billing/Rdv/{id}`: mise a jour controlee de l'entite existante (et non remplacement brut du graphe JSON), ce qui supprime les `BadRequest` parasites.
|
* [Yavsc.Api] Correction du `PUT /api/v1/billing/Rdv/{id}`: mise a jour controlee de l'entite existante (et non remplacement brut du graphe JSON), ce qui supprime les `BadRequest` parasites.
|
||||||
|
* [Yavsc.Api] Correction PostgreSQL `timestamptz` sur RDV: normalisation UTC de `EventDate` sur `POST/PUT /api/v1/billing/Rdv` pour eviter l'erreur `Cannot write DateTime with Kind=Local`.
|
||||||
* [Yavsc.Api] Correction du flux FrontOffice accept/reject de query: sauvegarde avec contexte utilisateur et fallback d'injection pour `IBillingService` afin d'eviter les erreurs serveur en environnement de test.
|
* [Yavsc.Api] Correction du flux FrontOffice accept/reject de query: sauvegarde avec contexte utilisateur et fallback d'injection pour `IBillingService` afin d'eviter les erreurs serveur en environnement de test.
|
||||||
* [Yavsc.Blogs] Correction des `BadRequest` sur `POST/PUT /api/v1/blogspot` avec payload JSON (PostIt): les proprietes de navigation/serveur (`Author`, `Tags`, `Comments`, audit) ne bloquent plus la validation.
|
* [Yavsc.Blogs] Correction des `BadRequest` sur `POST/PUT /api/v1/blogspot` avec payload JSON (PostIt): les proprietes de navigation/serveur (`Author`, `Tags`, `Comments`, audit) ne bloquent plus la validation.
|
||||||
* [Yavsc.Org] Correction du flux MVC de creation de commentaire: `SaveChangesAsync(userId)` est utilise pour renseigner les champs d'audit requis (`UserCreated`/`UserModified`).
|
* [Yavsc.Org] Correction du flux MVC de creation de commentaire: `SaveChangesAsync(userId)` est utilise pour renseigner les champs d'audit requis (`UserCreated`/`UserModified`).
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ public static class ServiceCollectionHelpers
|
||||||
services.AddTransient<RdvPage>();
|
services.AddTransient<RdvPage>();
|
||||||
services.AddTransient<BrushPage>();
|
services.AddTransient<BrushPage>();
|
||||||
services.AddTransient<BillingQueriesPage>();
|
services.AddTransient<BillingQueriesPage>();
|
||||||
|
services.AddTransient<BillingQueryDetailsPage>();
|
||||||
// ViewModels
|
// ViewModels
|
||||||
services.AddSingleton(settings);
|
services.AddSingleton(settings);
|
||||||
services.AddSingleton<YavscApiClient>(api);
|
services.AddSingleton<YavscApiClient>(api);
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ public class ViewLocator : IDataTemplate
|
||||||
AddCircleMemberDialogViewModel => services.GetRequiredService<AddCircleMemberDialog>(),
|
AddCircleMemberDialogViewModel => services.GetRequiredService<AddCircleMemberDialog>(),
|
||||||
CirclesPageViewModel => services.GetRequiredService<CirclesPage>(),
|
CirclesPageViewModel => services.GetRequiredService<CirclesPage>(),
|
||||||
PostAclDialogViewModel => services.GetRequiredService<PostAclDialog>(),
|
PostAclDialogViewModel => services.GetRequiredService<PostAclDialog>(),
|
||||||
|
BillingQueriesPageViewModel => services.GetRequiredService<BillingQueriesPage>(),
|
||||||
|
BillingQueryDetailsPageViewModel => services.GetRequiredService<BillingQueryDetailsPage>(),
|
||||||
null => new TextBlock { Text = "No view for <null>" },
|
null => new TextBlock { Text = "No view for <null>" },
|
||||||
_ => new TextBlock { Text = $"No view for {data.GetType().Name}" }
|
_ => new TextBlock { Text = $"No view for {data.GetType().Name}" }
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public partial class BillingQueriesPageViewModel : ViewModelBase, IActionStatusV
|
||||||
? $"Demandes en cours ({Form.Title})"
|
? $"Demandes en cours ({Form.Title})"
|
||||||
: $"Commandes {Form.Title}";
|
: $"Commandes {Form.Title}";
|
||||||
public string ContextLabel => $"{Performer.UserName} · {Activity.Name}";
|
public string ContextLabel => $"{Performer.UserName} · {Activity.Name}";
|
||||||
public bool CanOpenDetails => !IsReadOnly;
|
public bool CanOpenDetails => true;
|
||||||
|
|
||||||
public override bool CanNavigateNext
|
public override bool CanNavigateNext
|
||||||
{
|
{
|
||||||
|
|
@ -75,7 +75,7 @@ public partial class BillingQueriesPageViewModel : ViewModelBase, IActionStatusV
|
||||||
|
|
||||||
public Task InitializeAsync() => RefreshAsync();
|
public Task InitializeAsync() => RefreshAsync();
|
||||||
|
|
||||||
private bool CanOpenSelectedQuery() => !IsReadOnly && SelectedQuery is not null;
|
private bool CanOpenSelectedQuery() => SelectedQuery is not null;
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public async Task RefreshAsync()
|
public async Task RefreshAsync()
|
||||||
|
|
@ -114,12 +114,6 @@ public partial class BillingQueriesPageViewModel : ViewModelBase, IActionStatusV
|
||||||
[RelayCommand(CanExecute = nameof(CanOpenSelectedQuery))]
|
[RelayCommand(CanExecute = nameof(CanOpenSelectedQuery))]
|
||||||
public async Task OpenSelectedQueryAsync()
|
public async Task OpenSelectedQueryAsync()
|
||||||
{
|
{
|
||||||
if (IsReadOnly)
|
|
||||||
{
|
|
||||||
this.SetWarningStatus("Mode lecture seule: l'ouverture en modification est désactivée.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SelectedQuery is null)
|
if (SelectedQuery is null)
|
||||||
{
|
{
|
||||||
this.SetWarningStatus("Sélectionnez une commande.");
|
this.SetWarningStatus("Sélectionnez une commande.");
|
||||||
|
|
@ -136,8 +130,13 @@ public partial class BillingQueriesPageViewModel : ViewModelBase, IActionStatusV
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var details = await _billingClient.GetQueryAsync(Form.ActionName, SelectedQuery.Id).ConfigureAwait(true);
|
var details = await _billingClient.GetQueryAsync(Form.ActionName, SelectedQuery.Id).ConfigureAwait(true);
|
||||||
var vm = Form.CreateCommandPageViewModel(Activity, Performer, _billingClient);
|
var vm = new BillingQueryDetailsPageViewModel(
|
||||||
await vm!.InitializeAsync(details).ConfigureAwait(true);
|
Activity,
|
||||||
|
Performer,
|
||||||
|
Form,
|
||||||
|
_billingClient,
|
||||||
|
details,
|
||||||
|
IsReadOnly);
|
||||||
await app.PushPageAsync(vm).ConfigureAwait(true);
|
await app.PushPageAsync(vm).ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
catch (HttpRequestException ex) when (ex.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden)
|
catch (HttpRequestException ex) when (ex.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden)
|
||||||
|
|
|
||||||
208
src/PostIt/PostIt/ViewModels/BillingQueryDetailsPageViewModel.cs
Normal file
208
src/PostIt/PostIt/ViewModels/BillingQueryDetailsPageViewModel.cs
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using PostIt.Helpers;
|
||||||
|
using Yavsc;
|
||||||
|
using Yavsc.Abstract.Workflow;
|
||||||
|
using Yavsc.Api.Client;
|
||||||
|
|
||||||
|
namespace PostIt.ViewModels;
|
||||||
|
|
||||||
|
public partial class BillingQueryDetailsPageViewModel : ViewModelBase, IActionStatusViewModel
|
||||||
|
{
|
||||||
|
private readonly BillingApiClient _billingClient;
|
||||||
|
private readonly BillingQueryDetailsDto _details;
|
||||||
|
|
||||||
|
public ActivityInfo Activity { get; }
|
||||||
|
public ActivityUserDisplayItem Performer { get; }
|
||||||
|
public CommandFormSummary Form { get; }
|
||||||
|
public bool IsReadOnly { get; }
|
||||||
|
|
||||||
|
public long Id => _details.Id;
|
||||||
|
public string Title => $"Detail commande #{_details.Id}";
|
||||||
|
public string ContextLabel => $"{Performer.UserName} · {Activity.Name} · {Form.Title}";
|
||||||
|
public string StatusLabel => _details.Status.ToString();
|
||||||
|
public string StatusGlyph => GetStatusGlyph(_details.Status);
|
||||||
|
public string StatusBadgeBackground => GetStatusBadgeBackground(_details.Status);
|
||||||
|
public string StatusBadgeBorder => GetStatusBadgeBorder(_details.Status);
|
||||||
|
public string StatusBadgeForeground => GetStatusBadgeForeground(_details.Status);
|
||||||
|
public string TitleForeground => StatusBadgeForeground;
|
||||||
|
public string BillingCode => _details.BillingCode;
|
||||||
|
public string Description => EmptyAsPlaceholder(_details.Description, "(sans description)");
|
||||||
|
public string Reason => EmptyAsPlaceholder(_details.Reason, "(aucun motif)");
|
||||||
|
public string AdditionalInfo => EmptyAsPlaceholder(_details.AdditionalInfo, "(aucune info complementaire)");
|
||||||
|
public string ClientId => EmptyAsPlaceholder(_details.ClientId, "(non renseigne)");
|
||||||
|
public string EventDateLabel => _details.EventDate?.ToLocalTime().ToString("f") ?? "Date non precisee";
|
||||||
|
public string ConsentLabel => _details.Consent ? "Oui" : "Non";
|
||||||
|
public string ProvisionalLabel => _details.Provisional.HasValue ? _details.Provisional.Value.ToString("0.00") : "(non renseigne)";
|
||||||
|
public string LocationLabel => BuildLocationLabel(_details.Location);
|
||||||
|
public string PrestationsLabel => BuildPrestationsLabel(_details);
|
||||||
|
public bool CanEdit => !IsReadOnly;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public partial bool IsBusy { get; set; }
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public partial string StatusMessage { get; set; } = "Pret.";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public partial StatusNotice ActionStatus { get; set; } = StatusNotice.Info("Pret.");
|
||||||
|
|
||||||
|
public override bool CanNavigateNext
|
||||||
|
{
|
||||||
|
get => false;
|
||||||
|
protected set { _ = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanNavigatePrevious
|
||||||
|
{
|
||||||
|
get => true;
|
||||||
|
protected set { _ = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public BillingQueryDetailsPageViewModel(
|
||||||
|
ActivityInfo activity,
|
||||||
|
ActivityUserDisplayItem performer,
|
||||||
|
CommandFormSummary form,
|
||||||
|
BillingApiClient billingClient,
|
||||||
|
BillingQueryDetailsDto details,
|
||||||
|
bool isReadOnly)
|
||||||
|
{
|
||||||
|
Activity = activity ?? throw new ArgumentNullException(nameof(activity));
|
||||||
|
Performer = performer ?? throw new ArgumentNullException(nameof(performer));
|
||||||
|
Form = form ?? throw new ArgumentNullException(nameof(form));
|
||||||
|
_billingClient = billingClient ?? throw new ArgumentNullException(nameof(billingClient));
|
||||||
|
_details = details ?? throw new ArgumentNullException(nameof(details));
|
||||||
|
IsReadOnly = isReadOnly;
|
||||||
|
|
||||||
|
this.SetInfoStatus("Details de commande charges.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task OpenEditorAsync()
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
this.SetWarningStatus("Mode lecture seule: edition desactivee.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var app = (App?)Application.Current;
|
||||||
|
if (app is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Application PostIt indisponible.");
|
||||||
|
}
|
||||||
|
|
||||||
|
IsBusy = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var vm = Form.CreateCommandPageViewModel(Activity, Performer, _billingClient);
|
||||||
|
if (vm is null)
|
||||||
|
{
|
||||||
|
this.SetWarningStatus("Ce formulaire n'est pas encore pris en charge en edition.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await vm.InitializeAsync(_details).ConfigureAwait(true);
|
||||||
|
await app.PushPageAsync(vm).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
this.SetErrorStatus($"Erreur lors de l'ouverture en edition: {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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string EmptyAsPlaceholder(string? value, string placeholder)
|
||||||
|
=> string.IsNullOrWhiteSpace(value) ? placeholder : value;
|
||||||
|
|
||||||
|
private static string BuildLocationLabel(BillingLocationDto? location)
|
||||||
|
{
|
||||||
|
if (location is null)
|
||||||
|
{
|
||||||
|
return "(non renseignee)";
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = EmptyAsPlaceholder(location.Address, "adresse vide");
|
||||||
|
if (location.Latitude.HasValue && location.Longitude.HasValue)
|
||||||
|
{
|
||||||
|
text += $" ({location.Latitude.Value:0.####}, {location.Longitude.Value:0.####})";
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildPrestationsLabel(BillingQueryDetailsDto details)
|
||||||
|
{
|
||||||
|
if (details.PrestationIds.Count > 0)
|
||||||
|
{
|
||||||
|
return string.Join(", ", details.PrestationIds.Select(static id => id.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return details.PrestationId.HasValue
|
||||||
|
? details.PrestationId.Value.ToString()
|
||||||
|
: "(aucune)";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetStatusBadgeBackground(QueryStatus status)
|
||||||
|
=> status switch
|
||||||
|
{
|
||||||
|
QueryStatus.Accepted => "#E6F7EC",
|
||||||
|
QueryStatus.InProgress => "#FFF4D6",
|
||||||
|
QueryStatus.Rejected => "#FDECEA",
|
||||||
|
QueryStatus.Failed => "#ECEFF1",
|
||||||
|
QueryStatus.Success => "#E8F8EF",
|
||||||
|
_ => "#EAF3FF",
|
||||||
|
};
|
||||||
|
|
||||||
|
private static string GetStatusBadgeBorder(QueryStatus status)
|
||||||
|
=> status switch
|
||||||
|
{
|
||||||
|
QueryStatus.Accepted => "#2E7D32",
|
||||||
|
QueryStatus.InProgress => "#B26A00",
|
||||||
|
QueryStatus.Rejected => "#C62828",
|
||||||
|
QueryStatus.Failed => "#607D8B",
|
||||||
|
QueryStatus.Success => "#1E8E3E",
|
||||||
|
_ => "#2A5EA8",
|
||||||
|
};
|
||||||
|
|
||||||
|
private static string GetStatusBadgeForeground(QueryStatus status)
|
||||||
|
=> status switch
|
||||||
|
{
|
||||||
|
QueryStatus.Accepted => "#1B5E20",
|
||||||
|
QueryStatus.InProgress => "#7A4A00",
|
||||||
|
QueryStatus.Rejected => "#8E0000",
|
||||||
|
QueryStatus.Failed => "#37474F",
|
||||||
|
QueryStatus.Success => "#145A2A",
|
||||||
|
_ => "#1A4178",
|
||||||
|
};
|
||||||
|
|
||||||
|
private static string GetStatusGlyph(QueryStatus status)
|
||||||
|
=> status switch
|
||||||
|
{
|
||||||
|
QueryStatus.Accepted => "OK",
|
||||||
|
QueryStatus.InProgress => "~",
|
||||||
|
QueryStatus.Rejected => "!",
|
||||||
|
QueryStatus.Failed => "X",
|
||||||
|
QueryStatus.Success => "V",
|
||||||
|
_ => "i",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -5,38 +5,83 @@
|
||||||
x:Class="PostIt.Views.BillingQueriesPage"
|
x:Class="PostIt.Views.BillingQueriesPage"
|
||||||
x:DataType="vm:BillingQueriesPageViewModel"
|
x:DataType="vm:BillingQueriesPageViewModel"
|
||||||
Header="Commandes billing">
|
Header="Commandes billing">
|
||||||
<Grid RowDefinitions="Auto,Auto,*,Auto" Margin="12">
|
<Grid RowDefinitions="Auto,Auto,Auto,*,Auto" Margin="12">
|
||||||
<StackPanel Grid.Row="0" Spacing="2">
|
<StackPanel Grid.Row="0" Spacing="3">
|
||||||
<TextBlock Text="{Binding Title}" FontSize="18" FontWeight="Bold" />
|
<TextBlock Text="{Binding Title}" FontSize="20" FontWeight="Bold" />
|
||||||
<TextBlock Text="{Binding ContextLabel}" Opacity="0.75" />
|
<TextBlock Text="{Binding ContextLabel}" Opacity="0.75" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Spacing="8" Margin="0,12,0,12">
|
<Grid Grid.Row="1" Margin="0,10,0,0" ColumnDefinitions="Auto,8,Auto,8,*">
|
||||||
<Button Content="Rafraîchir" Command="{Binding RefreshCommand}" />
|
<Border Grid.Column="0" Background="#EAF3FF" BorderBrush="#2A5EA8" BorderThickness="1" CornerRadius="6" Padding="8,3">
|
||||||
<Button Content="Ouvrir la commande"
|
<TextBlock Text="{Binding IsReadOnly, StringFormat='Lecture seule : {0}'}" FontSize="11" />
|
||||||
|
</Border>
|
||||||
|
<Border Grid.Column="2" Background="#F2F7F2" BorderBrush="#2F6D2F" BorderThickness="1" CornerRadius="6" Padding="8,3">
|
||||||
|
<TextBlock Text="{Binding OngoingOnly, StringFormat='En cours uniquement : {0}'}" FontSize="11" />
|
||||||
|
</Border>
|
||||||
|
<TextBlock Grid.Column="4"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding Queries.Count, StringFormat='Résultats : {0}'}"
|
||||||
|
Opacity="0.7" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Grid.Row="2" ColumnDefinitions="Auto,8,Auto,*" Margin="0,12,0,12">
|
||||||
|
<Button Grid.Column="0" Content="Rafraîchir" Command="{Binding RefreshCommand}" />
|
||||||
|
<Button Content="Voir le detail"
|
||||||
|
Grid.Column="2"
|
||||||
Command="{Binding OpenSelectedQueryCommand}"
|
Command="{Binding OpenSelectedQueryCommand}"
|
||||||
IsVisible="{Binding CanOpenDetails}" />
|
IsVisible="{Binding CanOpenDetails}" />
|
||||||
</StackPanel>
|
</Grid>
|
||||||
|
|
||||||
<ListBox Grid.Row="2" ItemsSource="{Binding Queries}" SelectedItem="{Binding SelectedQuery, Mode=TwoWay}">
|
<ListBox Grid.Row="3"
|
||||||
|
ItemsSource="{Binding Queries}"
|
||||||
|
SelectedItem="{Binding SelectedQuery, Mode=TwoWay}">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:BillingQueryDisplayItem">
|
<DataTemplate x:DataType="vm:BillingQueryDisplayItem">
|
||||||
<Border BorderThickness="0,0,0,1" BorderBrush="#22000000" Padding="0,0,0,10" Margin="0,0,0,10">
|
<Border BorderThickness="1"
|
||||||
<StackPanel Spacing="3">
|
BorderBrush="#22000000"
|
||||||
<TextBlock Text="{Binding Description}" FontWeight="Bold" />
|
CornerRadius="8"
|
||||||
<TextBlock Text="{Binding Summary}" TextWrapping="Wrap" FontSize="12" Opacity="0.8" />
|
Padding="10"
|
||||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
Margin="0,0,0,8">
|
||||||
<TextBlock Text="{Binding EventDateLabel}" FontSize="11" Opacity="0.7" />
|
<Grid RowDefinitions="Auto,Auto,Auto" ColumnDefinitions="*,Auto">
|
||||||
<TextBlock Text="{Binding StatusLabel}" FontSize="11" Opacity="0.7" />
|
<TextBlock Grid.Row="0"
|
||||||
<TextBlock Text="{Binding BillingCode}" FontSize="11" Opacity="0.6" />
|
Grid.Column="0"
|
||||||
</StackPanel>
|
Text="{Binding Description}"
|
||||||
</StackPanel>
|
FontWeight="Bold"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Text="{Binding StatusLabel}"
|
||||||
|
FontSize="11"
|
||||||
|
Opacity="0.75"
|
||||||
|
HorizontalAlignment="Right" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
Text="{Binding Summary}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
FontSize="12"
|
||||||
|
Opacity="0.85" />
|
||||||
|
|
||||||
|
<Grid Grid.Row="2" Grid.ColumnSpan="2" ColumnDefinitions="*,Auto" Margin="0,8,0,0">
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
Text="{Binding EventDateLabel, StringFormat='Date : {0}'}"
|
||||||
|
FontSize="11"
|
||||||
|
Opacity="0.7" />
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
Text="{Binding BillingCode, StringFormat='Code : {0}'}"
|
||||||
|
FontSize="11"
|
||||||
|
Opacity="0.65"
|
||||||
|
HorizontalAlignment="Right" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
|
|
||||||
<Grid Grid.Row="3" ColumnDefinitions="*,Auto" Margin="0,12,0,0">
|
<Grid Grid.Row="4" ColumnDefinitions="*,Auto" Margin="0,12,0,0">
|
||||||
<postitControls:StatusBar Grid.Column="0"
|
<postitControls:StatusBar Grid.Column="0"
|
||||||
DataContext="{Binding ActionStatus}" />
|
DataContext="{Binding ActionStatus}" />
|
||||||
<ProgressBar Grid.Column="1" Width="120" IsIndeterminate="True" IsVisible="{Binding IsBusy}" />
|
<ProgressBar Grid.Column="1" Width="120" IsIndeterminate="True" IsVisible="{Binding IsBusy}" />
|
||||||
|
|
|
||||||
131
src/PostIt/PostIt/Views/BillingQueryDetailsPage.axaml
Normal file
131
src/PostIt/PostIt/Views/BillingQueryDetailsPage.axaml
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
<ContentPage xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:vm="using:PostIt.ViewModels"
|
||||||
|
xmlns:postitControls="using:PostIt.Controls"
|
||||||
|
x:Class="PostIt.Views.BillingQueryDetailsPage"
|
||||||
|
x:DataType="vm:BillingQueryDetailsPageViewModel"
|
||||||
|
Header="Detail commande">
|
||||||
|
<Grid RowDefinitions="Auto,Auto,*,Auto" Margin="12">
|
||||||
|
<StackPanel Grid.Row="0" Spacing="4">
|
||||||
|
<TextBlock Text="{Binding Title}"
|
||||||
|
FontSize="20"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Foreground="{Binding TitleForeground}" />
|
||||||
|
<TextBlock Text="{Binding ContextLabel}" Opacity="0.75" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1" Margin="0,10,0,0" ColumnDefinitions="Auto,8,Auto,*">
|
||||||
|
<Button Grid.Column="0"
|
||||||
|
Content="Retour"
|
||||||
|
Command="{Binding BackCommand}" />
|
||||||
|
<Button Grid.Column="2"
|
||||||
|
Content="Ouvrir en edition"
|
||||||
|
Command="{Binding OpenEditorCommand}"
|
||||||
|
IsVisible="{Binding CanEdit}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="2" Margin="0,12,0,12">
|
||||||
|
<StackPanel Spacing="10">
|
||||||
|
<Border BorderThickness="1"
|
||||||
|
BorderBrush="#22000000"
|
||||||
|
CornerRadius="8"
|
||||||
|
Padding="10">
|
||||||
|
<Grid ColumnDefinitions="Auto,12,*" RowDefinitions="Auto,Auto" RowSpacing="8">
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Statut" FontWeight="SemiBold" />
|
||||||
|
<Border Grid.Row="0"
|
||||||
|
Grid.Column="2"
|
||||||
|
Background="{Binding StatusBadgeBackground}"
|
||||||
|
BorderBrush="{Binding StatusBadgeBorder}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="6"
|
||||||
|
Padding="8,3"
|
||||||
|
HorizontalAlignment="Left">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<TextBlock Text="{Binding StatusGlyph}"
|
||||||
|
Foreground="{Binding StatusBadgeForeground}"
|
||||||
|
FontWeight="Bold" />
|
||||||
|
<TextBlock Text="{Binding StatusLabel}"
|
||||||
|
Foreground="{Binding StatusBadgeForeground}"
|
||||||
|
FontWeight="SemiBold" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Date" FontWeight="SemiBold" />
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding EventDateLabel}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border BorderThickness="1"
|
||||||
|
BorderBrush="#22000000"
|
||||||
|
CornerRadius="8"
|
||||||
|
Padding="10">
|
||||||
|
<Grid ColumnDefinitions="Auto,12,*" RowDefinitions="Auto,Auto,Auto,Auto" RowSpacing="8">
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Description" FontWeight="SemiBold" />
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding Description}" TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Motif" FontWeight="SemiBold" />
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding Reason}" TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="0" Text="Infos" FontWeight="SemiBold" />
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="2" Text="{Binding AdditionalInfo}" TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="3" Grid.Column="0" Text="Consentement" FontWeight="SemiBold" />
|
||||||
|
<TextBlock Grid.Row="3" Grid.Column="2" Text="{Binding ConsentLabel}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Expander Header="Metadonnees techniques"
|
||||||
|
IsExpanded="False">
|
||||||
|
<Border BorderThickness="1"
|
||||||
|
BorderBrush="#22000000"
|
||||||
|
CornerRadius="8"
|
||||||
|
Padding="10"
|
||||||
|
Margin="0,6,0,0">
|
||||||
|
<Grid ColumnDefinitions="Auto,12,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto" RowSpacing="8">
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Code" FontWeight="SemiBold" />
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Grid.Column="2"
|
||||||
|
Text="{Binding BillingCode}"
|
||||||
|
FontFamily="Consolas, Courier New, monospace" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Client" FontWeight="SemiBold" />
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.Column="2"
|
||||||
|
Text="{Binding ClientId}"
|
||||||
|
FontFamily="Consolas, Courier New, monospace" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="0" Text="Provision" FontWeight="SemiBold" />
|
||||||
|
<TextBlock Grid.Row="2"
|
||||||
|
Grid.Column="2"
|
||||||
|
Text="{Binding ProvisionalLabel}"
|
||||||
|
FontFamily="Consolas, Courier New, monospace" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="3" Grid.Column="0" Text="Lieu" FontWeight="SemiBold" />
|
||||||
|
<TextBlock Grid.Row="3"
|
||||||
|
Grid.Column="2"
|
||||||
|
Text="{Binding LocationLabel}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
FontFamily="Consolas, Courier New, monospace" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4" Grid.Column="0" Text="Prestations" FontWeight="SemiBold" />
|
||||||
|
<TextBlock Grid.Row="4"
|
||||||
|
Grid.Column="2"
|
||||||
|
Text="{Binding PrestationsLabel}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
FontFamily="Consolas, Courier New, monospace" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Expander>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<Grid Grid.Row="3" ColumnDefinitions="*,Auto" Margin="0,8,0,0">
|
||||||
|
<postitControls:StatusBar Grid.Column="0"
|
||||||
|
DataContext="{Binding ActionStatus}" />
|
||||||
|
<ProgressBar Grid.Column="1"
|
||||||
|
Width="120"
|
||||||
|
IsIndeterminate="True"
|
||||||
|
IsVisible="{Binding IsBusy}" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</ContentPage>
|
||||||
17
src/PostIt/PostIt/Views/BillingQueryDetailsPage.axaml.cs
Normal file
17
src/PostIt/PostIt/Views/BillingQueryDetailsPage.axaml.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace PostIt.Views;
|
||||||
|
|
||||||
|
public partial class BillingQueryDetailsPage : ContentPage
|
||||||
|
{
|
||||||
|
public BillingQueryDetailsPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue