fix(admin-layout): fix Guest display, error bar, empty shops

- Subscribe to AuthState.OnChange to refresh user info after login
- Call TryRestoreSessionAsync on first render to restore from localStorage
- Wrap Body in ErrorBoundary with custom UI (replaces red error bar)
- Properly unsubscribe OnChange in Dispose to prevent memory leaks
This commit is contained in:
Ho Ngoc Hai
2026-03-01 00:11:23 +07:00
parent 59521f6fd6
commit e5b3aa58fb

View File

@@ -134,14 +134,32 @@
@* ═══ MAIN AREA ═══ *@
<main class="admin-main">
<CascadingValue Value="this">
@Body
</CascadingValue>
<ErrorBoundary @ref="_errorBoundary">
<ChildContent>
<CascadingValue Value="this">
@Body
</CascadingValue>
</ChildContent>
<ErrorContent>
<div style="text-align:center;padding:48px 20px;">
<div style="width:64px;height:64px;border-radius:20px;background:rgba(239,68,68,0.1);display:flex;align-items:center;justify-content:center;margin:0 auto 16px;">
<i data-lucide="alert-triangle" style="width:28px;height:28px;color:#EF4444;"></i>
</div>
<h3 style="font-size:16px;font-weight:700;margin:0 0 8px;color:#EF4444;">Đã xảy ra lỗi</h3>
<p style="font-size:13px;color:var(--admin-text-tertiary);margin:0 0 16px;">Trang gặp sự cố. Vui lòng tải lại.</p>
<button class="admin-btn-primary" @onclick="RecoverError" style="display:inline-flex;align-items:center;gap:8px;">
<i data-lucide="refresh-cw" style="width:14px;height:14px;"></i>
Tải lại
</button>
</div>
</ErrorContent>
</ErrorBoundary>
</main>
</div>
@code {
private bool _sidebarOpen = false;
private ErrorBoundary? _errorBoundary;
// EN: Shop context detection — parsed from URL
// VI: Phát hiện context cửa hàng — parse từ URL
@@ -153,16 +171,29 @@
protected override void OnInitialized()
{
NavigationManager.LocationChanged += OnLocationChanged;
// EN: Subscribe to auth state changes so user display refreshes after login/logout
// VI: Subscribe auth state để cập nhật user display sau login/logout
AuthState.OnChange += OnAuthStateChanged;
DetectShopContext();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// EN: Restore session from localStorage on first render (Blazor WASM ready)
// VI: Khôi phục session từ localStorage khi render lần đầu (Blazor WASM sẵn sàng)
await AuthSvc.TryRestoreSessionAsync();
}
// EN: Re-init Lucide icons after every render (Blazor navigation replaces DOM)
// VI: Khởi tạo lại Lucide icons sau mỗi lần render (Blazor navigation thay đổi DOM)
try { await JS.InvokeVoidAsync("lucide.createIcons"); } catch { }
}
private void OnAuthStateChanged() => InvokeAsync(StateHasChanged);
private void RecoverError() => _errorBoundary?.Recover();
private void OnLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e)
{
DetectShopContext();
@@ -251,5 +282,6 @@
public void Dispose()
{
NavigationManager.LocationChanged -= OnLocationChanged;
AuthState.OnChange -= OnAuthStateChanged;
}
}