diff --git a/installer/PSLauncher.iss b/installer/PSLauncher.iss index 4e9d4d9..f07da43 100644 --- a/installer/PSLauncher.iss +++ b/installer/PSLauncher.iss @@ -11,7 +11,7 @@ #define MyAppName "PROSERVE Launcher" #define MyAppShortName "PSLauncher" -#define MyAppVersion "0.12.0" +#define MyAppVersion "0.13.0" #define MyAppPublisher "ASTERION VR" #define MyAppURL "https://asterionvr.com" #define MyAppExeName "PSLauncher.exe" diff --git a/src/PSLauncher.App/PSLauncher.App.csproj b/src/PSLauncher.App/PSLauncher.App.csproj index 6fb5fc6..9528405 100644 --- a/src/PSLauncher.App/PSLauncher.App.csproj +++ b/src/PSLauncher.App/PSLauncher.App.csproj @@ -15,9 +15,9 @@ PROSERVE Launcher © 2026 ASTERION VR — All rights reserved PSLauncher.App - 0.12.0 - 0.12.0.0 - 0.12.0.0 + 0.13.0 + 0.13.0.0 + 0.13.0.0 true diff --git a/src/PSLauncher.App/ViewModels/SettingsViewModel.cs b/src/PSLauncher.App/ViewModels/SettingsViewModel.cs index d14c23b..eec2f37 100644 --- a/src/PSLauncher.App/ViewModels/SettingsViewModel.cs +++ b/src/PSLauncher.App/ViewModels/SettingsViewModel.cs @@ -63,8 +63,12 @@ public sealed partial class SettingsViewModel : ObservableObject [ObservableProperty] private string _reportHtdocsRoot; [ObservableProperty] private string _reportFolderName; [ObservableProperty] private bool _reportAutoDeploy; + [ObservableProperty] private int _reportMaxBackups; [ObservableProperty] private string? _reportDeployStatus; [ObservableProperty] private bool _isDeployingReport; + public System.Collections.ObjectModel.ObservableCollection ReportBackups { get; } + = new(); + public bool HasReportBackups => ReportBackups.Count > 0; public IReadOnlyList AvailableLanguages { get; } = PSLauncher.Core.Localization.Strings.Available @@ -164,6 +168,9 @@ public sealed partial class SettingsViewModel : ObservableObject _reportHtdocsRoot = config.ReportTool.HtdocsRoot; _reportFolderName = config.ReportTool.FolderName; _reportAutoDeploy = config.ReportTool.AutoDeploy; + _reportMaxBackups = config.ReportTool.MaxBackups; + // Charge la liste de backups en arrière-plan (UI immédiate puis remplie quand prête) + _ = LoadReportBackupsAsync(); LauncherVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0"; MachineId = licenseService.GetMachineId(); @@ -287,6 +294,7 @@ public sealed partial class SettingsViewModel : ObservableObject _config.ReportTool.HtdocsRoot = ReportHtdocsRoot.Trim(); _config.ReportTool.FolderName = ReportFolderName.Trim(); _config.ReportTool.AutoDeploy = ReportAutoDeploy; + _config.ReportTool.MaxBackups = Math.Max(0, ReportMaxBackups); _configStore.Save(_config); _logger.LogInformation("Settings saved (language={Lang}, changed={Changed})", _config.Language, languageChanged); @@ -494,4 +502,76 @@ public sealed partial class SettingsViewModel : ObservableObject IsReplayingMigrations = false; } } + + // ===================== REPORT TOOL BACKUPS ===================== + + private async Task LoadReportBackupsAsync() + { + try + { + var list = await _reportDeployer.ListBackupsAsync(CancellationToken.None); + ReportBackups.Clear(); + foreach (var b in list) + ReportBackups.Add(new ReportBackupViewModel(b, RevertReportToBackupAsync)); + OnPropertyChanged(nameof(HasReportBackups)); + } + catch (Exception ex) + { + _logger.LogDebug(ex, "Could not list report backups"); + } + } + + /// Appelé par chaque ligne du tableau de backups via son own command. + private async Task RevertReportToBackupAsync(BackupInfo backup) + { + var localDate = backup.TimestampUtc.ToLocalTime(); + var dateLabel = Strings.FormatLongDate(localDate); + var confirm = MessageBox.Show( + Strings.MsgRevertReportConfirm(dateLabel), + Strings.MsgBoxRevertReport, + MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); + if (confirm != MessageBoxResult.Yes) return; + + IsDeployingReport = true; + ReportDeployStatus = $"Revert vers {dateLabel}…"; + try + { + var result = await _reportDeployer.RevertAsync(backup.FolderName, CancellationToken.None); + ReportDeployStatus = result.Status == DeployStatus.Deployed + ? Strings.MsgRevertReportSuccess(dateLabel) + : $"✗ {result.ErrorMessage}"; + await LoadReportBackupsAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Revert report failed"); + ReportDeployStatus = $"✗ {ex.Message}"; + } + finally + { + IsDeployingReport = false; + } + } +} + +/// +/// Ligne du tableau "Backups disponibles" dans Settings → Avancés → Outil Report. +/// Chaque instance porte sa propre commande Revert paramétrée pour ce backup. +/// +public sealed partial class ReportBackupViewModel : ObservableObject +{ + public BackupInfo Info { get; } + public string DateDisplay => Strings.FormatLongDate(Info.TimestampUtc.ToLocalTime()); + public string SizeDisplay => Strings.SettingsReportBackupSize(Strings.FormatSize(Info.SizeBytes)); + public string FolderName => Info.FolderName; + + private readonly Func _revertHandler; + public ReportBackupViewModel(BackupInfo info, Func revertHandler) + { + Info = info; + _revertHandler = revertHandler; + } + + [RelayCommand] + private Task Revert() => _revertHandler(Info); } diff --git a/src/PSLauncher.App/Views/SettingsDialog.xaml b/src/PSLauncher.App/Views/SettingsDialog.xaml index 6c5967c..3193531 100644 --- a/src/PSLauncher.App/Views/SettingsDialog.xaml +++ b/src/PSLauncher.App/Views/SettingsDialog.xaml @@ -444,6 +444,22 @@ Foreground="{StaticResource Brush.Text.Primary}" Content="{x:Static loc:Strings.SettingsReportAutoDeploy}" /> + + + + + + + + + + @@ -460,6 +476,52 @@ Command="{Binding RedeployReportCommand}" IsEnabled="{Binding IsDeployingReport, Converter={StaticResource InverseBoolToBool}}" /> + + + + + + + + + + + + + + + + + + + +