feat: full FrontOffice updates - discount store, blog, payment gateway, UI improvements
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
This commit is contained in:
masoodafar-web
2026-02-16 00:51:39 +03:30
parent f0d1156457
commit 6db83ccd90
104 changed files with 7668 additions and 1370 deletions
@@ -0,0 +1,193 @@
@using CMSMicroservice.Protobuf.Protos.UserAddress
@attribute [Route(RouteConstants.DiscountStore.Checkout)]
<PageTitle>تسویه حساب فروشگاه تخفیفی</PageTitle>
<MudContainer MaxWidth="MaxWidth.Large" Class="py-6">
<PageHeader Title="تسویه حساب تخفیفی" BackHref="@RouteConstants.DiscountStore.Cart" />
<MudGrid Spacing="3">
<!-- Left Column: Address + Payment Settings -->
<MudItem xs="12" md="8">
<!-- Address Selection -->
<MudPaper Elevation="2" Class="pa-4 rounded-lg mb-3">
<MudText Typo="Typo.h6" Class="mb-2">انتخاب آدرس</MudText>
@if (_loadingAddresses)
{
<MudStack AlignItems="AlignItems.Center" Class="py-4">
<MudProgressCircular Indeterminate="true" Color="Color.Primary" />
<MudText Class="mt-2 mud-text-secondary">در حال بارگذاری آدرس‌ها...</MudText>
</MudStack>
}
else if (_addresses.Count == 0)
{
<MudAlert Severity="Severity.Warning">
هیچ آدرسی ثبت نشده است. لطفاً از بخش پروفایل آدرس خود را اضافه کنید.
</MudAlert>
<MudButton Class="mt-2" Variant="Variant.Outlined" Href="/profile/addresses">افزودن آدرس</MudButton>
}
else
{
<MudStack Spacing="1">
@foreach (var address in _addresses)
{
<MudPaper Outlined="@(_selectedAddress?.Id != address.Id)"
Elevation="@(_selectedAddress?.Id == address.Id ? 2 : 0)"
Class="pa-3 rounded-xl cursor-pointer"
Style="@(_selectedAddress?.Id == address.Id ? "border: 2px solid var(--mud-palette-primary);" : "")"
@onclick="() => _selectedAddress = address">
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
<MudStack>
<MudText Typo="Typo.subtitle2">@address.Title</MudText>
<MudText Typo="Typo.body2" Class="mud-text-secondary">@address.Address</MudText>
</MudStack>
@if (address.IsDefault)
{
<MudChip T="string" Color="Color.Success" Variant="Variant.Outlined">پیش‌فرض</MudChip>
}
</MudStack>
</MudPaper>
}
</MudStack>
}
</MudPaper>
<!-- Discount Balance (Hybrid Payment) -->
<MudPaper Elevation="2" Class="pa-4 rounded-lg mb-3">
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center" Class="mb-3">
<MudIcon Icon="@Icons.Material.Filled.Loyalty" Color="Color.Secondary" />
<MudText Typo="Typo.h6">پرداخت از کیف تخفیفی</MudText>
</MudStack>
<MudStack Spacing="2">
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="flex-wrap">
<MudText Typo="Typo.body2">موجودی کیف تخفیفی شما:</MudText>
<MudText Typo="Typo.subtitle1" Color="Color.Secondary" Class="fw-bold">
@FormatPrice(_discountBalance) تومان
</MudText>
</MudStack>
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="flex-wrap">
<MudText Typo="Typo.body2">سقف مجاز تخفیف روی این سفارش:</MudText>
<MudText Typo="Typo.subtitle2" Color="Color.Success">
@FormatPrice(MaxAllowedDiscount) تومان
</MudText>
</MudStack>
<MudDivider />
<MudText Typo="Typo.body2" Class="mud-text-secondary">
مبلغی که از کیف تخفیفی کسر شود (باقیمانده از درگاه پرداخت می‌شود):
</MudText>
<MudSlider T="long" @bind-Value="_discountBalanceToUse"
Min="0" Max="@MaxUsable"
Step="1000"
Color="Color.Secondary"
Immediate="true">
استفاده از @FormatPrice(_discountBalanceToUse) تومان
</MudSlider>
<MudNumericField T="long" @bind-Value="_discountBalanceToUse"
Min="0" Max="@MaxUsable"
Label="مبلغ دلخواه (تومان)" Variant="Variant.Outlined"
Margin="Margin.Dense" Immediate="true" />
@if (_discountBalanceToUse > 0)
{
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense="true">
مبلغ @FormatPrice(_discountBalanceToUse) تومان از کیف تخفیفی کسر و
مبلغ @FormatPrice(GatewayAmount) تومان از درگاه پرداخت دریافت خواهد شد.
</MudAlert>
}
</MudStack>
</MudPaper>
<!-- Notes -->
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
<MudText Typo="Typo.h6" Class="mb-2">توضیحات سفارش</MudText>
<MudTextField T="string" @bind-Value="_notes" Lines="3"
Placeholder="توضیحات اختیاری..."
Variant="Variant.Outlined" />
</MudPaper>
</MudItem>
<!-- Right Column: Order Summary -->
<MudItem xs="12" md="4">
<MudPaper Elevation="2" Class="pa-4 rounded-lg" Style="position:sticky; top:80px;">
<MudText Typo="Typo.h6" Class="mb-2">خلاصه سفارش</MudText>
@if (DiscountCart.Items.Count == 0)
{
<MudAlert Severity="Severity.Info">سبد خرید شما خالی است.</MudAlert>
}
else
{
<MudList T="string" Dense="true">
@foreach (var item in DiscountCart.Items)
{
<MudListItem T="string">
<MudListItemText>
<MudStack Spacing="1">
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
<AppImage Path="@GetImageUrl(item.ImageUrl)" Alt="@item.Title"
ImgWidth="40" ImgHeight="40" Class="rounded-circle" />
<MudText Typo="Typo.subtitle2" Style="flex:1; margin:0 8px;">@item.Title</MudText>
<MudText Typo="Typo.subtitle2">@FormatPrice(item.FinalPrice)</MudText>
</MudStack>
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
<MudText Typo="Typo.caption" Class="mud-text-secondary">
@item.Quantity × @FormatPrice(item.UnitPrice)
</MudText>
@if (item.MaxDiscountPercent > 0)
{
<MudText Typo="Typo.caption" Color="Color.Error">تا @item.MaxDiscountPercent% تخفیف</MudText>
}
</MudStack>
</MudStack>
</MudListItemText>
</MudListItem>
}
</MudList>
<MudDivider Class="my-2" />
<MudStack Spacing="1">
<MudStack Row="true" Justify="Justify.SpaceBetween">
<MudText Typo="Typo.body2">جمع کل:</MudText>
<MudText Typo="Typo.body2">@FormatPrice(DiscountCart.TotalPrice)</MudText>
</MudStack>
@if (_discountBalanceToUse > 0)
{
<MudStack Row="true" Justify="Justify.SpaceBetween">
<MudText Typo="Typo.body2" Color="Color.Success">از کیف تخفیفی:</MudText>
<MudText Typo="Typo.body2" Color="Color.Success">@FormatPrice(_discountBalanceToUse)-</MudText>
</MudStack>
}
<MudDivider Class="my-1" />
<MudStack Row="true" Justify="Justify.SpaceBetween">
<MudText Typo="Typo.subtitle1" Class="fw-bold">پرداخت از درگاه:</MudText>
<MudText Typo="Typo.subtitle1" Color="Color.Primary" Class="fw-bold">
@FormatPrice(GatewayAmount) تومان
</MudText>
</MudStack>
<MudText Typo="Typo.caption" Class="mud-text-secondary">
مالیات بر ارزش افزوده صرفاً بر مبلغ درگاه محاسبه خواهد شد.
</MudText>
</MudStack>
<MudButton Disabled="@(!CanPlaceOrder || _placing)" Class="mt-3 w-100-mobile"
Variant="Variant.Filled" Color="Color.Primary"
OnClick="PlaceOrder" FullWidth="true"
StartIcon="@Icons.Material.Filled.CheckCircle">
@(_placing ? "در حال ثبت سفارش..." : "ثبت و پرداخت سفارش")
</MudButton>
}
</MudPaper>
</MudItem>
</MudGrid>
</MudContainer>