From 2c8b3c8483f432109c98942191e7874aad8a5386 Mon Sep 17 00:00:00 2001 From: MeysamMoghaddam <65253484+MeysamMoghaddam@users.noreply.github.com> Date: Thu, 9 Oct 2025 21:55:18 +0330 Subject: [PATCH] u --- src/FrontOffice.Main/Pages/Index.razor | 3 +- src/FrontOffice.Main/Pages/Index.razor.cs | 17 +- .../Pages/PackageDetail.razor | 206 +++++++++++++++++ .../Pages/PackageDetail.razor.cs | 214 ++++++++++++++++++ .../Utilities/RouteConstants.cs | 5 + 5 files changed, 434 insertions(+), 11 deletions(-) create mode 100644 src/FrontOffice.Main/Pages/PackageDetail.razor create mode 100644 src/FrontOffice.Main/Pages/PackageDetail.razor.cs diff --git a/src/FrontOffice.Main/Pages/Index.razor b/src/FrontOffice.Main/Pages/Index.razor index 6a733c4..34e0d9d 100644 --- a/src/FrontOffice.Main/Pages/Index.razor +++ b/src/FrontOffice.Main/Pages/Index.razor @@ -166,7 +166,8 @@ مشاهده جزئیات + FullWidth="true" + OnClick="@(() => NavigateToPackage(p.Id))">مشاهده جزئیات diff --git a/src/FrontOffice.Main/Pages/Index.razor.cs b/src/FrontOffice.Main/Pages/Index.razor.cs index 2902308..4b162e0 100644 --- a/src/FrontOffice.Main/Pages/Index.razor.cs +++ b/src/FrontOffice.Main/Pages/Index.razor.cs @@ -1,7 +1,5 @@ using FrontOffice.BFF.Package.Protobuf.Protos.Package; using FrontOffice.Main.Utilities; -using Google.Protobuf.WellKnownTypes; -using Grpc.Core; using Microsoft.AspNetCore.Components; using MudBlazor; @@ -28,6 +26,7 @@ public partial class Index if (response?.Models?.Any() == true) { _packs = response.Models.Select(p => new Pack( + p.Id, p.Title, p.Description, Image: UrlUtility.DownloadUrl + p.ImagePath @@ -60,16 +59,14 @@ public partial class Index _email = string.Empty; } - private record Pack(string Title, string Body, string Image); + private void NavigateToPackage(long packageId) + { + Navigation.NavigateTo($"{RouteConstants.Package.Detail}/{packageId}"); + } + + private record Pack(long Id,string Title, string Body, string Image); private record Plan(string Name, string Price, bool Highlight, IEnumerable Features); private record QA(string Q, string A); - private readonly List _plans = new() - { - new("استارتر", "رایگان", false, new []{ "تا ۲۰۰ عضو", "شجره‌نامه پایه", "پشتیبانی ایمیلی" }), - new("رشد", "۳۹ دلار / ماه", true, new []{ "تا ۵۰۰۰ عضو", "شجره‌نامه پیشرفته", "موتور کارمزد", "پشتیبانی اولویت‌دار" }), - new("اسکیل", "تماس بگیرید", false, new []{ "نامحدود", "قوانین سفارشی", "SLA و آن‌بوردینگ", "مدیر موفقیت اختصاصی" }), - }; - private readonly List _faqs = new() { new("دامنهٔ اختصاصی دارم؛ قابل اتصال است؟", "بله، پشت دامنه و گواهی SSL خودتان مستقر می‌شود."), diff --git a/src/FrontOffice.Main/Pages/PackageDetail.razor b/src/FrontOffice.Main/Pages/PackageDetail.razor new file mode 100644 index 0000000..8aa36b7 --- /dev/null +++ b/src/FrontOffice.Main/Pages/PackageDetail.razor @@ -0,0 +1,206 @@ +@attribute [Route(RouteConstants.Package.Detail + "{id:long}")] + +@(_package?.Title ?? "جزئیات پکیج") + +@if (_isLoading) +{ + + + در حال بارگذاری... + +} +else if (_package == null) +{ + + + پکیج یافت نشد + پکیج مورد نظر وجود ندارد یا حذف شده است. + + بازگشت به صفحه اصلی + + +} +else +{ + + + + + + + + + + + + + + + +
+ توضیحات پکیج + @((MarkupString)_package.Body) +
+ + +
+ مشخصات پکیج + + @foreach (var spec in _package.Specifications) + { + + + + +
+ @spec.Name + @spec.Value +
+
+
+
+ } +
+
+
+
+ + + + نظرات کاربران + + @if (_reviews.Any()) + { + + @foreach (var review in _reviews) + { + + + + + + +
+ @(review.UserName) + +
+ + @(review.Date) +
+ @(review.Comment) +
+
+ } +
+ } + else + { + + + هنوز نظری ثبت نشده است. + + } +
+
+ + + + + + + + + + + @(_package.Title) + + + + + + @if (_package.Pricing.HasDiscount) + { +
+ + @_package.Pricing.OriginalPrice.ToString("N0") تومان + @_package.Pricing.DiscountPercent% تخفیف + + @_package.Pricing.FinalPrice.ToString("N0") تومان +
+ } + else + { + @_package.Pricing.FinalPrice.ToString("N0") تومان + } + + + + @(_isPurchasing ? "در حال پردازش..." : "خرید پکیج") + + + +
+ شامل: + + @foreach (var feature in _package.Features) + { + + + @feature + + } + +
+ + + + + + پشتیبانی ۲۴ ساعته + +
+
+
+
+
+ + + @if (_relatedPackages.Any()) + { +
+ + پکیج‌های پیشنهادی + + @foreach (var relatedPackage in _relatedPackages) + { + + + + @relatedPackage.Title + @relatedPackage.ShortDescription + + @relatedPackage.Pricing.FinalPrice.ToString("N0") تومان + + + } + + +
+ } +} \ No newline at end of file diff --git a/src/FrontOffice.Main/Pages/PackageDetail.razor.cs b/src/FrontOffice.Main/Pages/PackageDetail.razor.cs new file mode 100644 index 0000000..8b88ff7 --- /dev/null +++ b/src/FrontOffice.Main/Pages/PackageDetail.razor.cs @@ -0,0 +1,214 @@ +using FrontOffice.BFF.Package.Protobuf.Protos.Package; +using FrontOffice.Main.Utilities; +using Grpc.Core; +using Microsoft.AspNetCore.Components; +using MudBlazor; + +namespace FrontOffice.Main.Pages; + +public partial class PackageDetail : IDisposable +{ + [Parameter] public long Id { get; set; } + + [Inject] private PackageContract.PackageContractClient PackageClient { get; set; } = default!; + + private PackageDetailDto? _package; + private List _reviews = new(); + private List _relatedPackages = new(); + private bool _isLoading = true; + private bool _isPurchasing; + private CancellationTokenSource? _loadCts; + + + private List _breadcrumbItems = new() + { + new BreadcrumbItem("صفحه اصلی", RouteConstants.Main.MainPage), + new BreadcrumbItem("پکیج‌ها", "#features"), + new BreadcrumbItem("جزئیات پکیج", null, disabled: true) + }; + protected override async Task OnInitializedAsync() + { + if (Id < 1) + { + _isLoading = false; + return; + } + + await LoadPackageDetailsAsync(); + } + + protected override async Task OnParametersSetAsync() + { + if (Id > 0) + { + await LoadPackageDetailsAsync(); + } + } + + private async Task LoadPackageDetailsAsync() + { + _isLoading = true; + _loadCts?.Cancel(); + _loadCts?.Dispose(); + _loadCts = new CancellationTokenSource(); + + try + { + // Load package details + var packageRequest = new GetPackageRequest { Id = Id }; + var packageResponse = await PackageClient.GetPackageAsync(request: new() { Id = Id}, cancellationToken: _loadCts.Token); + + if (packageResponse != null) + { + _package = new PackageDetailDto + { + Id = packageResponse.Id, + Title = packageResponse.Title, + Body = packageResponse.Description, + Image = UrlUtility.DownloadUrl + packageResponse.ImagePath, + Specifications = new List + { + new() { Name = "ظرفیت", Value = "تا ۲۰۰ عضو", Icon = Icons.Material.Filled.Group }, + new() { Name = "شجره‌نامه", Value = "پیشرفته", Icon = Icons.Material.Filled.AccountTree }, + new() { Name = "گزارش‌گیری", Value = "جامع", Icon = Icons.Material.Filled.Analytics }, + new() { Name = "پشتیبانی", Value = "۲۴ ساعته", Icon = Icons.Material.Filled.Support } + }, + Features = new List + { + "مدیریت تیم نامحدود", + "شجره‌نامه بصری", + "محاسبه کارمزد خودکار", + "گزارش‌های مالی", + "پشتیبانی اولویت‌دار" + }, + Pricing = new PricingInfo + { + OriginalPrice = packageResponse.Price, + FinalPrice = packageResponse.Price, + HasDiscount = false, + DiscountPercent = 0 + } + }; + + // Load reviews (mock data for now) + await LoadReviewsAsync(); + + // Load related packages + await LoadRelatedPackagesAsync(); + } + } + catch (RpcException rpcEx) + { + Snackbar.Add($"خطا در بارگذاری پکیج: {rpcEx.Status.Detail}", Severity.Error); + } + catch (Exception ex) + { + Snackbar.Add($"خطا در بارگذاری پکیج: {ex.Message}", Severity.Error); + } + finally + { + _isLoading = false; + await InvokeAsync(StateHasChanged); + } + } + + private async Task LoadReviewsAsync() + { + // TODO: Load reviews from API + _reviews = new List + { + new() { UserName = "علی احمدی", Rating = 5, Comment = "عالی! کارمزد رو دقیق حساب می‌کنه و گزارش‌ها کامل هستن.", Date = "۱۴۰۲/۱۰/۰۵" }, + new() { UserName = "مریم رضایی", Rating = 4, Comment = "رابط کاربری خوبی داره، فقط سرعت بارگذاری می‌تونه بهتر بشه.", Date = "۱۴۰۲/۰۹/۲۲" }, + new() { UserName = "حسن کریمی", Rating = 5, Comment = "پشتیبانی فوق‌العاده سریع و حرفه‌ای داشتن. پیشنهاد می‌کنم.", Date = "۱۴۰۲/۰۹/۱۵" } + }; + } + + private async Task LoadRelatedPackagesAsync() + { + // TODO: Load related packages from API + _relatedPackages = new List + { + new() { Id = "2", Title = "پکیج رشد", ShortDescription = "مناسب برای تیم‌های در حال توسعه", Image = "https://images.unsplash.com/photo-1552664730-d307ca884978?q=80&w=400", Pricing = new PricingInfo { FinalPrice = 750000 } }, + new() { Id = "3", Title = "پکیج حرفه‌ای", ShortDescription = "برای کسب‌وکارهای بزرگ", Image = "https://images.unsplash.com/photo-1460925895917-afdab827c52f?q=80&w=400", Pricing = new PricingInfo { FinalPrice = 1200000 } } + }; + } + + private async Task PurchasePackage() + { + if (_package == null) return; + + _isPurchasing = true; + + try + { + // TODO: Implement purchase logic + await Task.Delay(2000); // Simulate API call + + Snackbar.Add("پکیج با موفقیت خریداری شد!", Severity.Success); + } + catch (Exception ex) + { + Snackbar.Add($"خطا در خرید پکیج: {ex.Message}", Severity.Error); + } + finally + { + _isPurchasing = false; + await InvokeAsync(StateHasChanged); + } + } + + private void NavigateToPackage(string packageId) + { + Navigation.NavigateTo($"{RouteConstants.Package.Detail}/{packageId}"); + } + + public void Dispose() + { + _loadCts?.Cancel(); + _loadCts?.Dispose(); + _loadCts = null; + } + + public class PackageDetailDto + { + public long? Id { get; set; } + public string? Title { get; set; } + public string? Body { get; set; } + public string? Image { get; set; } + public List Specifications { get; set; } = new(); + public List Features { get; set; } = new(); + public PricingInfo Pricing { get; set; } = new(); + } + + public class Specification + { + public string? Name { get; set; } + public string? Value { get; set; } + public string? Icon { get; set; } + } + + public class PricingInfo + { + public long OriginalPrice { get; set; } + public long FinalPrice { get; set; } + public bool HasDiscount { get; set; } + public int DiscountPercent { get; set; } + } + + public class Review + { + public string? UserName { get; set; } + public int Rating { get; set; } + public string? Comment { get; set; } + public string? Date { get; set; } + } + + public class RelatedPackage + { + public string? Id { get; set; } + public string? Title { get; set; } + public string? ShortDescription { get; set; } + public string? Image { get; set; } + public PricingInfo Pricing { get; set; } = new(); + } +} \ No newline at end of file diff --git a/src/FrontOffice.Main/Utilities/RouteConstants.cs b/src/FrontOffice.Main/Utilities/RouteConstants.cs index dbbe53d..b59e83c 100644 --- a/src/FrontOffice.Main/Utilities/RouteConstants.cs +++ b/src/FrontOffice.Main/Utilities/RouteConstants.cs @@ -17,4 +17,9 @@ public static class RouteConstants { public const string Index = "/profile"; } + + public static class Package + { + public const string Detail = "/package/"; + } }