Persist admin login defaults consistently

Adds shared admin login post-processing to save the admin employee id and site defaults, and updates login flows to use the helper.
feature/centralized-offline-canteen
SYED MUSTUFA AHMED NAQVI 2026-05-25 16:22:35 +05:00
parent 8123d67121
commit 659c162c93
4 changed files with 120 additions and 23 deletions

View File

@ -32,9 +32,10 @@ public class AdminAuditService : IAdminAuditService
db.AdminLoginRecords.Add(record);
await db.SaveChangesAsync().ConfigureAwait(false);
}
catch
catch (Exception ex)
{
// Audit failures should never block login; ignore errors.
// Audit failures should never block login.
Logger.Log(ex, "AdminAuditService.RecordLoginAsync");
}
}
@ -50,8 +51,9 @@ public class AdminAuditService : IAdminAuditService
.OrderByDescending(x => x.LoginTimeUtc)
.FirstOrDefault();
}
catch
catch (Exception ex)
{
Logger.Log(ex, "AdminAuditService.GetLastLogin");
return null;
}
}

View File

@ -0,0 +1,97 @@
namespace UtopiaCanteenSystem.Services;
/// <summary>
/// Shared admin login post-processing: persist employee id and site from session/backend.
/// </summary>
public static class AdminLoginHelper
{
/// <summary>
/// Admin Access field: persisted <see cref="IConfigService.GetAdminCardId"/> first,
/// then current session, then SQLite audit, then "ADMIN".
/// </summary>
public static string ResolveAdminCardIdForDisplay(
IConfigService config,
AppSession? session = null,
IAdminAuditService? adminAudit = null)
{
var fromConfig = (config.GetAdminCardId() ?? string.Empty).Trim();
if (!string.IsNullOrWhiteSpace(fromConfig))
return fromConfig;
if (session != null && !string.IsNullOrWhiteSpace(session.AdminEmployeeId))
return session.AdminEmployeeId.Trim();
var last = adminAudit?.GetLastLogin();
if (last != null && !string.IsNullOrWhiteSpace(last.EmployeeId))
return last.EmployeeId.Trim();
return "ADMIN";
}
public static async Task ApplyAdminLoginDefaultsAsync(
string employeeId,
IConfigService config,
IEmployeeLookupService employeeLookup,
ICanteenBackendApiClient? backendApi = null,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(employeeId))
return;
config.SetAdminCardId(employeeId.Trim());
string? siteId = null;
try
{
siteId = await employeeLookup.GetLocationSiteIdByEmployeeSerialAsync(employeeId, cancellationToken)
.ConfigureAwait(false);
}
catch
{
// Local/HRMS lookup optional on client.
}
if (string.IsNullOrWhiteSpace(siteId) && backendApi != null)
{
try
{
siteId = await backendApi.GetEmployeeLocationSiteAsync(employeeId, cancellationToken)
.ConfigureAwait(false);
}
catch
{
// Backend lookup optional.
}
}
config.ApplyLocationSiteIdFromAuth(siteId);
}
/// <summary>Format site for UI fields (e.g. "SITE : 02").</summary>
public static string FormatSiteIdForDisplay(string? siteId)
{
if (string.IsNullOrWhiteSpace(siteId))
return string.Empty;
var raw = siteId.Trim();
if (raw.StartsWith("SITE :", StringComparison.OrdinalIgnoreCase))
return raw;
var digits = new string(raw.Where(char.IsDigit).ToArray());
return string.IsNullOrEmpty(digits) ? raw : $"SITE : {digits}";
}
/// <summary>Extract digits from site display for scanner header.</summary>
public static string ExtractSiteDigits(string? siteId)
{
if (string.IsNullOrWhiteSpace(siteId))
return "1";
var raw = siteId.Trim();
if (raw.StartsWith("SITE :", StringComparison.OrdinalIgnoreCase))
raw = raw.Substring(raw.IndexOf(':') + 1).Trim();
var digits = new string(raw.Where(char.IsDigit).ToArray());
return string.IsNullOrEmpty(digits) ? "1" : digits;
}
}

View File

@ -12,6 +12,7 @@ public partial class AdminLoginViewModel : ObservableObject
private readonly IConfigService _config;
private readonly IAdminAuditService _adminAudit;
private readonly IEmployeeLookupService _employeeLookup;
private readonly ICanteenBackendApiClient? _backendApi;
[ObservableProperty]
private string _username = string.Empty;
@ -35,7 +36,8 @@ public partial class AdminLoginViewModel : ObservableObject
INavigationService navigation,
IConfigService config,
IAdminAuditService adminAudit,
IEmployeeLookupService employeeLookup)
IEmployeeLookupService employeeLookup,
ICanteenBackendApiClient? backendApi = null)
{
_authService = authService;
_session = session;
@ -43,6 +45,7 @@ public partial class AdminLoginViewModel : ObservableObject
_config = config;
_adminAudit = adminAudit;
_employeeLookup = employeeLookup;
_backendApi = backendApi;
RememberCredentials = _config.GetRememberAdminCredentials();
if (RememberCredentials)
@ -86,15 +89,11 @@ public partial class AdminLoginViewModel : ObservableObject
_session.SetAdminAuthenticated(user, result.EmployeeId);
try
{
var siteId = await _employeeLookup.GetLocationSiteIdByEmployeeSerialAsync(result.EmployeeId).ConfigureAwait(true);
_config.ApplyLocationSiteIdFromAuth(siteId);
}
catch
{
// HRMS lookup optional; login still succeeds.
}
await AdminLoginHelper.ApplyAdminLoginDefaultsAsync(
result.EmployeeId,
_config,
_employeeLookup,
_backendApi).ConfigureAwait(true);
// Persist who logged in (for audit / reporting).
await _adminAudit.RecordLoginAsync(user, result.EmployeeId).ConfigureAwait(false);

View File

@ -11,6 +11,7 @@ public partial class AdminSettingsAuthViewModel : ObservableObject
private readonly INavigationService _navigation;
private readonly IConfigService _config;
private readonly IEmployeeLookupService _employeeLookup;
private readonly ICanteenBackendApiClient? _backendApi;
[ObservableProperty]
private string _username = string.Empty;
@ -33,13 +34,15 @@ public partial class AdminSettingsAuthViewModel : ObservableObject
AppSession session,
INavigationService navigation,
IConfigService config,
IEmployeeLookupService employeeLookup)
IEmployeeLookupService employeeLookup,
ICanteenBackendApiClient? backendApi = null)
{
_authService = authService;
_session = session;
_navigation = navigation;
_config = config;
_employeeLookup = employeeLookup;
_backendApi = backendApi;
RememberCredentials = _config.GetRememberAdminCredentials();
if (RememberCredentials)
@ -90,15 +93,11 @@ public partial class AdminSettingsAuthViewModel : ObservableObject
// Refresh session details (who authenticated for Settings).
_session.SetAdminAuthenticated(user, result.EmployeeId);
try
{
var siteId = await _employeeLookup.GetLocationSiteIdByEmployeeSerialAsync(result.EmployeeId).ConfigureAwait(true);
_config.ApplyLocationSiteIdFromAuth(siteId);
}
catch
{
// HRMS lookup optional; auth still succeeds.
}
await AdminLoginHelper.ApplyAdminLoginDefaultsAsync(
result.EmployeeId,
_config,
_employeeLookup,
_backendApi).ConfigureAwait(true);
// Persist credentials only if user opted in.
_config.SetRememberAdminCredentials(RememberCredentials);