Refactor code structure for improved readability and maintainability

This commit is contained in:
masoodafar-web
2026-02-16 00:59:16 +03:30
parent 956a9ff6d6
commit ad31c8be97
10 changed files with 3388 additions and 3 deletions
@@ -0,0 +1,195 @@
# 🏪 یکسان‌سازی فروشگاه عادی و تخفیفی — 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
<BasePageComponent @ref="_basePage" OnClearFilterClick="OnFilterCleared" OnSubmitClick="OnFilterSubmit">
<Filters>
<!-- فیلدهای فیلتر در MudItem -->
</Filters>
<Content>
<!-- MudDataGrid اصلی -->
</Content>
</BasePageComponent>
```
**در 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 (سفارشات + گزارش فروش) | 🟢 یکسان |