diff --git a/ViewModels/SettingsViewModel.cs b/ViewModels/SettingsViewModel.cs index 9881761..d36644b 100644 --- a/ViewModels/SettingsViewModel.cs +++ b/ViewModels/SettingsViewModel.cs @@ -6,15 +6,14 @@ using UtopiaCanteenSystem.Services; namespace UtopiaCanteenSystem.ViewModels; /// -/// ViewModel for SettingsView: scan settings, production post, and offline cache sync. +/// Settings UI (frontend): persists local kiosk config; cache/production jobs call backend APIs only. /// 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; + private readonly ICanteenBackendApiClient _backendApi; [ObservableProperty] private string _syncApiEndpoint = string.Empty; @@ -59,20 +58,26 @@ public partial class SettingsViewModel : ObservableObject private string _selectedAppMode = "Server"; [ObservableProperty] - private string _centralServerBaseUrl = string.Empty; + private string _backendBaseUrl = string.Empty; [ObservableProperty] private string _localServerListenUrls = "http://0.0.0.0:5000"; public IReadOnlyList AppModeOptions { get; } = new[] { "Server", "Client" }; - /// Central PC: runs SQLite, sync jobs, and Kestrel API. public bool IsCentralServerMode => string.Equals(SelectedAppMode, "Server", StringComparison.OrdinalIgnoreCase); - public bool CanPostNow => !IsPosting && IsCentralServerMode; - public string PostButtonText => IsPosting ? "Posting..." : "Post Data"; + public string EffectiveBackendUrlDisplay => + IsCentralServerMode + ? _configService.GetBackendBaseUrl() + : (BackendBaseUrl?.Trim() ?? string.Empty); - public bool CanSyncCacheNow => !IsSyncingCache && IsCentralServerMode; + public bool HasBackendUrl => !string.IsNullOrWhiteSpace(_configService.GetBackendBaseUrl()); + + public bool CanPostNow => !IsPosting && HasBackendUrl; + public string PostButtonText => IsPosting ? "Posting..." : "Post Pending Orders"; + + public bool CanSyncCacheNow => !IsSyncingCache && HasBackendUrl; public string SyncCacheButtonText => IsSyncingCache ? "Syncing cache..." : "Sync Now"; partial void OnIsPostingChanged(bool value) @@ -90,6 +95,14 @@ public partial class SettingsViewModel : ObservableObject partial void OnSelectedAppModeChanged(string value) { OnPropertyChanged(nameof(IsCentralServerMode)); + OnPropertyChanged(nameof(EffectiveBackendUrlDisplay)); + OnPropertyChanged(nameof(CanPostNow)); + OnPropertyChanged(nameof(CanSyncCacheNow)); + } + + partial void OnBackendBaseUrlChanged(string value) + { + OnPropertyChanged(nameof(HasBackendUrl)); OnPropertyChanged(nameof(CanPostNow)); OnPropertyChanged(nameof(CanSyncCacheNow)); } @@ -98,16 +111,14 @@ public partial class SettingsViewModel : ObservableObject IConfigService configService, INavigationService navigation, IAdminAuditService adminAudit, - ISyncService syncService, - IOfflineCacheSyncService offlineCacheSyncService) + ICanteenBackendApiClient backendApi) { _configService = configService; _navigation = navigation; _adminAudit = adminAudit; - _syncService = syncService; - _offlineCacheSyncService = offlineCacheSyncService; + _backendApi = backendApi; LoadFromConfig(); - RefreshCacheSyncTimestamps(); + _ = RefreshCacheSyncTimestampsAsync(); } public void LoadFromConfig() @@ -118,21 +129,37 @@ public partial class SettingsViewModel : ObservableObject 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"; + AdminCardId = AdminLoginHelper.ResolveAdminCardIdForDisplay(_configService, adminAudit: _adminAudit); SelectedAppMode = _configService.GetAppMode() == AppMode.Client ? "Client" : "Server"; - CentralServerBaseUrl = _configService.GetCentralServerBaseUrl(); + BackendBaseUrl = string.IsNullOrWhiteSpace(_configService.GetCentralServerBaseUrl()) + ? _configService.GetBackendBaseUrl() + : _configService.GetCentralServerBaseUrl(); LocalServerListenUrls = _configService.GetLocalServerListenUrls(); + OnPropertyChanged(nameof(EffectiveBackendUrlDisplay)); + OnPropertyChanged(nameof(HasBackendUrl)); + } + + public async Task RefreshCacheSyncTimestampsAsync() + { + if (IsCentralServerMode) + { + LastEmployeeRfidCacheSyncDisplay = FormatSyncTime(_configService.GetLastEmployeeRfidCacheSyncUtc()); + LastMealMenuCacheSyncDisplay = FormatSyncTime(_configService.GetLastMealMenuCacheSyncUtc()); + return; + } + + var status = await _backendApi.GetCacheStatusAsync().ConfigureAwait(true); + if (status != null) + { + LastEmployeeRfidCacheSyncDisplay = FormatSyncTime(status.LastEmployeeRfidCacheSyncUtc); + LastMealMenuCacheSyncDisplay = FormatSyncTime(status.LastMealMenuCacheSyncUtc); + } } public void RefreshCacheSyncTimestamps() { - LastEmployeeRfidCacheSyncDisplay = FormatSyncTime(_configService.GetLastEmployeeRfidCacheSyncUtc()); - LastMealMenuCacheSyncDisplay = FormatSyncTime(_configService.GetLastMealMenuCacheSyncUtc()); + _ = RefreshCacheSyncTimestampsAsync(); } [RelayCommand] @@ -176,9 +203,9 @@ public partial class SettingsViewModel : ObservableObject } if (string.Equals(SelectedAppMode, "Client", StringComparison.OrdinalIgnoreCase) && - string.IsNullOrWhiteSpace(CentralServerBaseUrl?.Trim())) + string.IsNullOrWhiteSpace(BackendBaseUrl?.Trim())) { - SaveMessage = "Client mode requires Central server base URL (e.g. http://192.168.1.10:5000)."; + SaveMessage = "Client mode requires Backend base URL (e.g. http://192.168.1.10:5000)."; IsError = true; IsSaving = false; return; @@ -187,7 +214,7 @@ public partial class SettingsViewModel : ObservableObject _configService.SetAppMode(string.Equals(SelectedAppMode, "Client", StringComparison.OrdinalIgnoreCase) ? AppMode.Client : AppMode.Server); - _configService.SetCentralServerBaseUrl(CentralServerBaseUrl?.Trim() ?? string.Empty); + _configService.SetCentralServerBaseUrl(BackendBaseUrl?.Trim() ?? string.Empty); _configService.SetLocalServerListenUrls(LocalServerListenUrls?.Trim() ?? string.Empty); _configService.SetSyncApiEndpoint(SyncApiEndpoint?.Trim() ?? string.Empty); @@ -196,9 +223,11 @@ public partial class SettingsViewModel : ObservableObject _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."; + SaveMessage = "Settings saved. Restart the app after changing App mode or listen URLs."; IsError = false; IsSaving = false; + OnPropertyChanged(nameof(EffectiveBackendUrlDisplay)); + OnPropertyChanged(nameof(HasBackendUrl)); } [RelayCommand] @@ -225,33 +254,27 @@ public partial class SettingsViewModel : ObservableObject try { - if (!IsCentralServerMode) + if (string.IsNullOrWhiteSpace(_configService.GetBackendBaseUrl())) { - SaveMessage = "Cache sync runs only on the central server PC."; + SaveMessage = "Backend URL is not configured."; IsError = true; return; } - if (string.IsNullOrWhiteSpace(_configService.GetHrmsLookupConnectionString())) + if (!await _backendApi.HealthCheckAsync().ConfigureAwait(true)) { - SaveMessage = "HRMS connection is not configured. Cannot sync offline cache."; + SaveMessage = "Cannot reach backend. Ensure the central server app is running and API is listening."; IsError = true; return; } - var result = await _offlineCacheSyncService.SyncEmployeeAndMenuCacheAsync().ConfigureAwait(true); - RefreshCacheSyncTimestamps(); + var result = await _backendApi.SyncCacheNowAsync().ConfigureAwait(true); + await RefreshCacheSyncTimestampsAsync().ConfigureAwait(true); - if (result.Success) - { - SaveMessage = "Employee and menu cache synced successfully."; - IsError = false; - } - else - { - SaveMessage = result.ErrorMessage ?? "Cache sync failed."; - IsError = true; - } + SaveMessage = result.Message; + IsError = !result.Success; + if (!string.IsNullOrWhiteSpace(result.Details) && result.Success) + SaveMessage += " " + result.Details; } catch (Exception ex) { @@ -277,28 +300,30 @@ public partial class SettingsViewModel : ObservableObject try { - if (!IsCentralServerMode) + if (string.IsNullOrWhiteSpace(_configService.GetBackendBaseUrl())) { - SaveMessage = "Posting to production runs only on the central server PC."; + SaveMessage = "Backend URL is not configured."; IsError = true; return; } - if (string.IsNullOrWhiteSpace(_configService.GetMySqlConnectionString())) + if (!await _backendApi.HealthCheckAsync().ConfigureAwait(true)) { - SaveMessage = "MySQL connection string not configured."; + SaveMessage = "Cannot reach backend. Ensure the central server app is running."; IsError = true; return; } - await _syncService.SyncNowAsync().ConfigureAwait(false); - SaveMessage = "Posted data to production."; - IsError = false; + var result = await _backendApi.SyncOrdersNowAsync().ConfigureAwait(true); + SaveMessage = result.Message; + IsError = !result.Success; + if (!string.IsNullOrWhiteSpace(result.Details) && result.Success) + SaveMessage += " " + result.Details; } catch (Exception ex) { Logger.Log(ex, "SettingsViewModel.PostDataNow"); - SaveMessage = "Failed to post data to production."; + SaveMessage = "Failed to post pending orders: " + ex.Message; IsError = true; } finally diff --git a/Views/SettingsView.xaml b/Views/SettingsView.xaml index 41571b4..47d7775 100644 --- a/Views/SettingsView.xaml +++ b/Views/SettingsView.xaml @@ -286,10 +286,14 @@ Margin="0,0,0,12" Padding="12,8"/> - - + - + @@ -302,43 +306,9 @@ Foreground="{StaticResource MutedText}" Margin="0,0,0,20" /> - - - + - - - - - - - - - - -