6db83ccd90
Build and Deploy to Kubernetes / build-and-deploy (push) Failing after 4m55s
- Discount store pages (products, cart, orders, order detail) - Blog pages and services - Payment gateway callback page - AppImage component, EmptyState, LoadingState, PageHeader - DiscountCartService, DiscountOrderService, DiscountProductService - BlogCategoryService, BlogPostService, SitePageService, ImageCacheService - PhoneVerifyForm component - Profile Hub page - UI/UX improvements across all pages - landing.js for homepage - Config and routing updates
284 lines
10 KiB
C#
284 lines
10 KiB
C#
using Blazored.LocalStorage;
|
|
using FrontOffice.Main.Utilities;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.AspNetCore.Components.Routing;
|
|
using Microsoft.JSInterop;
|
|
using MudBlazor;
|
|
using Microsoft.AspNetCore.Components.Authorization;
|
|
|
|
namespace FrontOffice.Main.Shared;
|
|
public partial class MainLayout : IDisposable
|
|
{
|
|
private const string TokenStorageKey = "auth:token";
|
|
|
|
private MudThemeProvider _mudThemeProvider;
|
|
private bool _isDark;
|
|
private bool _drawerOpen;
|
|
private bool _isAuthenticated;
|
|
private string? _email;
|
|
private int _cartCount;
|
|
private int _discountCartCount;
|
|
private bool _isCompleteRegister;
|
|
|
|
// ── Store Context ──
|
|
private enum StoreContext { None, Regular, Discount }
|
|
private StoreContext _storeCtx = StoreContext.None;
|
|
private bool IsInRegularStore => _storeCtx == StoreContext.Regular;
|
|
private bool IsInDiscountStore => _storeCtx == StoreContext.Discount;
|
|
private bool IsInAnyStore => _storeCtx != StoreContext.None;
|
|
|
|
// متغیرهای بروزرسانی
|
|
private bool _hasUpdate;
|
|
private string? _newVersion;
|
|
private VersionCheckResult? _versionCheckResult;
|
|
|
|
[Inject] private ILocalStorageService LocalStorage { get; set; } = default!;
|
|
[Inject] private AuthService AuthService { get; set; } = default!;
|
|
[Inject] private AuthDialogService AuthDialogService { get; set; } = default!;
|
|
[Inject] private CartService CartService { get; set; } = default!;
|
|
[Inject] private DiscountCartService DiscountCartService { get; set; } = default!;
|
|
[Inject] private AppVersionService AppVersionService { get; set; } = default!;
|
|
|
|
private void ToggleTheme() => _isDark = !_isDark;
|
|
private void ToggleDrawer() => _drawerOpen = !_drawerOpen;
|
|
private async void Back()
|
|
{
|
|
await JSRuntime.InvokeVoidAsync("history.back");
|
|
}
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
Navigation.LocationChanged += OnLocationChanged;
|
|
UpdateStoreContext();
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (firstRender)
|
|
{
|
|
await JSRuntime.InvokeVoidAsync("changeNavBgOnBodyScroll", "top", null, 1);
|
|
await CheckAuthStatus();
|
|
await EnforceAuthGuardAsync();
|
|
|
|
if (_isAuthenticated)
|
|
{
|
|
// بررسی وضعیت ثبتنام کامل
|
|
_isCompleteRegister = await AuthService.IsCompleteRegisterAsync();
|
|
|
|
// لود سبد خرید فقط برای کاربر لاگین شده
|
|
await CartService.EnsureInitializedAsync();
|
|
CartService.OnChange += OnCartChanged;
|
|
_cartCount = CartService.Count;
|
|
|
|
await DiscountCartService.EnsureInitializedAsync();
|
|
DiscountCartService.OnChange += OnDiscountCartChanged;
|
|
_discountCartCount = DiscountCartService.Count;
|
|
|
|
// چک کردن بروزرسانی (بدون نمایش popup - فقط برای نشون دادن آیکون)
|
|
await CheckForUpdateSilentlyAsync();
|
|
}
|
|
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
private void OnLocationChanged(object? sender, LocationChangedEventArgs e)
|
|
{
|
|
UpdateStoreContext();
|
|
_ = InvokeAsync(async () =>
|
|
{
|
|
await EnforceAuthGuardAsync();
|
|
StateHasChanged();
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// If the current route requires authentication and the user is not logged in, redirect to home.
|
|
/// </summary>
|
|
private async Task EnforceAuthGuardAsync()
|
|
{
|
|
if (_isAuthenticated) return;
|
|
|
|
// Re-check in case token was set after initial render
|
|
_isAuthenticated = await AuthService.IsAuthenticatedAsync();
|
|
if (_isAuthenticated) return;
|
|
|
|
var path = new Uri(Navigation.Uri).AbsolutePath.ToLowerInvariant();
|
|
if (IsProtectedRoute(path))
|
|
{
|
|
Navigation.NavigateTo(RouteConstants.Main.MainPage, replace: true);
|
|
}
|
|
}
|
|
|
|
private static bool IsProtectedRoute(string path)
|
|
{
|
|
// Public routes — no auth needed
|
|
if (path is "/" or "/register" or "/about" or "/faq" or "/contact"
|
|
or "/packages" or "/stores" or "/products" or "/categories")
|
|
return false;
|
|
if (path.StartsWith("/blog")) return false;
|
|
if (path.StartsWith("/package/")) return false;
|
|
if (path.StartsWith("/product/")) return false;
|
|
if (path == "/discount-store" || path.StartsWith("/discount-store/product/")) return false;
|
|
|
|
// Everything else is protected
|
|
if (path.StartsWith("/profile")) return true;
|
|
if (path.StartsWith("/commission")) return true;
|
|
if (path.StartsWith("/network")) return true;
|
|
if (path.StartsWith("/club")) return true;
|
|
if (path.StartsWith("/checkout")) return true;
|
|
if (path.StartsWith("/cart")) return true;
|
|
if (path.StartsWith("/orders") || path.StartsWith("/order/") || path.StartsWith("/order-tracking/")) return true;
|
|
if (path.StartsWith("/my-packages") || path.StartsWith("/my-orders") || path.StartsWith("/my-cart")) return true;
|
|
if (path.StartsWith("/discount-store/cart") || path.StartsWith("/discount-store/checkout")
|
|
|| path.StartsWith("/discount-store/orders") || path.StartsWith("/discount-store/order/")) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
private void UpdateStoreContext()
|
|
{
|
|
var path = new Uri(Navigation.Uri).AbsolutePath.ToLowerInvariant();
|
|
|
|
// Discount store routes all start with /discount-store
|
|
if (path.StartsWith("/discount-store"))
|
|
{
|
|
_storeCtx = StoreContext.Discount;
|
|
}
|
|
// Regular store routes
|
|
else if (path.StartsWith("/products") || path.StartsWith("/product/") ||
|
|
path.StartsWith("/cart") || path.StartsWith("/checkout-summary") ||
|
|
path.StartsWith("/orders") || path.StartsWith("/order/") ||
|
|
path.StartsWith("/order-tracking/") || path.StartsWith("/categories"))
|
|
{
|
|
_storeCtx = StoreContext.Regular;
|
|
}
|
|
else
|
|
{
|
|
_storeCtx = StoreContext.None;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// چک کردن بروزرسانی بدون نمایش popup
|
|
/// </summary>
|
|
private async Task CheckForUpdateSilentlyAsync()
|
|
{
|
|
try
|
|
{
|
|
_versionCheckResult = await AppVersionService.CheckVersionAsync(ignoreSkipped: true);
|
|
|
|
if (_versionCheckResult.HasNewVersion && !string.IsNullOrEmpty(_versionCheckResult.NewVersion))
|
|
{
|
|
_hasUpdate = true;
|
|
_newVersion = _versionCheckResult.NewVersion;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// در صورت خطا نادیده بگیر
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// باز کردن دیالوگ بروزرسانی
|
|
/// </summary>
|
|
private async Task OpenUpdateDialog()
|
|
{
|
|
if (_versionCheckResult == null || string.IsNullOrEmpty(_versionCheckResult.NewVersion))
|
|
return;
|
|
|
|
// ثبت زمان نمایش دیالوگ
|
|
await AppVersionService.RecordDialogShownAsync();
|
|
|
|
var dialogOptions = new DialogOptions
|
|
{
|
|
BackdropClick = !_versionCheckResult.IsForceUpdate,
|
|
CloseOnEscapeKey = !_versionCheckResult.IsForceUpdate,
|
|
CloseButton = !_versionCheckResult.IsForceUpdate,
|
|
MaxWidth = MaxWidth.Small,
|
|
FullWidth = true
|
|
};
|
|
|
|
var parameters = new DialogParameters<ReleaseNotesDialog>
|
|
{
|
|
{ x => x.OldVersion, _versionCheckResult.OldVersion },
|
|
{ x => x.NewVersion, _versionCheckResult.NewVersion },
|
|
{ x => x.ReleaseNotes, _versionCheckResult.ReleaseNotes },
|
|
{ x => x.UpdateMessage, _versionCheckResult.UpdateMessage },
|
|
{ x => x.IsForceUpdate, _versionCheckResult.IsForceUpdate }
|
|
};
|
|
|
|
var dialog = await DialogService.ShowAsync<ReleaseNotesDialog>("بروزرسانی", parameters, dialogOptions);
|
|
var result = await dialog.Result;
|
|
|
|
if (result is { Canceled: false, Data: bool shouldUpdate })
|
|
{
|
|
if (shouldUpdate)
|
|
{
|
|
await AppVersionService.ApplyUpdateAsync(_versionCheckResult.NewVersion);
|
|
Navigation.NavigateTo(Navigation.Uri, forceLoad: true);
|
|
}
|
|
else
|
|
{
|
|
await AppVersionService.SkipVersionAsync(_versionCheckResult.NewVersion);
|
|
// نمایش آپدیت همچنان باقی بمونه (hasUpdate true بمونه)
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnCartChanged()
|
|
{
|
|
_cartCount = CartService.Count;
|
|
InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
private void OnDiscountCartChanged()
|
|
{
|
|
_discountCartCount = DiscountCartService.Count;
|
|
InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
private async Task CheckAuthStatus()
|
|
{
|
|
_isAuthenticated = await AuthService.IsAuthenticatedAsync();
|
|
}
|
|
|
|
private async Task OpenAuthDialog()
|
|
{
|
|
await AuthDialogService.ShowAuthDialogAsync();
|
|
await CheckAuthStatus();
|
|
if (_isAuthenticated)
|
|
{
|
|
Snackbar.Add(GlobalConstants.SuccessMsg, Severity.Success);
|
|
}
|
|
|
|
StateHasChanged();
|
|
Navigation.NavigateTo(RouteConstants.Main.MainPage);
|
|
}
|
|
|
|
private void NavigateToProfile()
|
|
{
|
|
Navigation.NavigateTo(RouteConstants.Profile.Index);
|
|
}
|
|
|
|
private void NavigateToHub()
|
|
{
|
|
Navigation.NavigateTo(RouteConstants.Profile.Hub);
|
|
}
|
|
|
|
private async Task Logout()
|
|
{
|
|
await AuthService.LogoutAsync();
|
|
_isAuthenticated = false;
|
|
_cartCount = 0;
|
|
StateHasChanged();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
CartService.OnChange -= OnCartChanged;
|
|
DiscountCartService.OnChange -= OnDiscountCartChanged;
|
|
Navigation.LocationChanged -= OnLocationChanged;
|
|
}
|
|
} |