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,209 @@
using CMSMicroservice.Protobuf.Protos.DiscountOrder;
using Google.Protobuf.WellKnownTypes;
using MudBlazor;
namespace FrontOffice.Main.Utilities;
// ── DTOs ──
public record DiscountOrderSummary(
long Id,
string OrderNumber,
long TotalPrice,
long DiscountBalanceUsed,
long GatewayAmount,
bool PaymentCompleted,
int DeliveryStatus,
string? TrackingCode,
int ItemsCount,
DateTime Created);
public record DiscountOrderDetail(
long Id,
string OrderNumber,
long TotalPrice,
long DiscountBalanceUsed,
long GatewayAmount,
bool PaymentCompleted,
string? TransactionId,
int DeliveryStatus,
string? TrackingCode,
string? Notes,
string? AdminNotes,
DiscountOrderAddress? Address,
List<DiscountOrderItem> Items,
DateTime Created);
public record DiscountOrderAddress(
long Id, string Title, string Address, string PostalCode, string? Phone);
public record DiscountOrderItem(
long ProductId,
string Title,
long UnitPrice,
int MaxDiscountPercent,
int Count,
long TotalPrice,
long DiscountAmount,
long FinalPrice);
public record PlaceDiscountOrderResult(
bool Success,
string Message,
long OrderId,
long GatewayAmount,
string? PaymentUrl);
public record DiscountOrderListResult(
List<DiscountOrderSummary> Orders,
int TotalCount,
int TotalPages,
int CurrentPage);
// ── Service ──
public class DiscountOrderService
{
private readonly DiscountOrderContract.DiscountOrderContractClient _client;
public DiscountOrderService(DiscountOrderContract.DiscountOrderContractClient client)
{
_client = client;
}
public async Task<PlaceDiscountOrderResult> PlaceOrderAsync(long addressId, long discountBalanceToUse, string? notes = null)
{
try
{
var request = new PlaceOrderRequest
{
UserAddressId = addressId,
DiscountBalanceToUse = discountBalanceToUse
};
if (!string.IsNullOrWhiteSpace(notes))
request.Notes = notes;
var response = await _client.PlaceOrderAsync(request);
return new PlaceDiscountOrderResult(
Success: response.Success,
Message: response.Message ?? string.Empty,
OrderId: response.OrderId,
GatewayAmount: response.GatewayAmount,
PaymentUrl: response.PaymentUrl);
}
catch (Exception ex)
{
return new PlaceDiscountOrderResult(false, ex.Message, 0, 0, null);
}
}
public async Task<bool> CompletePaymentAsync(long orderId, string? transactionId, bool success)
{
try
{
var response = await _client.CompleteOrderPaymentAsync(new CompleteOrderPaymentRequest
{
OrderId = orderId,
TransactionId = transactionId,
PaymentSuccess = success
});
return response.Success;
}
catch { return false; }
}
public async Task<DiscountOrderListResult> GetUserOrdersAsync(int page = 1, int pageSize = 10)
{
try
{
var response = await _client.GetUserOrdersAsync(new GetUserOrdersRequest
{
PageNumber = page,
PageSize = pageSize
});
var orders = response.Models.Select(m => new DiscountOrderSummary(
Id: m.Id,
OrderNumber: m.OrderNumber ?? string.Empty,
TotalPrice: m.TotalPrice,
DiscountBalanceUsed: m.DiscountBalanceUsed,
GatewayAmount: m.GatewayAmount,
PaymentCompleted: m.PaymentCompleted,
DeliveryStatus: (int)m.DeliveryStatus,
TrackingCode: m.TrackingCode,
ItemsCount: m.ItemsCount,
Created: m.Created?.ToDateTime() ?? DateTime.MinValue
)).ToList();
var totalCount = (int)(response.MetaData?.TotalCount ?? 0);
var totalPages = (int)(response.MetaData?.TotalPage ?? 0);
return new DiscountOrderListResult(orders, totalCount, totalPages, page);
}
catch
{
return new DiscountOrderListResult(new(), 0, 0, page);
}
}
public async Task<DiscountOrderDetail?> GetOrderByIdAsync(long orderId)
{
try
{
var r = await _client.GetOrderByIdAsync(new GetOrderByIdRequest { OrderId = orderId });
var address = r.Address != null
? new DiscountOrderAddress(r.Address.Id, r.Address.Title ?? "", r.Address.Address ?? "", r.Address.PostalCode ?? "", r.Address.Phone)
: null;
var items = r.Items.Select(i => new DiscountOrderItem(
ProductId: i.ProductId,
Title: i.ProductTitle ?? string.Empty,
UnitPrice: i.UnitPrice,
MaxDiscountPercent: i.MaxDiscountPercent,
Count: i.Count,
TotalPrice: i.TotalPrice,
DiscountAmount: i.DiscountAmount,
FinalPrice: i.FinalPrice
)).ToList();
return new DiscountOrderDetail(
Id: r.Id,
OrderNumber: r.OrderNumber ?? string.Empty,
TotalPrice: r.TotalPrice,
DiscountBalanceUsed: r.DiscountBalanceUsed,
GatewayAmount: r.GatewayAmount,
PaymentCompleted: r.PaymentCompleted,
TransactionId: r.TransactionId,
DeliveryStatus: (int)r.DeliveryStatus,
TrackingCode: r.TrackingCode,
Notes: r.Notes,
AdminNotes: r.AdminNotes,
Address: address,
Items: items,
Created: r.Created?.ToDateTime() ?? DateTime.MinValue);
}
catch
{
return null;
}
}
public static string GetDeliveryStatusText(int status) => status switch
{
0 => "در انتظار",
1 => "در حال پردازش",
2 => "ارسال شده",
3 => "تحویل داده شده",
4 => "لغو شده",
_ => "نامشخص"
};
public static Color GetDeliveryStatusColor(int status) => status switch
{
0 => Color.Warning,
1 => Color.Info,
2 => Color.Primary,
3 => Color.Success,
4 => Color.Error,
_ => Color.Default
};
}