Utopia-Canteen-System/ViewModels/SettingsViewModel.cs

319 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using UtopiaCanteenSystem.Models;
using UtopiaCanteenSystem.Services;
namespace UtopiaCanteenSystem.ViewModels;
/// <summary>
/// ViewModel for SettingsView: scan settings, production post, and offline cache sync.
/// </summary>
public partial class SettingsViewModel : ObservableObject
{
private readonly IConfigService _configService;
private readonly INavigationService _navigation;
private readonly IAdminAuditService _adminAudit;
private readonly ISyncService _syncService;
private readonly IOfflineCacheSyncService _offlineCacheSyncService;
[ObservableProperty]
private string _syncApiEndpoint = string.Empty;
[ObservableProperty]
private string _scanIntervalDays = "0";
[ObservableProperty]
private string _scanIntervalHours = "0";
[ObservableProperty]
private string _scanIntervalMinutes = "1";
[ObservableProperty]
private string _scanIntervalSeconds = "0";
[ObservableProperty]
private string _adminCardId = "ADMIN";
[ObservableProperty]
private string _saveMessage = string.Empty;
[ObservableProperty]
private bool _isError;
[ObservableProperty]
private bool _isSaving;
[ObservableProperty]
private bool _isPosting;
[ObservableProperty]
private bool _isSyncingCache;
[ObservableProperty]
private string _lastEmployeeRfidCacheSyncDisplay = "Never";
[ObservableProperty]
private string _lastMealMenuCacheSyncDisplay = "Never";
[ObservableProperty]
private string _selectedAppMode = "Server";
[ObservableProperty]
private string _centralServerBaseUrl = string.Empty;
[ObservableProperty]
private string _localServerListenUrls = "http://0.0.0.0:5000";
public IReadOnlyList<string> AppModeOptions { get; } = new[] { "Server", "Client" };
/// <summary>Central PC: runs SQLite, sync jobs, and Kestrel API.</summary>
public bool IsCentralServerMode => string.Equals(SelectedAppMode, "Server", StringComparison.OrdinalIgnoreCase);
public bool CanPostNow => !IsPosting && IsCentralServerMode;
public string PostButtonText => IsPosting ? "Posting..." : "Post Data";
public bool CanSyncCacheNow => !IsSyncingCache && IsCentralServerMode;
public string SyncCacheButtonText => IsSyncingCache ? "Syncing cache..." : "Sync Now";
partial void OnIsPostingChanged(bool value)
{
OnPropertyChanged(nameof(PostButtonText));
OnPropertyChanged(nameof(CanPostNow));
}
partial void OnIsSyncingCacheChanged(bool value)
{
OnPropertyChanged(nameof(SyncCacheButtonText));
OnPropertyChanged(nameof(CanSyncCacheNow));
}
partial void OnSelectedAppModeChanged(string value)
{
OnPropertyChanged(nameof(IsCentralServerMode));
OnPropertyChanged(nameof(CanPostNow));
OnPropertyChanged(nameof(CanSyncCacheNow));
}
public SettingsViewModel(
IConfigService configService,
INavigationService navigation,
IAdminAuditService adminAudit,
ISyncService syncService,
IOfflineCacheSyncService offlineCacheSyncService)
{
_configService = configService;
_navigation = navigation;
_adminAudit = adminAudit;
_syncService = syncService;
_offlineCacheSyncService = offlineCacheSyncService;
LoadFromConfig();
RefreshCacheSyncTimestamps();
}
public void LoadFromConfig()
{
SyncApiEndpoint = _configService.GetSyncApiEndpoint();
ScanIntervalDays = _configService.GetScanIntervalDays().ToString();
ScanIntervalHours = _configService.GetScanIntervalHours().ToString();
ScanIntervalMinutes = _configService.GetScanIntervalMinutes().ToString();
ScanIntervalSeconds = _configService.GetScanIntervalSeconds().ToString();
var last = _adminAudit.GetLastLogin();
if (last != null && !string.IsNullOrWhiteSpace(last.EmployeeId))
AdminCardId = last.EmployeeId;
else
AdminCardId = "ADMIN";
SelectedAppMode = _configService.GetAppMode() == AppMode.Client ? "Client" : "Server";
CentralServerBaseUrl = _configService.GetCentralServerBaseUrl();
LocalServerListenUrls = _configService.GetLocalServerListenUrls();
}
public void RefreshCacheSyncTimestamps()
{
LastEmployeeRfidCacheSyncDisplay = FormatSyncTime(_configService.GetLastEmployeeRfidCacheSyncUtc());
LastMealMenuCacheSyncDisplay = FormatSyncTime(_configService.GetLastMealMenuCacheSyncUtc());
}
[RelayCommand]
private void Save()
{
IsSaving = true;
if (!int.TryParse(ScanIntervalDays, out var days) || days < 0 || days > 365)
{
SaveMessage = "Scan interval Days must be 0365.";
IsError = true;
IsSaving = false;
return;
}
if (!int.TryParse(ScanIntervalHours, out var hours) || hours < 0 || hours > 23)
{
SaveMessage = "Scan interval Hours must be 023.";
IsError = true;
IsSaving = false;
return;
}
if (!int.TryParse(ScanIntervalMinutes, out var minutes) || minutes < 0 || minutes > 59)
{
SaveMessage = "Scan interval Minutes must be 059.";
IsError = true;
IsSaving = false;
return;
}
if (!int.TryParse(ScanIntervalSeconds, out var seconds) || seconds < 0 || seconds > 59)
{
SaveMessage = "Scan interval Seconds must be 059.";
IsError = true;
IsSaving = false;
return;
}
if (days == 0 && hours == 0 && minutes == 0 && seconds == 0)
{
SaveMessage = "Scan interval cannot be zero. Use at least 1 second.";
IsError = true;
IsSaving = false;
return;
}
if (string.Equals(SelectedAppMode, "Client", StringComparison.OrdinalIgnoreCase) &&
string.IsNullOrWhiteSpace(CentralServerBaseUrl?.Trim()))
{
SaveMessage = "Client mode requires Central server base URL (e.g. http://192.168.1.10:5000).";
IsError = true;
IsSaving = false;
return;
}
_configService.SetAppMode(string.Equals(SelectedAppMode, "Client", StringComparison.OrdinalIgnoreCase)
? AppMode.Client
: AppMode.Server);
_configService.SetCentralServerBaseUrl(CentralServerBaseUrl?.Trim() ?? string.Empty);
_configService.SetLocalServerListenUrls(LocalServerListenUrls?.Trim() ?? string.Empty);
_configService.SetSyncApiEndpoint(SyncApiEndpoint?.Trim() ?? string.Empty);
_configService.SetScanIntervalDays(days);
_configService.SetScanIntervalHours(hours);
_configService.SetScanIntervalMinutes(minutes);
_configService.SetScanIntervalSeconds(seconds);
_configService.SetAdminCardId(AdminCardId?.Trim() ?? string.Empty);
SaveMessage = "Settings saved. Restart the app for App mode / server URL changes to take full effect.";
IsError = false;
IsSaving = false;
}
[RelayCommand]
private void Back()
{
_navigation.NavigateBackFromSettings(GetDashboardTimeout());
}
[RelayCommand]
private void OpenMealSchedules()
{
_navigation.NavigateToMealSchedules();
}
[RelayCommand]
private async Task SyncEmployeeAndMenuCacheNow()
{
if (IsSyncingCache)
return;
SaveMessage = "Syncing cache...";
IsError = false;
IsSyncingCache = true;
try
{
if (!IsCentralServerMode)
{
SaveMessage = "Cache sync runs only on the central server PC.";
IsError = true;
return;
}
if (string.IsNullOrWhiteSpace(_configService.GetHrmsLookupConnectionString()))
{
SaveMessage = "HRMS connection is not configured. Cannot sync offline cache.";
IsError = true;
return;
}
var result = await _offlineCacheSyncService.SyncEmployeeAndMenuCacheAsync().ConfigureAwait(true);
RefreshCacheSyncTimestamps();
if (result.Success)
{
SaveMessage = "Employee and menu cache synced successfully.";
IsError = false;
}
else
{
SaveMessage = result.ErrorMessage ?? "Cache sync failed.";
IsError = true;
}
}
catch (Exception ex)
{
Logger.Log(ex, "SettingsViewModel.SyncEmployeeAndMenuCacheNow");
SaveMessage = "Cache sync failed: " + ex.Message;
IsError = true;
}
finally
{
IsSyncingCache = false;
}
}
[RelayCommand]
private async Task PostDataNow()
{
if (IsPosting)
return;
SaveMessage = string.Empty;
IsError = false;
IsPosting = true;
try
{
if (!IsCentralServerMode)
{
SaveMessage = "Posting to production runs only on the central server PC.";
IsError = true;
return;
}
if (string.IsNullOrWhiteSpace(_configService.GetMySqlConnectionString()))
{
SaveMessage = "MySQL connection string not configured.";
IsError = true;
return;
}
await _syncService.SyncNowAsync().ConfigureAwait(false);
SaveMessage = "Posted data to production.";
IsError = false;
}
catch (Exception ex)
{
Logger.Log(ex, "SettingsViewModel.PostDataNow");
SaveMessage = "Failed to post data to production.";
IsError = true;
}
finally
{
IsPosting = false;
}
}
private static string FormatSyncTime(DateTime? utc)
{
if (utc == null)
return "Never";
return utc.Value.ToLocalTime().ToString("MM/dd/yyyy, hh:mm:ss tt");
}
private TimeSpan GetDashboardTimeout() => _configService.GetScanInterval();
}