using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MaksIT.UScheduler.ScheduleManager.Models;
using MaksIT.UScheduler.ScheduleManager.Services;
using MaksIT.UScheduler.Shared;
using MaksIT.UScheduler.Shared.Helpers;
namespace MaksIT.UScheduler.ScheduleManager.ViewModels;
public partial class MainViewModel : ObservableObject
{
///
/// Returns true if the current process is running as administrator.
///
public bool IsAdmin
{
get
{
try
{
var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
var principal = new System.Security.Principal.WindowsPrincipal(identity);
return principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator);
}
catch
{
return false;
}
}
}
private readonly ScriptSettingsService _scriptSettingsService;
private readonly UISettingsService _uiSettingsService;
private readonly AppSettingsService _appSettingsService;
private readonly ScriptStatusService _scriptStatusService;
private readonly LogViewerService _logViewerService;
private WindowsServiceManager _serviceManager;
private Configuration? _serviceConfig;
private ScriptSchedule? _originalSchedule;
public MainViewModel()
{
_scriptSettingsService = new ScriptSettingsService();
_uiSettingsService = new UISettingsService();
_appSettingsService = new AppSettingsService();
_scriptStatusService = new ScriptStatusService();
_logViewerService = new LogViewerService();
_serviceManager = new WindowsServiceManager();
// Initialize month/weekday options
MonthOptions = [
new MonthOption("January"), new MonthOption("February"), new MonthOption("March"),
new MonthOption("April"), new MonthOption("May"), new MonthOption("June"),
new MonthOption("July"), new MonthOption("August"), new MonthOption("September"),
new MonthOption("October"), new MonthOption("November"), new MonthOption("December")
];
WeekdayOptions = [
new WeekdayOption("Monday"), new WeekdayOption("Tuesday"), new WeekdayOption("Wednesday"),
new WeekdayOption("Thursday"), new WeekdayOption("Friday"), new WeekdayOption("Saturday"),
new WeekdayOption("Sunday")
];
// Load UI settings first (stored paths)
LoadUISettings();
// Initial load
RefreshScripts();
RefreshServiceStatus();
RefreshServiceLogs();
}
///
/// Loads UI settings from the ScheduleManager's appsettings.json.
/// If paths are empty, tries auto-detection.
///
private void LoadUISettings()
{
var uiSettings = _uiSettingsService.Load();
// Use stored bin path - no auto-detection, user will configure manually if empty
ServiceBinPath = uiSettings.ServiceBinPath ?? string.Empty;
// Load the UScheduler's appsettings.json to get service config
LoadServiceAppSettings();
}
///
/// Gets the resolved service bin path (absolute path).
///
private string ResolvedServiceBinPath => UISettingsService.ResolvePath(ServiceBinPath);
///
/// Gets the resolved appsettings.json path from the service bin folder.
///
private string ResolvedAppSettingsPath => string.IsNullOrEmpty(ResolvedServiceBinPath)
? string.Empty
: Path.Combine(ResolvedServiceBinPath, "appsettings.json");
///
/// Gets the resolved executable path from the service bin folder.
///
private string ResolvedExecutablePath => string.IsNullOrEmpty(ResolvedServiceBinPath)
? string.Empty
: Path.Combine(ResolvedServiceBinPath, "MaksIT.UScheduler.exe");
///
/// Loads the UScheduler's appsettings.json from the path in ScheduleManager appsettings (ServiceBinPath).
/// LogDir is read from that file and evaluated against the UScheduler program path (ServiceBinPath).
///
private void LoadServiceAppSettings()
{
// Path to UScheduler appsettings = path from ScheduleManager appsettings (ServiceBinPath) + appsettings.json
var uschedulerAppSettingsPath = ResolvedAppSettingsPath;
if (!string.IsNullOrEmpty(uschedulerAppSettingsPath) && File.Exists(uschedulerAppSettingsPath))
{
_serviceConfig = _appSettingsService.LoadAppSettings(uschedulerAppSettingsPath);
}
else
{
_serviceConfig = null;
}
if (_serviceConfig != null)
{
AppSettingsLoadError = null;
// Update service manager with correct service name
_serviceManager = new WindowsServiceManager(_serviceConfig.ServiceName);
ServiceName = _serviceConfig.ServiceName;
// Log dir: retrieve from MaksIT.UScheduler appsettings.json, show relative path in UI (e.g. ".\Logs")
// Configuration.LogDir is required, so it is always set when _serviceConfig is loaded.
LogDirectory = AppSettingsService.GetLogDirFromAppSettingsFile(uschedulerAppSettingsPath)
?? _serviceConfig.LogDir;
// Refresh process list for Processes Management tab
ProcessList.Clear();
foreach (var p in _serviceConfig.Processes)
ProcessList.Add(p);
}
else
{
// appsettings.json not loaded — surface error; do not use fake defaults
AppSettingsLoadError = string.IsNullOrEmpty(uschedulerAppSettingsPath) || !File.Exists(uschedulerAppSettingsPath)
? "Service Bin Path must be set and must point to a folder that contains appsettings.json."
: "Failed to load appsettings.json. Ensure the file is valid and contains required settings (e.g. LogDir).";
ServiceName = string.Empty;
LogDirectory = string.Empty;
ProcessList.Clear();
}
}
///
/// Saves the current paths to the UI's appsettings.json.
///
private void SaveUISettings()
{
var settings = new UISettings
{
ServiceBinPath = ServiceBinPath
};
_uiSettingsService.Save(settings);
}
#region AppSettings
[ObservableProperty]
private string _serviceBinPath = string.Empty;
[ObservableProperty]
private string _serviceName = "MaksIT.UScheduler";
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(AppSettingsLoadErrorVisibility))]
private string? _appSettingsLoadError;
///
/// Visibility for the app settings load error message (Visible when is set).
///
public Visibility AppSettingsLoadErrorVisibility =>
string.IsNullOrEmpty(AppSettingsLoadError) ? Visibility.Collapsed : Visibility.Visible;
[RelayCommand]
private void BrowseServiceBinPath()
{
var dialog = new Microsoft.Win32.OpenFolderDialog
{
Title = "Select MaksIT.UScheduler bin folder"
};
if (dialog.ShowDialog() == true)
{
ServiceBinPath = dialog.FolderName;
SaveUISettings();
LoadServiceAppSettings();
RefreshScripts();
RefreshServiceStatus();
RefreshServiceLogs();
}
}
[RelayCommand]
private void ReloadAppSettings()
{
LoadServiceAppSettings();
RefreshScripts();
RefreshServiceStatus();
RefreshServiceLogs();
}
[RelayCommand]
private void SavePaths()
{
SaveUISettings();
MessageBox.Show("Paths saved successfully.", "Save", MessageBoxButton.OK, MessageBoxImage.Information);
}
#endregion
#region Service Management
[ObservableProperty]
private ServiceStatus _serviceStatus = ServiceStatus.Unknown;
[ObservableProperty]
private string _serviceStatusText = "Unknown";
[ObservableProperty]
private string _serviceStatusColor = "Gray";
[RelayCommand]
private void RefreshServiceStatus()
{
ServiceStatus = _serviceManager.GetStatus(ResolvedExecutablePath);
(ServiceStatusText, ServiceStatusColor) = GetServiceStatusDisplay(ServiceStatus);
}
[RelayCommand]
private void StartService()
{
var result = _serviceManager.Start(ResolvedExecutablePath);
if (!result.Success)
{
MessageBox.Show(result.Message, "Start Service", MessageBoxButton.OK, MessageBoxImage.Warning);
}
RefreshServiceStatus();
}
[RelayCommand]
private void StopService()
{
var result = _serviceManager.Stop(ResolvedExecutablePath);
if (!result.Success)
{
MessageBox.Show(result.Message, "Stop Service", MessageBoxButton.OK, MessageBoxImage.Warning);
}
RefreshServiceStatus();
}
[RelayCommand]
private void RegisterService()
{
if (string.IsNullOrWhiteSpace(ServiceBinPath))
{
MessageBox.Show("Please specify the path to MaksIT.UScheduler bin folder", "Register Service",
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var result = _serviceManager.Register(ResolvedExecutablePath);
MessageBox.Show(result.Message, "Register Service",
MessageBoxButton.OK, result.Success ? MessageBoxImage.Information : MessageBoxImage.Warning);
RefreshServiceStatus();
}
[RelayCommand]
private void UnregisterService()
{
var confirm = MessageBox.Show(
"Are you sure you want to unregister the service?",
"Unregister Service",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (confirm != MessageBoxResult.Yes)
return;
var result = _serviceManager.Unregister(ResolvedExecutablePath);
MessageBox.Show(result.Message, "Unregister Service",
MessageBoxButton.OK, result.Success ? MessageBoxImage.Information : MessageBoxImage.Warning);
RefreshServiceStatus();
}
private static (string statusText, string statusColor) GetServiceStatusDisplay(ServiceStatus status)
{
return status switch
{
ServiceStatus.Running => ("Running", "Green"),
ServiceStatus.Stopped => ("Stopped", "Red"),
ServiceStatus.StartPending => ("Starting...", "Orange"),
ServiceStatus.StopPending => ("Stopping...", "Orange"),
ServiceStatus.Paused => ("Paused", "Orange"),
ServiceStatus.NotInstalled => ("Not Installed", "Gray"),
_ => ("Unknown", "Gray")
};
}
#endregion
#region Schedule Management
[ObservableProperty]
private ObservableCollection _scripts = [];
///
/// Process entries from service config (for Processes Management tab).
///
[ObservableProperty]
private ObservableCollection _processList = [];
[ObservableProperty]
private ScriptSchedule? _selectedScript;
[ObservableProperty]
private bool _hasSelectedScript;
/// Require script to be digitally signed (from service config).
[ObservableProperty]
private bool _scriptIsSigned = true;
/// Script disabled and will not be executed (from service config).
[ObservableProperty]
private bool _scriptDisabled;
/// Display name for the selected script (from service config; editable).
[ObservableProperty]
private string? _scriptName;
private bool _isSyncingSelection;
[ObservableProperty]
private bool _isDirty;
[ObservableProperty]
private ObservableCollection _monthOptions = [];
[ObservableProperty]
private ObservableCollection _weekdayOptions = [];
[ObservableProperty]
private ObservableCollection _runTimes = [];
[ObservableProperty]
private string? _selectedRunTime;
[ObservableProperty]
private string _newRunTime = "00:00";
[ObservableProperty]
private int _minIntervalMinutes = 10;
partial void OnSelectedScriptChanged(ScriptSchedule? value)
{
HasSelectedScript = value != null;
LaunchScriptCommand.NotifyCanExecuteChanged();
_isSyncingSelection = true;
ScriptIsSigned = value?.IsSigned ?? true;
ScriptDisabled = value?.Disabled ?? false;
ScriptName = value?.Name ?? string.Empty;
_isSyncingSelection = false;
if (value != null)
{
LoadScheduleToUI(value);
_originalSchedule = CloneSchedule(value);
RefreshScriptStatus();
}
else
{
ClearUI();
_originalSchedule = null;
CurrentScriptStatus = null;
}
IsDirty = false;
}
partial void OnScriptIsSignedChanged(bool value)
{
if (_isSyncingSelection || SelectedScript == null)
return;
SelectedScript.IsSigned = value;
UpdateServiceConfigScriptEntry(SelectedScript.ConfigScriptPath, SelectedScript.Name, value, SelectedScript.Disabled);
}
partial void OnScriptDisabledChanged(bool value)
{
if (_isSyncingSelection || SelectedScript == null)
return;
SelectedScript.Disabled = value;
UpdateServiceConfigScriptEntry(SelectedScript.ConfigScriptPath, SelectedScript.Name, SelectedScript.IsSigned, value);
}
partial void OnScriptNameChanged(string? value)
{
if (_isSyncingSelection || SelectedScript == null)
return;
var name = value ?? string.Empty;
SelectedScript.Name = name;
UpdateServiceConfigScriptEntry(SelectedScript.ConfigScriptPath, name, SelectedScript.IsSigned, SelectedScript.Disabled);
}
private void UpdateServiceConfigScriptEntry(string configScriptPath, string? name, bool isSigned, bool disabled)
{
if (_serviceConfig == null)
return;
var entry = _serviceConfig.Powershell.FirstOrDefault(p => p.Path == configScriptPath);
if (entry != null)
{
if (name != null)
entry.Name = name;
entry.IsSigned = isSigned;
entry.Disabled = disabled;
}
_appSettingsService.UpdatePowershellScriptEntry(configScriptPath, name, isSigned, disabled);
}
partial void OnMinIntervalMinutesChanged(int value)
{
MarkDirty();
}
[RelayCommand]
private void RefreshScripts()
{
Scripts.Clear();
// Load scripts from service configuration (MaksIT.UScheduler appsettings.json)
if (_serviceConfig != null)
{
foreach (var psConfig in _serviceConfig.Powershell)
{
if (string.IsNullOrEmpty(psConfig.Path))
continue;
// Resolve relative path against service directory
var resolvedScriptPath = _appSettingsService.ResolvePathRelativeToService(psConfig.Path);
var scriptDir = Path.GetDirectoryName(resolvedScriptPath);
if (string.IsNullOrEmpty(scriptDir))
continue;
// scriptsettings.json should be in the same folder as the script
var settingsPath = Path.Combine(scriptDir, "scriptsettings.json");
if (File.Exists(settingsPath))
{
var schedule = _scriptSettingsService.LoadScheduleFromFile(settingsPath);
if (schedule != null)
{
schedule.ConfigScriptPath = psConfig.Path;
if (!string.IsNullOrWhiteSpace(psConfig.Name))
schedule.Name = psConfig.Name;
schedule.IsSigned = psConfig.IsSigned;
schedule.Disabled = psConfig.Disabled;
Scripts.Add(schedule);
}
}
}
}
}
///
/// Launches the selected script via its .bat file in a separate window.
///
[RelayCommand(CanExecute = nameof(CanLaunchScript))]
private void LaunchScript()
{
if (SelectedScript == null)
return;
var scriptDir = Path.GetDirectoryName(SelectedScript.FilePath);
if (string.IsNullOrEmpty(scriptDir) || !Directory.Exists(scriptDir))
{
MessageBox.Show("Script directory not found.", "Launch Script",
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var batFiles = Directory.GetFiles(scriptDir, "*.bat");
if (batFiles.Length == 0)
{
MessageBox.Show($"No .bat file found in script folder.\n{scriptDir}", "Launch Script",
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
try
{
var batPath = batFiles[0];
var startInfo = new ProcessStartInfo
{
FileName = batPath,
WorkingDirectory = scriptDir,
UseShellExecute = true,
CreateNoWindow = false
};
Process.Start(startInfo);
}
catch (Exception ex)
{
MessageBox.Show($"Failed to launch script: {ex.Message}", "Launch Script",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private bool CanLaunchScript() => SelectedScript != null;
[RelayCommand]
private void AddRunTime()
{
if (string.IsNullOrWhiteSpace(NewRunTime))
return;
// Validate time format
if (!TimeOnly.TryParse(NewRunTime, out var time))
{
MessageBox.Show("Invalid time format. Please use HH:mm format.", "Invalid Time",
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var formattedTime = time.ToString("HH:mm");
if (!RunTimes.Contains(formattedTime))
{
RunTimes.Add(formattedTime);
MarkDirty();
}
NewRunTime = "00:00";
}
[RelayCommand]
private void RemoveRunTime()
{
if (SelectedRunTime != null)
{
RunTimes.Remove(SelectedRunTime);
MarkDirty();
}
}
[RelayCommand]
private void SaveSchedule()
{
if (SelectedScript == null)
return;
try
{
// Update the selected script from UI
UpdateScheduleFromUI(SelectedScript);
// Save to file
_scriptSettingsService.SaveScheduleToFile(SelectedScript);
// Update original for dirty tracking
_originalSchedule = CloneSchedule(SelectedScript);
IsDirty = false;
MessageBox.Show("Schedule saved successfully.", "Save",
MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Failed to save schedule: {ex.Message}", "Save Error",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
[RelayCommand]
private void RevertSchedule()
{
if (_originalSchedule != null && SelectedScript != null)
{
// Copy original values back
SelectedScript.RunMonth = new List(_originalSchedule.RunMonth);
SelectedScript.RunWeekday = new List(_originalSchedule.RunWeekday);
SelectedScript.RunTime = new List(_originalSchedule.RunTime);
SelectedScript.MinIntervalMinutes = _originalSchedule.MinIntervalMinutes;
LoadScheduleToUI(SelectedScript);
IsDirty = false;
}
}
public void OnMonthCheckedChanged()
{
MarkDirty();
}
public void OnWeekdayCheckedChanged()
{
MarkDirty();
}
private void LoadScheduleToUI(ScriptSchedule schedule)
{
// Load months
foreach (var month in MonthOptions)
{
month.IsChecked = schedule.RunMonth.Contains(month.Name);
}
// Load weekdays
foreach (var weekday in WeekdayOptions)
{
weekday.IsChecked = schedule.RunWeekday.Contains(weekday.Name);
}
// Load times
RunTimes = new ObservableCollection(schedule.RunTime);
// Load interval
MinIntervalMinutes = schedule.MinIntervalMinutes;
}
private void UpdateScheduleFromUI(ScriptSchedule schedule)
{
schedule.RunMonth = MonthOptions.Where(m => m.IsChecked).Select(m => m.Name).ToList();
schedule.RunWeekday = WeekdayOptions.Where(w => w.IsChecked).Select(w => w.Name).ToList();
schedule.RunTime = RunTimes.ToList();
schedule.MinIntervalMinutes = MinIntervalMinutes;
}
private void ClearUI()
{
foreach (var month in MonthOptions) month.IsChecked = false;
foreach (var weekday in WeekdayOptions) weekday.IsChecked = false;
RunTimes.Clear();
MinIntervalMinutes = 10;
}
private void MarkDirty()
{
if (SelectedScript != null)
{
IsDirty = true;
}
}
private static ScriptSchedule CloneSchedule(ScriptSchedule source)
{
return new ScriptSchedule
{
FilePath = source.FilePath,
Name = source.Name,
Title = source.Title,
ConfigScriptPath = source.ConfigScriptPath,
IsSigned = source.IsSigned,
Disabled = source.Disabled,
RunMonth = new List(source.RunMonth),
RunWeekday = new List(source.RunWeekday),
RunTime = new List(source.RunTime),
MinIntervalMinutes = source.MinIntervalMinutes
};
}
#endregion
#region Script Status (Lock File, Last Run)
[ObservableProperty]
private ScriptStatus? _currentScriptStatus;
[ObservableProperty]
private bool _hasLockFile;
[RelayCommand]
private void RefreshScriptStatus()
{
if (SelectedScript == null)
{
CurrentScriptStatus = null;
HasLockFile = false;
return;
}
CurrentScriptStatus = _scriptStatusService.GetScriptStatus(SelectedScript.FilePath);
HasLockFile = CurrentScriptStatus?.HasLockFile ?? false;
}
[RelayCommand]
private void RemoveLockFile()
{
if (CurrentScriptStatus == null || !CurrentScriptStatus.HasLockFile)
{
MessageBox.Show("No lock file exists.", "Remove Lock File",
MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var confirm = MessageBox.Show(
"Are you sure you want to remove the lock file?\n\n" +
"This should only be done if the script crashed or was interrupted.\n" +
"Removing the lock while the script is running may cause issues.",
"Remove Lock File",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
if (confirm != MessageBoxResult.Yes)
return;
if (_scriptStatusService.RemoveLockFile(CurrentScriptStatus.LockFilePath))
{
MessageBox.Show("Lock file removed successfully.", "Remove Lock File",
MessageBoxButton.OK, MessageBoxImage.Information);
RefreshScriptStatus();
}
else
{
MessageBox.Show("Failed to remove lock file.", "Remove Lock File",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion
#region Log Viewer
[ObservableProperty]
private string _logDirectory = string.Empty;
[ObservableProperty]
private ObservableCollection _serviceLogs = [];
/// Script log folder names (e.g. file-sync, hyper-v-backup) under LogDir.
[ObservableProperty]
private ObservableCollection _scriptLogFolders = [];
[ObservableProperty]
private string? _selectedScriptLogFolder;
[ObservableProperty]
private ObservableCollection _scriptLogFiles = [];
[ObservableProperty]
private LogFileInfo? _selectedLogFile;
[ObservableProperty]
private string _logContent = string.Empty;
[ObservableProperty]
private int _selectedLogTab;
partial void OnSelectedScriptLogFolderChanged(string? value)
{
ScriptLogFiles.Clear();
SelectedLogFile = null;
if (string.IsNullOrEmpty(value))
return;
var resolvedLogDir = ResolvedLogDirectory;
if (string.IsNullOrEmpty(resolvedLogDir))
return;
var logs = _logViewerService.GetScriptLogs(resolvedLogDir, value);
foreach (var log in logs)
{
ScriptLogFiles.Add(log);
}
}
partial void OnSelectedLogFileChanged(LogFileInfo? value)
{
if (value != null)
{
LoadLogContent(value.FullPath);
}
else
{
LogContent = string.Empty;
}
}
[RelayCommand]
private void BrowseLogDirectory()
{
var dialog = new Microsoft.Win32.OpenFolderDialog
{
Title = "Select Log Directory"
};
if (dialog.ShowDialog() == true)
{
LogDirectory = dialog.FolderName;
RefreshServiceLogs();
RefreshScriptLogs();
}
}
///
/// Gets the resolved log directory path for reading logs. LogDir (e.g. .\logs) is always resolved
/// against ServiceBinPath so the result is e.g. ..\..\..\..\MaksIT.UScheduler\bin\Debug\net10.0\win-x64\logs.
///
private string ResolvedLogDirectory
{
get
{
if (string.IsNullOrEmpty(ResolvedServiceBinPath))
return string.Empty;
// Configuration.LogDir is required; fallback only when config is not loaded.
var logDir = _serviceConfig != null ? _serviceConfig.LogDir : ".\\logs";
return PathHelper.ResolvePath(logDir, ResolvedServiceBinPath);
}
}
[RelayCommand]
private void RefreshServiceLogs()
{
ServiceLogs.Clear();
var resolvedLogDir = ResolvedLogDirectory;
if (string.IsNullOrEmpty(resolvedLogDir))
return;
var logs = _logViewerService.GetServiceLogs(resolvedLogDir);
foreach (var log in logs)
{
ServiceLogs.Add(log);
}
// Also refresh script logs so the Script Logs tab stays in sync
RefreshScriptLogs();
}
[RelayCommand]
private void RefreshScriptLogs()
{
ScriptLogFolders.Clear();
SelectedScriptLogFolder = null;
ScriptLogFiles.Clear();
SelectedLogFile = null;
var resolvedLogDir = ResolvedLogDirectory;
if (string.IsNullOrEmpty(resolvedLogDir))
return;
var folderNames = _logViewerService.GetScriptLogFolderNames(resolvedLogDir);
foreach (var name in folderNames)
{
ScriptLogFolders.Add(name);
}
if (ScriptLogFolders.Count > 0)
{
SelectedScriptLogFolder = ScriptLogFolders[0];
}
}
[RelayCommand]
private void RefreshLogContent()
{
if (SelectedLogFile != null)
{
LoadLogContent(SelectedLogFile.FullPath);
}
}
[RelayCommand]
private void OpenLogInExplorer()
{
if (SelectedLogFile == null)
return;
try
{
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{SelectedLogFile.FullPath}\"");
}
catch (Exception ex)
{
MessageBox.Show($"Failed to open explorer: {ex.Message}", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
[RelayCommand]
private void OpenLogDirectory()
{
var resolvedLogDir = ResolvedLogDirectory;
if (string.IsNullOrEmpty(resolvedLogDir) || !Directory.Exists(resolvedLogDir))
{
MessageBox.Show("Log directory not found.", "Error",
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
try
{
System.Diagnostics.Process.Start("explorer.exe", resolvedLogDir);
}
catch (Exception ex)
{
MessageBox.Show($"Failed to open explorer: {ex.Message}", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void LoadLogContent(string filePath)
{
LogContent = _logViewerService.ReadLogFile(filePath);
}
#endregion
}
///
/// Represents a month checkbox option.
///
public partial class MonthOption : ObservableObject
{
public string Name { get; }
[ObservableProperty]
private bool _isChecked;
public MonthOption(string name)
{
Name = name;
}
}
///
/// Represents a weekday checkbox option.
///
public partial class WeekdayOption : ObservableObject
{
public string Name { get; }
[ObservableProperty]
private bool _isChecked;
public WeekdayOption(string name)
{
Name = name;
}
}