postit: unify action status bar with severity across settings/activities/circles/rdv
This commit is contained in:
parent
bfbcaeb400
commit
c1c060799d
12 changed files with 159 additions and 40 deletions
22
src/PostIt/PostIt/Controls/StatusBar.axaml
Normal file
22
src/PostIt/PostIt/Controls/StatusBar.axaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:PostIt.ViewModels"
|
||||
x:Class="PostIt.Controls.StatusBar"
|
||||
x:DataType="vm:StatusNotice">
|
||||
<Border Background="{Binding Background}"
|
||||
BorderBrush="{Binding BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="6"
|
||||
Padding="8">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock Text="{Binding Glyph}"
|
||||
FontWeight="Bold"
|
||||
Foreground="{Binding Foreground}"
|
||||
VerticalAlignment="Top"/>
|
||||
<TextBlock Text="{Binding Message}"
|
||||
Foreground="{Binding Foreground}"
|
||||
TextWrapping="Wrap"
|
||||
VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</UserControl>
|
||||
11
src/PostIt/PostIt/Controls/StatusBar.axaml.cs
Normal file
11
src/PostIt/PostIt/Controls/StatusBar.axaml.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using Avalonia.Controls;
|
||||
|
||||
namespace PostIt.Controls;
|
||||
|
||||
public partial class StatusBar : UserControl
|
||||
{
|
||||
public StatusBar()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
|
@ -43,6 +43,14 @@ public partial class ActivitiesPageViewModel : ViewModelBase
|
|||
[ObservableProperty]
|
||||
public partial string StatusMessage { get; set; } = "Choisissez une activité.";
|
||||
|
||||
[ObservableProperty]
|
||||
public partial StatusNotice ActionStatus { get; set; } = StatusNotice.Info("Choisissez une activité.");
|
||||
|
||||
partial void OnStatusMessageChanged(string value)
|
||||
{
|
||||
ActionStatus = StatusNotice.FromMessage(value);
|
||||
}
|
||||
|
||||
public ActivityInfo? CurrentActivity => SelectedSpecialization ?? SelectedActivity;
|
||||
public string SelectedActivityLabel => SelectedActivity?.Name ?? "(aucune activité)";
|
||||
public string CurrentActivityLabel => CurrentActivity?.Name ?? "(aucune)";
|
||||
|
|
|
|||
|
|
@ -65,6 +65,14 @@ public partial class CirclesPageViewModel : ViewModelBase
|
|||
[ObservableProperty]
|
||||
public partial string StatusMessage { get; set; } = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial StatusNotice ActionStatus { get; set; } = StatusNotice.Info("Pret.");
|
||||
|
||||
partial void OnStatusMessageChanged(string value)
|
||||
{
|
||||
ActionStatus = StatusNotice.FromMessage(value);
|
||||
}
|
||||
|
||||
|
||||
public CirclesPageViewModel(CircleApiClient client)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ public abstract partial class BillingCommandPageViewModel : RemoteViewModelBase
|
|||
[ObservableProperty]
|
||||
public partial string StatusMessage { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial StatusNotice ActionStatus { get; set; } = StatusNotice.Info("Pret.");
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string Reason { get; set; } = string.Empty;
|
||||
|
||||
|
|
@ -89,6 +92,11 @@ public abstract partial class BillingCommandPageViewModel : RemoteViewModelBase
|
|||
OnPropertyChanged(nameof(CanUseCurrentLocation));
|
||||
}
|
||||
|
||||
partial void OnStatusMessageChanged(string value)
|
||||
{
|
||||
ActionStatus = StatusNotice.FromMessage(value);
|
||||
}
|
||||
|
||||
public async Task InitializeAsync(BillingQueryDetailsDto? existingQuery = null)
|
||||
{
|
||||
await LoadAsync();
|
||||
|
|
|
|||
|
|
@ -31,6 +31,19 @@ public partial class Settings : ViewModelBase
|
|||
[ObservableProperty]
|
||||
public partial string SearchText { get; set; } = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial StatusNotice ActionStatus { get; set; } = StatusNotice.Info("Pret.");
|
||||
|
||||
public void SetActionStatus(string message, StatusSeverity severity = StatusSeverity.Info)
|
||||
{
|
||||
ActionStatus = severity switch
|
||||
{
|
||||
StatusSeverity.Error => StatusNotice.Error(message),
|
||||
StatusSeverity.Warning => StatusNotice.Warning(message),
|
||||
_ => StatusNotice.Info(message),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Catch top-level mutations: the four ObservableProperty
|
||||
/// setters above all funnel through here, and we flip
|
||||
|
|
@ -406,6 +419,8 @@ public partial class Settings : ViewModelBase
|
|||
[RelayCommand(CanExecute = nameof(CanSave))]
|
||||
public void Save()
|
||||
{
|
||||
SetActionStatus("Enregistrement des parametres...", StatusSeverity.Info);
|
||||
|
||||
var configDir = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"PostIt");
|
||||
|
|
@ -425,10 +440,13 @@ public partial class Settings : ViewModelBase
|
|||
File.SetUnixFileMode(configPath,
|
||||
UnixFileMode.UserRead | UnixFileMode.UserWrite);
|
||||
IsDirty = false;
|
||||
SetActionStatus("Parametres sauvegardes.", StatusSeverity.Info);
|
||||
|
||||
Console.WriteLine($"💾 Settings saved to {configPath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetActionStatus($"Echec sauvegarde parametres: {ex.Message}", StatusSeverity.Error);
|
||||
Console.Error.WriteLine($"🩎 Error saving settings to {configPath}: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
|
|
|
|||
58
src/PostIt/PostIt/ViewModels/StatusNotice.cs
Normal file
58
src/PostIt/PostIt/ViewModels/StatusNotice.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
namespace PostIt.ViewModels;
|
||||
|
||||
public enum StatusSeverity
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
public sealed class StatusNotice
|
||||
{
|
||||
public string Message { get; }
|
||||
public StatusSeverity Severity { get; }
|
||||
public string Glyph { get; }
|
||||
public string Background { get; }
|
||||
public string BorderBrush { get; }
|
||||
public string Foreground { get; }
|
||||
|
||||
private StatusNotice(string message, StatusSeverity severity)
|
||||
{
|
||||
Message = string.IsNullOrWhiteSpace(message) ? "Pret." : message;
|
||||
Severity = severity;
|
||||
|
||||
(Glyph, Background, BorderBrush, Foreground) = severity switch
|
||||
{
|
||||
StatusSeverity.Error => ("!", "#FDECEA", "#C62828", "#7F1D1D"),
|
||||
StatusSeverity.Warning => ("~", "#FFF8E1", "#E6A700", "#7C4A03"),
|
||||
_ => ("i", "#E8F0FE", "#5B8DEF", "#1E3A8A"),
|
||||
};
|
||||
}
|
||||
|
||||
public static StatusNotice Info(string message) => new(message, StatusSeverity.Info);
|
||||
public static StatusNotice Warning(string message) => new(message, StatusSeverity.Warning);
|
||||
public static StatusNotice Error(string message) => new(message, StatusSeverity.Error);
|
||||
|
||||
public static StatusNotice FromMessage(string? message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
return Info("Pret.");
|
||||
}
|
||||
|
||||
var text = message.Trim();
|
||||
var lower = text.ToLowerInvariant();
|
||||
|
||||
if (lower.StartsWith("erreur") || lower.StartsWith("echec") || lower.StartsWith("impossible"))
|
||||
{
|
||||
return Error(text);
|
||||
}
|
||||
|
||||
if (lower.Contains("refuse") || lower.Contains("annule") || lower.Contains("obligatoire") || lower.Contains("deja"))
|
||||
{
|
||||
return Warning(text);
|
||||
}
|
||||
|
||||
return Info(text);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<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"
|
||||
xmlns:wf="using:Yavsc.Abstract.Workflow"
|
||||
x:Class="PostIt.Views.ActivitiesPage"
|
||||
x:DataType="vm:ActivitiesPageViewModel"
|
||||
|
|
@ -131,16 +132,8 @@
|
|||
</Grid>
|
||||
|
||||
<Grid Grid.Row="2" ColumnDefinitions="*,Auto">
|
||||
<TextBox Grid.Column="0"
|
||||
Text="{Binding StatusMessage}"
|
||||
IsReadOnly="True"
|
||||
AcceptsReturn="True"
|
||||
TextWrapping="Wrap"
|
||||
BorderThickness="0"
|
||||
BorderBrush="Transparent"
|
||||
Padding="0"
|
||||
Background="Transparent"
|
||||
VerticalAlignment="Center" />
|
||||
<postitControls:StatusBar Grid.Column="0"
|
||||
DataContext="{Binding ActionStatus}" />
|
||||
<ProgressBar Grid.Column="1" Width="120" IsIndeterminate="True" IsVisible="{Binding IsBusy}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<ContentPage
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:postitControls="using:PostIt.Controls"
|
||||
x:Class="PostIt.Views.CirclesPage"
|
||||
xmlns:vm="using:PostIt.ViewModels"
|
||||
xmlns:dtos="using:Yavsc.Api.Client.Dtos"
|
||||
|
|
@ -101,8 +102,8 @@
|
|||
|
||||
<!-- Status bar -->
|
||||
<Grid Grid.Row="2" ColumnDefinitions="*,Auto" Margin="12,0,12,12">
|
||||
<TextBlock Grid.Column="0" Text="{Binding StatusMessage}"
|
||||
VerticalAlignment="Center"/>
|
||||
<postitControls:StatusBar Grid.Column="0"
|
||||
DataContext="{Binding ActionStatus}" />
|
||||
<ProgressBar Grid.Column="1" IsIndeterminate="True"
|
||||
IsVisible="{Binding IsBusy}"
|
||||
Width="120"/>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<ContentPage xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cvm="using:PostIt.ViewModels.Commands"
|
||||
xmlns:postitControls="using:PostIt.Controls"
|
||||
x:Class="PostIt.Views.Commands.RdvPage"
|
||||
x:DataType="cvm:RdvViewModel"
|
||||
Header="Commande billing">
|
||||
|
|
@ -71,16 +72,8 @@
|
|||
Content="{Binding SubmitLabel}"
|
||||
Command="{Binding SubmitCommand}"
|
||||
IsEnabled="{Binding IsSupported}" />
|
||||
<TextBox Grid.Column="2"
|
||||
Text="{Binding StatusMessage}"
|
||||
IsReadOnly="True"
|
||||
AcceptsReturn="True"
|
||||
TextWrapping="Wrap"
|
||||
BorderThickness="0"
|
||||
BorderBrush="Transparent"
|
||||
Padding="0"
|
||||
Background="Transparent"
|
||||
VerticalAlignment="Center" />
|
||||
<postitControls:StatusBar Grid.Column="2"
|
||||
DataContext="{Binding ActionStatus}" />
|
||||
<ProgressBar Grid.Column="3"
|
||||
Width="120"
|
||||
IsIndeterminate="True"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="cl:avalonia.Controls"
|
||||
xmlns:postitControls="using:PostIt.Controls"
|
||||
x:Class="PostIt.Views.SettingsPage"
|
||||
xmlns:vm="using:PostIt.ViewModels"
|
||||
x:DataType="vm:Settings"
|
||||
|
|
@ -51,9 +52,7 @@
|
|||
Opacity="0.8"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock x:Name="AvatarUploadStatusText"
|
||||
Text=""
|
||||
TextWrapping="Wrap" />
|
||||
<postitControls:StatusBar DataContext="{Binding ActionStatus}"/>
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button x:Name="ChooseAvatarButton"
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ using System.Linq;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using PostIt.Services;
|
||||
using PostIt.ViewModels;
|
||||
|
||||
namespace PostIt.Views;
|
||||
public partial class SettingsPage: ContentPage
|
||||
|
|
@ -28,12 +28,12 @@ public partial class SettingsPage: ContentPage
|
|||
{
|
||||
var statusVersion = Interlocked.Increment(ref _avatarStatusVersion);
|
||||
ChooseAvatarButton.IsEnabled = false;
|
||||
SetAvatarStatus("Sélection d'un fichier avatar...", Brushes.Gray);
|
||||
SetAvatarStatus("Selection d'un fichier avatar...", StatusSeverity.Info);
|
||||
|
||||
var topLevel = TopLevel.GetTopLevel(this);
|
||||
if (topLevel is null)
|
||||
{
|
||||
SetAvatarStatus("Impossible d'accéder à la fenêtre active.", Brushes.IndianRed);
|
||||
SetAvatarStatus("Impossible d'acceder a la fenetre active.", StatusSeverity.Error);
|
||||
ChooseAvatarButton.IsEnabled = true;
|
||||
return;
|
||||
}
|
||||
|
|
@ -54,27 +54,28 @@ public partial class SettingsPage: ContentPage
|
|||
var file = files.FirstOrDefault();
|
||||
if (file is null)
|
||||
{
|
||||
SetAvatarStatus("Upload annulé.", Brushes.Gray);
|
||||
SetAvatarStatus("Upload annule.", StatusSeverity.Warning);
|
||||
ChooseAvatarButton.IsEnabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SetAvatarStatus("Upload avatar en cours...", Brushes.Gray);
|
||||
SetAvatarStatus("Upload avatar en cours...", StatusSeverity.Info);
|
||||
|
||||
await using var stream = await file.OpenReadAsync();
|
||||
var api = ((App)App.Current!).ServiceProvider!.GetRequiredService<YavscApiClient>();
|
||||
var message = await api.SetAvatarAsync(stream, file.Name, GetMimeType(file.Name));
|
||||
SetAvatarStatus(string.IsNullOrWhiteSpace(message)
|
||||
? "Avatar mis à jour."
|
||||
: message, Brushes.ForestGreen);
|
||||
? "Avatar mis a jour."
|
||||
: message,
|
||||
StatusSeverity.Info);
|
||||
|
||||
_ = ClearSuccessStatusLaterAsync(statusVersion);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetAvatarStatus($"Échec upload avatar: {ex.Message}", Brushes.IndianRed);
|
||||
SetAvatarStatus($"Echec upload avatar: {ex.Message}", StatusSeverity.Error);
|
||||
Console.Error.WriteLine($"🩎 Avatar upload failed: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
|
|
@ -89,16 +90,15 @@ public partial class SettingsPage: ContentPage
|
|||
if (statusVersion != _avatarStatusVersion)
|
||||
return;
|
||||
|
||||
if (AvatarUploadStatusText.Foreground == Brushes.ForestGreen)
|
||||
{
|
||||
SetAvatarStatus($"Avatar prêt. Formats supportés: {AcceptedAvatarFormats}. Taille max: {MaxAvatarSizeMegabytes} MB.", Brushes.Gray);
|
||||
}
|
||||
SetAvatarStatus($"Avatar pret. Formats supportes: {AcceptedAvatarFormats}. Taille max: {MaxAvatarSizeMegabytes} MB.", StatusSeverity.Info);
|
||||
}
|
||||
|
||||
private void SetAvatarStatus(string message, IBrush color)
|
||||
private void SetAvatarStatus(string message, StatusSeverity severity)
|
||||
{
|
||||
AvatarUploadStatusText.Foreground = color;
|
||||
AvatarUploadStatusText.Text = message;
|
||||
if (DataContext is Settings settings)
|
||||
{
|
||||
settings.SetActionStatus(message, severity);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetMimeType(string fileName)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue