# 🏪 یکسان‌سازی فروشگاه عادی و تخفیفی — BackOffice > **تاریخ:** ۱۴۰۴/۱۱/۲۴ (February 13, 2026) > **وضعیت:** ✅ کامل > **Build:** 0 Error ✅ --- ## ۱. هدف فروشگاه عادی و فروشگاه تخفیفی در پنل مدیریت باید از نظر **ظاهری و UX** کاملاً یکسان باشند. قبل از این تغییرات، صفحات فروشگاه تخفیفی ظاهر و ساختار متفاوتی داشتند. هدف این فاز: 1. **NavMenu** — جداسازی دو فروشگاه در گروه‌بندی‌های مجزا 2. **دسته‌بندی‌ها** — ظاهر یکسان با فروشگاه عادی (ستون‌ها، درخت، اکشن‌ها) 3. **محصولات** — ظاهر یکسان (گالری، فیلترها، ستون‌های گرید، اکسپورت) 4. **سفارشات** — حذف گزارش‌های کوچک اضافی، فقط لیست خالص + رفع باگ لیست خالی --- ## ۲. خلاصه تغییرات ### ۲.۱ بازسازی NavMenu | قبل | بعد | |-----|-----| | یک بخش «فروشگاه» با زیرگروه‌های محصولات + دسته‌بندی + سفارش + ویرایش دسته‌جمعی | دو گروه مجزا: «فروشگاه عادی» و «فروشگاه تخفیفی» | | ویرایش دسته‌جمعی در منو | حذف شد از منو | | انبارداری داخل فروشگاه | انبارداری گروه مجزا | | پکیج‌ها داخل فروشگاه | پکیج‌ها آیتم مستقل | **ساختار جدید:** ``` فروشگاه (بخش) ├── پکیج‌ها (مستقل) ├── فروشگاه عادی (NavGroup) │ ├── محصولات → /ProductsPage/ │ ├── دسته‌بندی‌ها → /CategoryPage/ │ └── سفارشات و گزارش → /OrdersPage/ ├── انبارداری (NavGroup مستقل) │ ├── موجودی انبار │ ├── محصولات کم‌موجود │ ├── مدیریت انبارها │ └── تاریخچه تغییرات └── فروشگاه تخفیفی (NavGroup) ├── محصولات → /discount-products ├── دسته‌بندی‌ها → /discount-categories └── سفارشات و گزارش → /discount-orders ``` **فایل:** `Shared/NavMenu.razor` --- ### ۲.۲ رفع لیست خالی سفارشات + حذف گزارش‌های کوچک **مشکل ۱ — لیست خالی:** - `PaymentDate.ToDateTime()` بدون null check باعث exception در WASM می‌شد - Exception در Blazor WASM silent است و grid خالی نشان می‌دهد - **رفع:** اضافه کردن `@if (context.Item.PaymentDate != null)` با fallback `"-"` **مشکل ۲ — گزارش‌های اضافی:** - کارت‌های آماری (تعداد سفارشات + مجموع مبلغ) و نمودار Bar وضعیت ارسال بالای گرید بودند - این آمار اضافی بود چون تب جداگانه «گزارش فروش» وجود دارد - **رفع:** حذف کامل `MudGrid` (کارت‌ها)، `MudChart` (نمودار)، فیلدهای `_stats`/`_statusChartLabels`/`_statusChartSeries`، متد `UpdateStats()`، کلاس `OrderStatsViewModel` - عنوان تولبار از «سفارش‌های کاربر» به «لیست سفارشات» تغییر کرد **فایل‌ها:** - `Pages/UserOrder/UserOrderMainPage.razor` - `Pages/UserOrder/UserOrderMainPage.razor.cs` --- ### ۲.۳ بازنویسی صفحه محصولات تخفیفی **قبل:** markup سفارشی بدون `BasePageComponent`، ستون‌های ساده، بدون image preview **بعد:** کاملاً مطابق با `ProductsMainPage` فروشگاه عادی | ویژگی | قبل | بعد | |-------|-----|-----| | Wrapper | markup دستی | `BasePageComponent` | | فیلترها | جستجو + دسته‌بندی | جستجو + دسته‌بندی + وضعیت + موجودی | | ستون عنوان | متن ساده | تصویر inline (MudAvatar) + متن truncate + tooltip | | ستون موجودی | عدد ساده | چیپ رنگی (قرمز/نارنجی/سبز) | | ستون وضعیت | متن | چیپ Error/Success | | خروجی Excel | ✅ (داشت) | ✅ (حفظ شد) | | گالری تصاویر | ✅ (داشت) | ✅ (حفظ شد) | | Server-side paging | ✅ | ✅ | **فایل‌ها:** - `Pages/DiscountShop/DiscountProductsMainPage.razor` — بازنویسی کامل - `Pages/DiscountShop/DiscountProductsMainPage.razor.cs` — بازنویسی کامل (code-behind) --- ### ۲.۴ بازنویسی صفحه دسته‌بندی‌های تخفیفی **قبل:** markup دستی بدون `BasePageComponent`، ستون‌های متفاوت **بعد:** کاملاً مطابق با `CategoryMainPage` فروشگاه عادی | ویژگی | قبل | بعد | |-------|-----|-----| | Wrapper | markup دستی | `BasePageComponent` | | لایوت | درخت + گرید | درخت (3 col) + گرید (9 col) — بدون تغییر | | ستون‌ها | شناسه، عنوان، توضیحات، وضعیت | شناسه، نام لاتین، عنوان، دسته‌بندی والد، تعداد محصولات، ترتیب، فعال؟ | | ستون والد | نداشت | resolve نام والد از لیست | | ستون محصولات | نداشت | چیپ Info | | ستون ترتیب | نداشت | PropertyColumn | | فیلتر | داخل page | داخل `BasePageComponent` | | حذف با فرزند | disabled | disabled (حفظ شد) | **فایل‌ها:** - `Pages/DiscountShop/DiscountCategoriesMainPage.razor` — بازنویسی کامل - `Pages/DiscountShop/DiscountCategoriesMainPage.razor.cs` — ایجاد (code-behind جدید) --- ## ۳. فایل‌های تغییر یافته | فایل | نوع تغییر | توضیح | |------|----------|-------| | `Shared/NavMenu.razor` | ✏️ ویرایش | بازسازی ساختار فروشگاه | | `Pages/UserOrder/UserOrderMainPage.razor` | ✏️ ویرایش | حذف آمار، رفع PaymentDate | | `Pages/UserOrder/UserOrderMainPage.razor.cs` | ✏️ ویرایش | حذف فیلدها/متدهای آمار | | `Pages/DiscountShop/DiscountProductsMainPage.razor` | 🔄 بازنویسی | BasePageComponent + ستون‌های جدید | | `Pages/DiscountShop/DiscountProductsMainPage.razor.cs` | 🔄 بازنویسی | code-behind کامل | | `Pages/DiscountShop/DiscountCategoriesMainPage.razor` | 🔄 بازنویسی | BasePageComponent + ستون‌های جدید | | `Pages/DiscountShop/DiscountCategoriesMainPage.razor.cs` | 🆕 ایجاد | code-behind جدید (از @code درون‌خطی) | --- ## ۴. الگوی پیاده‌سازی — BasePageComponent تمام صفحات لیست در BackOffice از `BasePageComponent` استفاده می‌کنند: ```razor ``` **در code-behind:** ```csharp private BasePageComponent _basePage = default!; private async Task OnFilterSubmit() { _basePage.IsFiltered = true; // اعمال فیلتر } private async Task OnFilterCleared() { _basePage.IsFiltered = false; // ریست فیلترها } ``` --- ## ۵. الگوی Code-Behind به دلیل محدودیت Razor source generator در پروژه، **همه فایل‌هایی که سرویس inject دارند باید code-behind داشته باشند**: ``` Page.razor → فقط markup (بدون @code) Page.razor.cs → partial class با [Inject] و منطق ``` **نکته مهم:** سرویس‌های global از `_Imports.razor` نباید دوباره با `[Inject]` تعریف شوند: - ❌ `[Inject] public IDialogService DialogService { get; set; }` — از قبل global - ❌ `[Inject] public ISnackbar Snackbar { get; set; }` — از قبل global - ❌ `[Inject] public IJSRuntime jsRuntime { get; set; }` — از قبل global (حرف کوچک!) - ✅ `[Inject] public IDiscountProductService DiscountProductService { get; set; }` — باید inject شود --- ## ۶. مقایسه نهایی فروشگاه عادی و تخفیفی | جنبه | فروشگاه عادی | فروشگاه تخفیفی | وضعیت | |------|-------------|---------------|-------| | ارتباط با بکند | gRPC/Protobuf | HTTP REST (IDiscountXxxService) | تفاوت ذاتی | | BasePageComponent | ✅ | ✅ | 🟢 یکسان | | فیلترهای محصول | جستجو+دسته‌بندی+وضعیت | جستجو+دسته‌بندی+وضعیت+موجودی | 🟢 یکسان+ | | ستون‌های محصول | تصویر+عنوان، قیمت، موجودی (چیپ)، وضعیت (چیپ) | تصویر+عنوان، قیمت، تخفیف، موجودی (چیپ)، وضعیت (چیپ) | 🟢 یکسان+ | | گالری تصاویر | ✅ GalleryDialog | ✅ ProductImageGallery | 🟢 هر دو دارند | | خروجی Excel | ✅ | ✅ | 🟢 یکسان | | درخت دسته‌بندی | ✅ | ✅ | 🟢 یکسان | | ستون‌های دسته‌بندی | شناسه+نام+عنوان+والد+محصولات+ترتیب+فعال | شناسه+نام+عنوان+والد+محصولات+ترتیب+فعال | 🟢 یکسان | | سفارشات Hub | MudTabs (سفارشات + گزارش فروش) | MudTabs (سفارشات + گزارش فروش) | 🟢 یکسان |