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" />
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -358,9 +328,27 @@
+ Margin="0,0,0,12" />
-
+
+
+
+
+
+
+
+
+