Files
BackOffice/docs/BACKOFFICE-CHANGELOG.md
T
masoodafar-web 4eaca9bc4c
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled
docs: update changelog with phase 16 (site pages) and phase 17 (inventory UX)
2026-02-18 01:05:51 +03:30

851 lines
47 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📝 BackOffice — Changelog
**آخرین بروزرسانی:** اسفند ۱۴۰۴ (February 18, 2026)
---
## 🏷 فاز ۱۷ — بهبود سیستم موجودی + اتوکامپلیت تخفیفی (اسفند ۱۴۰۴)
> **هدف:** UX هوشمند ورود کالا با autocomplete اختصاصی برای هر نوع فروشگاه
### ۱۷.۱ — کامپوننت DiscountProductsAutoComplete ✅
- **فایل‌ها:** `Pages/AutoComplete/DiscountProductsAutoComplete.razor(.cs)`
- **الگو:** مشابه `ProductsAutoComplete` موجود
- **gRPC Client:** `DiscountProductContract.DiscountProductContractClient`
- **دِبانس:** ۷۰۰ms، حداکثر ۹ نتیجه
- **نکته:** `SearchQuery` از نوع `string` (نه `StringValue`)
### ۱۷.۲ — UX هوشمند AddStockDialog ✅
- **فایل:** `Pages/Inventory/Components/AddStockDialog.razor`
- **تغییر:** فیلد عددی خام شناسه محصول → autocomplete هوشمند
- **رفتار:**
- انتخاب «فروشگاه عادی» → نمایش `ProductsAutoComplete`
- انتخاب «فروشگاه اعتباری» → نمایش `DiscountProductsAutoComplete`
- ولیدیشن قبل از submit
### ۱۷.۳ — Merge به production ✅
- **برنچ:** `kub-stage``production`
- **ریموت:** `kub-stage` (Gitea)
---
## 🏷 فاز ۱۶ — ساده‌سازی صفحات سایت + ادیتورهای Shopify-style (اسفند ۱۴۰۴)
> **هدف:** مدیریت typed صفحات سایت با فرم‌های اختصاصی
### ۱۶.۱ — صفحه مدیریت PageSettings ✅
- **فایل‌ها:** `Pages/Content/PageSettingsManagementPage.razor(.cs)`
- **قابلیت‌ها:** لیست ۴ صفحه ثابت (landing/about/contact/licenses)، ویرایش تنظیمات، مدیریت تصاویر
### ۱۶.۲ — ادیتورهای اختصاصی Shopify-style ✅
| فایل | صفحه |
|------|------|
| `AboutSettingsEditor.razor` | درباره ما: ارزش‌ها + اعضای تیم |
| `ContactSettingsEditor.razor` | تماس: آدرس/تلفن/ایمیل/شبکه‌های اجتماعی |
| `LandingSettingsEditor.razor` | لندینگ: Hero + Steps + Features + Stats + FAQ + Testimonials |
| `LicensesSettingsEditor.razor` | مجوزها: JSON array |
### ۱۶.۳ — سرویس gRPC و دیالوگ‌ها ✅
- `ISitePageSettingsService` + `SitePageSettingsService`
- `PageSettingsEditDialog` + `PageImagesDialog` + `PageImageEditDialog`
- `PageSettingsModels.cs` — مدل‌های typed برای JSON
### ۱۶.۴ — Proto v0.0.180 ✅
- **تغییر:** `Foursat.CMSMicroservice.Protobuf``0.0.180`
---
## 🏷 فاز ۱۵ — Production Deploy + فیکس CI/CD Cross-Deploy (اسفند ۱۴۰۴)
> **هدف:** دیپلوی به سرور Production و رفع باگ cross-deploy
### ۱۵.۱ — Merge به برنچ production ✅
- **کامیت:** `4454b8d` — merge `kub-stage``production`
- **ریموت:** `kub-stage` (Gitea)
### ۱۵.۲ — فیکس باگ Cross-Deploy ✅
- **مشکل:** Gitea Act Runner همه workflowهای `.gitea/workflows/` را اجرا می‌کرد — بدون توجه به `branches:` filter
- **عارضه:** push به `production` باعث دیپلوی همزمان به staging و production می‌شد
- **رفع:** حذف فایل‌های workflow staging از برنچ production
- **فایل حذف شده:** `.gitea/workflows/deploy.yml` (staging) از برنچ production
### ۱۵.۳ — بروزرسانی Proto به v0.0.179 ✅
- **تغییر:** `Foursat.CMSMicroservice.Protobuf``0.0.179`
- **فایل:** `BackOffice.csproj`
### ۱۵.۴ — فیکس K8S_SERVER در prod-deploy.yml ✅
- **مشکل:** `K8S_SERVER` هنوز `194.5.195.53` (staging) بود
- **رفع:** تغییر به `45.149.79.127` (production)
- **فیکس اضافی:** `kubectl rollout restart``kubectl set image` (تا ایمیج SHA-tagged واقعاً set بشه)
### ۱۵.۵ — ساخت appsettings.Production.json ✅
- **مشکل:** فایل `appsettings.Production.json` وجود نداشت → `GwUrl` fallback به `localhost`
- **رفع:** ساخت `wwwroot/appsettings.Production.json` با `GwUrl=https://cms.kbs1.ir`
### ۱۵.۶ — فیکس nginx image path در Dockerfile (production branch) ✅
- **مشکل:** Dockerfile روی برنچ production از `library/nginx:alpine` استفاده می‌کرد — وجود نداشت
- **ارور:** `manifest for 194.5.195.53:32082/library/nginx:alpine not found`
- **رفع:** تغییر به `194.5.195.53:32082/nginx:alpine`
- **کامیت:** `743403e` (مستقیم روی برنچ production)
---
## 🏷 فاز ۱۴ — فیکس Docker Build + Deployment (بهمن ۱۴۰۴)
> **هدف:** رفع مشکلات Docker build و CI/CD pipeline
### ۱۴.۱ — تغییر به NuGet Package ✅
- **مشکل:** `ProjectReference` به `../../../CMS/src/CMSMicroservice.Protobuf/` خارج از Docker build context بود
- **رفع:** تغییر به `PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.178"`
- **فایل:** `BackOffice.csproj`
### ۱۴.۲ — حذف COPY libs/ از Dockerfile ✅
- **مشکل:** `COPY ["libs/", "libs/"]` فیل می‌شد — `libs/` خارج از build context
- **رفع:** حذف خط — دیگه نیازی به DLLهای BFF نیست
- **فایل:** `Dockerfile`
### ۱۴.۳ — تغییر nginx:alpine به رجیستری لوکال ✅
- **مشکل:** `FROM nginx:alpine` از Docker Hub → TLS handshake timeout
- **رفع:** `FROM 194.5.195.53:32082/nginx:alpine`
- **فایل:** `Dockerfile`
### ۱۴.۴ — اضافه شدن SERVER_PASSWORD secret ✅
- **مشکل:** مرحله Deploy to Kubernetes با `Permission denied` فیل می‌شد
- **رفع:** سکرت `SERVER_PASSWORD` به ریپو Gitea اضافه شد (BackOffice + FrontOffice)
---
## 🏷 فاز ۱۳ — بهبودهای UI بک‌آفیس (بهمن ۱۴۰۴)
> **هدف:** رفع مشکلات بصری، بهبود UX فرم‌ها و ناوبری
### ۱۳.۱ — فیکس کامپوننت Image (پیشوند `/uploads/`) ✅
- **مشکل:** تصاویر آپلود‌شده (بلاگ، صفحات سایت، سکشن‌ها) در بک‌آفیس نمایش داده نمی‌شدند — مسیر `GwUrl + Src` با مسیر `UploadsController` هماهنگ نبود
- **رفع:** اضافه شدن `/uploads/` بین `FileBaseUrl` و مسیر نسبی تصویر
- **فایل:** `BackOffice/Common/BaseComponents/Image.razor.cs`
### ۱۳.۲ — فیکس فیلتر هفته در گزارش‌های هفتگی ✅
- **مشکل:** فیلتر هفته از `MudTextField` (ورودی متنی) استفاده می‌کرد — کاربر باید عدد حدس می‌زد
- **رفع:** جایگزینی با `WeekNumberPicker` — انتخاب هفته از لیست با بایندینگ `WeekDefinitionId`
- **فایل‌ها:**
- `CMS/src/CMSMicroservice.Protobuf/Protos/commission.proto` — تغییر فیلدها از `StringValue` به `Int64Value`
- `CMS/.../CommissionCQ/Queries/GetAllWeeklyPools/GetAllWeeklyPoolsQuery.cs` + `Handler.cs`
- `BackOffice/Pages/Commission/WeeklyReports.razor`
### ۱۳.۳ — فیکس دانلود PDF قراردادها ✅
- **مشکل:** فیلد `SignedPdfFile` محتوای HTML کامل دارد (نه URL فایل PDF) — دکمه دانلود کار نمی‌کرد
- **رفع:** تابع JS جدید `jsHtmlToPdf()` برای باز کردن HTML در پنجره جدید و چاپ/ذخیره به‌عنوان PDF
- **فایل‌ها:**
- `BackOffice/wwwroot/js/main.js` — تابع `jsHtmlToPdf`
- `BackOffice/Pages/Contract/UserContractPage.razor`
- `BackOffice/Pages/Contract/ContractDetailsDialog.razor`
### ۱۳.۴ — یکسان‌سازی ارتفاع ویرایشگر HTML ✅
- **مشکل:** ارتفاع `MudHtmlEditor` در صفحات مختلف متفاوت بود — برخی خیلی کوتاه
- **رفع:** اضافه شدن `MinHeight="300px"` به تمام ۷ ویرایشگر HTML
- **فایل‌ها:** `BlogPostEditDialog`, `SitePageSectionEditDialog`, `DiscountShop/ProductFormDialog`, `Package/CreateDialog`, `Package/UpdateDialog`, `Products/CreateDialog`, `Products/UpdateDialog`
### ۱۳.۵ — تولید خودکار اسلاگ بلاگ ✅
- **مشکل:** کاربر باید اسلاگ URL را دستی وارد می‌کرد — غیرضروری و مستعد خطا
- **رفع:** حذف فیلد اسلاگ از فرم، تولید خودکار `post-{GUID}` در بک‌اند (حداکثر ۲۰ کاراکتر)
- **فایل‌ها:**
- `CMS/.../BlogPostCQ/Commands/CreateBlogPost/CreateBlogPostCommandHandler.cs`
- `CMS/.../BlogPostCQ/Commands/UpdateBlogPost/UpdateBlogPostCommandHandler.cs`
- `BackOffice/Pages/Blog/Components/BlogPostEditDialog.razor`
### ۱۳.۶ — انتقال تگ‌ها به منوی جداگانه ✅
- **مشکل:** تگ‌ها فقط از تب سوم بلاگ قابل دسترسی بودند — ولی تگ‌ها بین محصولات و بلاگ مشترکند
- **رفع:** اضافه شدن مسیر `/tags` به `TagManagementPage`، لینک جدید «مدیریت تگ‌ها» در منوی ناوبری، حذف تب تگ‌ها از `BlogHub`
- **فایل‌ها:**
- `BackOffice/Pages/Tag/TagManagementPage.razor`
- `BackOffice/Shared/NavMenu.razor`
- `BackOffice/Pages/Blog/BlogHub.razor`
### ۱۳.۷ — تولید خودکار کلید سکشن ✅
- **مشکل:** کاربر باید کلید سکشن صفحه سایت را دستی وارد می‌کرد (مثلاً `about-us-section`)
- **رفع:** فیلد اختیاری شد، تولید خودکار `section-{GUID}` (حداکثر ۲۰ کاراکتر) در هندلر CMS
- **فایل‌ها:**
- `CMS/.../SitePageCQ/Commands/CreateSitePageSection/CreateSitePageSectionCommandHandler.cs`
- `CMS/.../SitePageCQ/Commands/CreateSitePageSection/CreateSitePageSectionCommandValidator.cs`
- `BackOffice/Pages/Content/Components/SitePageSectionEditDialog.razor`
---
## 🏷 فاز ۱۲ — فیکس فلوی ثبت‌نام / ورود FrontOffice (بهمن ۱۴۰۴)
> **هدف:** رفع ۳ باگ بحرانی در فلوی ثبت‌نام و ورود FrontOffice
> **مستند کامل:** [`totalDoc/cms/REGISTRATION-FLOW-FIXES.md`](../../totalDoc/cms/REGISTRATION-FLOW-FIXES.md)
### ۱۲.۱ — رفع ایجاد کاربر جدید در VerifyOtpToken 🔴 ✅
- **مشکل:** `UserCQ/VerifyOtpTokenCommandHandler` کاربران جدید ایجاد نمی‌کرد — فقط خطای «کاربر یافت نشد»
- **رفع:** اضافه شدن لاجیک کامل: اعتبارسنجی کد معرف، بررسی ظرفیت درخت باینری (حداکثر ۲ فرزند)، تعیین شاخه (چپ/راست)، ایجاد User + UserRole + UserWallet، رویدادهای دامنه
- **فایل:** `CMS/src/CMSMicroservice.Application/UserCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs`
### ۱۲.۲ — رفع جستجوی موبایل در AcceptContract 🔴 ✅
- **مشکل:** `_currentUserService.Username` = `"{FirstName} {LastName}"` — نه شماره موبایل! در نتیجه OTP و کاربر هیچوقت پیدا نمی‌شد
- **رفع:** ابتدا کاربر از `UserId` پیدا شود، سپس `user.Mobile` برای جستجوی OTP استفاده شود
- **فایل:** `CMS/src/CMSMicroservice.Application/UserCQ/Commands/AcceptContract/AcceptContractCommandHandler.cs`
### ۱۲.۳ — رفع `IsCompleteRegister()` در FrontOffice 🟡 ✅
- **مشکل:** `InitUserAuthInfo().GetAwaiter()` بدون `.GetResult()` — عملیات async اجرا نمی‌شود، منوی کناری وضعیت نادرست
- **رفع:** تغییر به `InitUserAuthInfo().GetAwaiter().GetResult()`
- **فایل:** `FrontOffice/src/FrontOffice.Main/Utilities/AuthService.cs`
---
## 🏷 فاز ۱۱ — رفع خطای صفحات + سرویس عمومی تصاویر (بهمن ۱۴۰۴)
> **هدف:** رفع خطای ۳ صفحه BackOffice + ایجاد اندپوینت HTTP عمومی برای سرو تصاویر
### ۱۱.۱ — سرویس عمومی سرو تصاویر (CMS) ✅
- **`UploadsController`** — اندپوینت `GET /uploads/{path}` برای سرو مستقیم فایل‌ها
- `[AllowAnonymous]` — بدون نیاز به احراز هویت
- کش مرورگر ۲۴ ساعته (`ResponseCache`)
- پشتیبانی از Range Requests
- **FMS Fallback:** اگر فایل محلی نباشد، از `dl.afrino.co` دانلود و کش می‌شود
- `IHttpClientFactory` با named client `"FMS"` (timeout: 30s)
- **فایل‌ها:** `CMS/WebApi/Controllers/UploadsController.cs`, `CMS/WebApi/Program.cs`
### ۱۱.۲ — رفع صفحه «صفحات سایت» ✅
- اضافه شدن **دکمه «افزودن صفحه جدید»** به بالای صفحه
- اضافه شدن **دکمه «حذف»** (آیکون قرمز) برای هر ردیف
- ایجاد **`SitePageCreateDialog`** — دیالوگ ایجاد صفحه جدید با فیلدها:
- کلید صفحه (PageKey)، عنوان، توضیحات متا، عنوان هیرو، زیرعنوان هیرو، تصویر هیرو، وضعیت
- Proto: اضافه شدن `CreateSitePage` و `DeleteSitePage` RPC
- CQRS: `CreateSitePageCommand/Handler` و `DeleteSitePageCommand/Handler`
- **فایل‌ها:**
- `sitepage.proto` — 2 RPC جدید + پیام‌های Request/Response
- `SitePageCQ/Commands/CreateSitePage/` — Command + Handler
- `SitePageCQ/Commands/DeleteSitePage/` — Command + Handler
- `CMS/WebApi/Services/SitePageService.cs` — 2 override جدید
- `BackOffice/Services/Content/ISitePageService.cs``CreateAsync` + `DeleteAsync` + `SitePageCreateDto`
- `BackOffice/Services/Content/SitePageService.cs` — پیاده‌سازی Create/Delete
- `BackOffice/Pages/Content/SitePageManagementPage.razor` — دکمه‌های Add/Delete
- `BackOffice/Pages/Content/SitePageManagementPage.razor.cs` — متدهای CreatePage/DeletePage
- `BackOffice/Pages/Content/Components/SitePageCreateDialog.razor` + `.razor.cs`
### ۱۱.۳ — رفع صفحه «قراردادها» (Contracts) ✅
- **مشکل:** صفحه `/contracts` اصلاً بارگذاری نمی‌شد — crash در DI
- **علت:** `UserContractContract.UserContractContractClient` در DI ثبت نشده بود
- **رفع:** اضافه شدن `using CMSMicroservice.Protobuf.Protos.UserContract` و ثبت gRPC client
- **فایل:** `BackOffice/Common/Configure/ConfigureService.cs`
### ۱۱.۴ — رفع صفحه «کیف پول کاربران» (Wallets) ✅
- **مشکل:** صفحه `/wallets` اصلاً بارگذاری نمی‌شد — crash در DI
- **علت:** دو سرویس ثبت نشده بود:
- `UserWalletContract.UserWalletContractClient`
- `UserWalletChangeLogContract.UserWalletChangeLogContractClient`
- **رفع:** اضافه شدن using‌ها و ثبت هر دو gRPC client
- **فایل:** `BackOffice/Common/Configure/ConfigureService.cs`
---
## 🏷 فاز ۱۰ — بهبود مدیریت محتوا: بلاگ + صفحات سایت (بهمن ۱۴۰۴)
> **هدف:** فعال‌سازی آپلود تصویر باینری و ادیتور HTML در صفحات بلاگ و صفحات سایت
### ۱۰.۱ — بهبود ادیتور پست بلاگ ✅
- **`MudTextField Lines="10"`** → **`MudHtmlEditor`** برای ویرایش محتوای HTML
- **فیلد متنی مسیر تصویر** → **`MudFileUpload`** با پیش‌نمایش تصویر (`Image` component)
- حذف فیلد `FeaturedImageThumbnailPath` (تولید خودکار توسط بک‌اند)
- اضافه شدن `MudForm` با اعتبارسنجی + حالت بارگذاری (`_loading`)
- مدیریت خطا: try-catch + Snackbar
- **فایل‌ها:** `Pages/Blog/Components/BlogPostEditDialog.razor` + `.razor.cs`
### ۱۰.۲ — بهبود ادیتور صفحه سایت ✅
- **فیلد متنی Hero Image** → **`MudFileUpload`** با پیش‌نمایش تصویر
- اضافه شدن `MudForm` + حالت آپلود + مدیریت خطا
- **فایل‌ها:** `Pages/Content/Components/SitePageEditDialog.razor` + `.razor.cs`
### ۱۰.۳ — بهبود ادیتور بخش صفحه سایت (Section) ✅
- **`MudTextField Lines="8"`** → **`MudHtmlEditor`** برای ویرایش محتوای HTML بخش‌ها
- **فیلدهای متنی تصویر/thumbnail** → **`MudFileUpload`** با پیش‌نمایش
- اضافه شدن `MudForm` + حالت آپلود + مدیریت خطا
- **فایل‌ها:** `Pages/Content/Components/SitePageSectionEditDialog.razor` + `.razor.cs`
### ۱۰.۴ — تغییرات Proto ✅
| Proto | تغییر |
|-------|-------|
| `blogpost.proto` | اضافه شدن `BlogImageFileModel` + فیلد `image_file` در Create/Update |
| `sitepage.proto` | اضافه شدن `SitePageImageFileModel` + فیلد `image_file` در Update/CreateSection/UpdateSection |
### ۱۰.۵ — تغییرات CQRS (بک‌اند CMS) ✅
- **Commands:** اضافه شدن `ImageFileBytes`, `ImageMime`, `ImageFileName` به:
- `CreateBlogPostCommand`, `UpdateBlogPostCommand`
- `UpdateSitePageCommand`, `CreateSitePageSectionCommand`, `UpdateSitePageSectionCommand`
- **Handlers:** تزریق `IFileManager` + فراخوانی `UploadImageAsync`:
- BlogPost → `Images/BlogPosts/`
- SitePage → `Images/SitePages/`
- SitePageSection → `Images/SitePageSections/`
### ۱۰.۶ — تغییرات سرویس‌های BackOffice ✅
- `BlogPostService.cs` — ارسال `BlogImageFileModel` با `ByteString.CopyFrom(dto.ImageFile)`
- `SitePageService.cs` — ارسال `SitePageImageFileModel` در Update/CreateSection/UpdateSection
- **DTOs:** اضافه شدن `ImageFile`, `ImageMime`, `ImageFileName` به `BlogPostEditDto`, `SitePageEditDto`, `SitePageSectionEditDto`
### خلاصه آمار فاز ۱۰
| معیار | تعداد |
|---|---|
| فایل‌های proto تغییریافته | 2 |
| Command‌های بروزشده | 5 |
| Handler‌های بروزشده | 5 |
| سرویس‌های gRPC تغییریافته | 2 (BlogPostService + SitePageService) |
| دیالوگ‌های UI بازنویسی‌شده | 3 (BlogPost, SitePage, SitePageSection) |
| سرویس‌های BackOffice بروزشده | 2 |
| DTO‌های بروزشده | 3 |
| Build | 0 Error ✅ (هر ۳ پروژه) |
---
## 🏷 فاز ۹ — معماری مدیریت فایل + رفع سفارشات + مهاجرت DB (بهمن ۱۴۰۴)
> **مستند کامل معماری فایل:** [`totalDoc/cms/FILE-MANAGEMENT-ARCHITECTURE.md`](../../totalDoc/cms/FILE-MANAGEMENT-ARCHITECTURE.md)
### ۹.۱ — حذف کد مرده FMS ✅
- حذف ۳ فایل غیرقابل استفاده مرتبط با FMS خارجی (`https://dl.afrino.co`):
| فایل حذف‌شده | شرح |
|-------------|------|
| `Infrastructure/Services/FmsFileManager.cs` | پیاده‌سازی HTTP upload به FMS |
| `Application/Common/FileManager/FileManagementService.cs` | سرویس قدیمی مدیریت فایل |
| `Application/Common/FileManager/IFileManagementService.cs` | اینترفیس قدیمی |
### ۹.۲ — رفع صفحه سفارشات (Orders) ✅
**مشکل ۱ — صفحه خالی (بدون پیغام خطا)**
- **علت:** `ServerReload` بدون try-catch — exception‌های gRPC ساکت می‌شدند
- **رفع:** اضافه شدن try-catch + نمایش خطا با Snackbar
- **فایل:** `Pages/UserOrder/UserOrderMainPage.razor.cs`
**مشکل ۲ — Shadow FK تکراری در OrderVAT**
- **علت:** `.WithOne()` بدون inverse navigation → EF Core یک shadow FK اضافی می‌ساخت
- **رفع:** `.WithOne()``.WithOne(x => x.OrderVAT)`
- **فایل:** `Persistence/Configurations/OrderVATConfiguration.cs`
**مشکل ۳ — فیلترهای oneof همیشه فعال**
- **علت:** فیلدهای proto `oneof` مقدار پیش‌فرض enum دارند (هیچ‌وقت null نیستند)، پس `PaymentStatus != null` همیشه true بود
- **رفع:** `PaymentStatus != null``HasPaymentStatus == true` (و مشابه برای DeliveryStatus و PaymentMethod)
- **فایل‌ها:** `UserOrderService.cs` — هر دو متد `GetAllUserOrderByFilter` و `GetCustomerOrders`
### ۹.۳ — مهاجرت DB: حذف محدودیت طول ستون‌های تصویر ✅
- **علت:** data URI‌های base64 بزرگ‌تر از `nvarchar(500)` بودند → truncation خاموش
- **رفع:** حذف `HasMaxLength(500)` از تمام ستون‌های image path
- **Migration:** `RemoveImagePathMaxLength`
| Entity | ستون‌ها |
|--------|---------|
| Product | `ImagePath`, `ImageThumbnailPath` |
| DiscountProduct | `ImagePath`, `ImageThumbnailPath` |
| Category | `ImagePath` |
| DiscountCategory | `ImagePath` |
| BlogPost | `FeaturedImagePath`, `FeaturedImageThumbnailPath` |
| SitePage | `HeroImagePath` |
| SitePageSection | `ImagePath`, `ImageThumbnailPath` |
### ۹.۴ — بازنویسی کامل مدیریت فایل: ذخیره دیسکی ✅
**معماری قبلی:** ذخیره data URI مستقیم در DB (ناکارآمد + حجم بالا)
**معماری جدید:**
1. فایل‌ها در دیسک ذخیره می‌شوند: `Uploads/Images/{folder}/{guid}.jpg`
2. مسیر نسبی در DB ذخیره می‌شود
3. `ImagePathResolverInterceptor` هنگام serve تبدیل به base64 می‌کند
**فایل‌های اصلی:**
| فایل | شرح |
|------|------|
| `Application/Common/FileManager/IFileManager.cs` | اینترفیس: Upload, UploadImage, Delete, ResolveImageUrl |
| `Infrastructure/Services/LocalFileManager.cs` | پیاده‌سازی: ImageSharp, main 1200×1200 + thumb 300×300, JPEG Q75 |
| `WebApi/Interceptors/ImagePathResolverInterceptor.cs` | gRPC Interceptor: walk بازگشتی response → تبدیل path به data URI |
**تنظیمات:**
- `FileStorage:UploadPath` → پوشه ریشه آپلود (پیش‌فرض: `AppContext.BaseDirectory/Uploads`)
- gRPC MaxReceiveMessageSize: 50MB
### ۹.۵ — پاکسازی LoggingBehaviour ✅
- **قبل:** فیلدهای باینری (byte[] تصاویر) کامل در لاگ چاپ می‌شدند → لاگ‌های چند مگابایتی
- **بعد:**
- فرمت: `JsonFormatter.Default.Format()` به جای `{@Request}`
- Regex: حذف فیلدهای باینری (`File`, `ImageFile`, `image_file`, `file`)
- محدودیت: حداکثر 2000 کاراکتر
- **فایل:** `WebApi/Common/Behaviours/LoggingBehaviour.cs`
### خلاصه آمار فاز ۹
| معیار | تعداد |
|---|---|
| فایل‌های حذف‌شده (کد مرده) | 3 |
| فایل‌های جدید | 2 (LocalFileManager بازنویسی + ImagePathResolverInterceptor) |
| فایل‌های ویرایش‌شده | ~12 |
| باگ‌های رفع‌شده | 4 (Orders page ×3 + DB truncation) |
| Migration‌های جدید | 1 (RemoveImagePathMaxLength) |
| ستون‌های DB تغییریافته | 9 (nvarchar(500) → nvarchar(max)) |
| Build | 0 Error ✅ (هر ۳ پروژه) |
---
## 🏷 فاز ۸ — یکسان‌سازی فروشگاه عادی و تخفیفی (بهمن ۱۴۰۴)
> **مستند کامل:** [`totalDoc/ui-modernization/BACKOFFICE-STORE-UNIFICATION.md`](../../totalDoc/ui-modernization/BACKOFFICE-STORE-UNIFICATION.md)
### ۸.۱ — بازسازی NavMenu ✅
- ایجاد دو NavGroup مجزا: «فروشگاه عادی» و «فروشگاه تخفیفی»
- حذف «ویرایش دسته‌جمعی» از منو
- انبارداری → گروه مستقل | پکیج‌ها → آیتم مستقل
- **فایل:** `Shared/NavMenu.razor`
### ۸.۲ — حذف گزارش‌های کوچک از سفارشات ✅
- حذف کارت‌های آماری (TotalOrders + TotalAmount) و نمودار Bar ارسال
- حذف فیلدها: `_stats`, `_statusChartLabels`, `_statusChartSeries`
- حذف متد `UpdateStats()` و کلاس `OrderStatsViewModel`
- عنوان: «سفارش‌های کاربر» → «لیست سفارشات»
- **فایل‌ها:** `Pages/UserOrder/UserOrderMainPage.razor` + `.razor.cs`
### ۸.۳ — رفع باگ لیست خالی سفارشات ✅
- **علت:** `PaymentDate.ToDateTime()` بدون null check → exception ساکت در WASM
- **رفع:** `@if (context.Item.PaymentDate != null)` + fallback "-"
- **فایل:** `Pages/UserOrder/UserOrderMainPage.razor`
### ۸.۴ — بازنویسی صفحه محصولات تخفیفی ✅
- تبدیل به `BasePageComponent` (مطابق فروشگاه عادی)
- فیلترهای جدید: وضعیت + موجودی
- ستون‌های جدید: تصویر inline + عنوان truncate + چیپ رنگی موجودی + چیپ وضعیت
- **فایل‌ها:** `Pages/DiscountShop/DiscountProductsMainPage.razor` + `.razor.cs` (بازنویسی کامل)
### ۸.۵ — بازنویسی صفحه دسته‌بندی‌های تخفیفی ✅
- تبدیل به `BasePageComponent` (مطابق فروشگاه عادی)
- ستون‌های جدید: نام لاتین، دسته‌بندی والد (resolve شده)، تعداد محصولات، ترتیب
- code-behind جدید (`@code` درون‌خطی → `.razor.cs`)
- **فایل‌ها:** `Pages/DiscountShop/DiscountCategoriesMainPage.razor` + `.razor.cs` (بازنویسی کامل)
### خلاصه آمار فاز ۸
| معیار | تعداد |
|---|---|
| فایل‌های بازنویسی‌شده | 4 |
| فایل‌های ویرایش‌شده | 3 |
| باگ‌های رفع‌شده | 2 (PaymentDate null + لیست خالی) |
| Build | 0 Error ✅ |
---
## 🏷 فاز ۷ — پس از اجرای Audit v7.0 (تیر ۱۴۰۴)
### خلاصه تغییرات
- **۵۷ مورد** از ۵۸ یافته audit اجرا شد (≈98%)
- **تمام ۱۶ مورد بحرانی 🔴** حل شد
- **تمام ۱۸ مورد جزئی 🟢** حل شد
- **فازهای ۱ تا ۶ تکمیل شدند** (به‌جز DayaLoan و Audit Trail که نیاز به بک‌اند دارند)
- **فاز ۷** — Global Search, Notification Bell, NavMenu Badge, Export Excel 4 صفحه جدید
- **۱۳+ فایل جدید** ساخته شد
- **۹ صفحه** ادغام شدند (۱۴ صفحه → ۵ صفحه)
- **Build**: 0 error ✅
---
## فاز ۷ — Global Search + Notifications + Export All + NavMenu Badge (v7.0)
### 6.5 — Badge Pending در NavMenu — ✅
- **`NavMenu.razor`**: اضافه شدن `MudBadge` روی لینک «درخواست‌های برداشت»
- نمایش تعداد درخواست‌های Pending بصورت real-time از gRPC (`CommissionContract.GetWithdrawalRequestsAsync` با `Status=0`)
- fire-and-forget در `OnInitializedAsync` با try/catch silent
### 6.2 — Global Search در AppBar — ✅
- **فایل جدید**: `Shared/GlobalSearch.razor`
- `MudAutocomplete<SearchResultItem>` با Debounce 400ms
- دو نوع جستجو: ۱) نام صفحات (۱۵ صفحه اصلی) ۲) کاربران از gRPC (`UserContract.GetAllUserByFilterAsync`)
- Template سفارشی با آیکون + عنوان + زیرنویس
- ناوبری خودکار با `Navigation.NavigateTo`
- اضافه شده در `MainLayout.razor` داخل `<AuthorizeView>`
- CSS در `app.css` — rounded border-radius 24px, compact padding
### 6.1 — Notification Bell (placeholder) — ✅
- **`MainLayout.razor`**: اضافه شدن `MudMenu` + `MudBadge` (Dot) + آیکون Notifications
- Dropdown با هدر «اعلان‌ها» + placeholder «اعلان جدیدی وجود ندارد»
- آماده اتصال به بک‌اند — فقط `Visible=true` و populate items
### 6.3 — Export Excel صفحات جدید — ✅ (۱۰ صفحه کامل)
- **۴ صفحه جدید** اضافه شدند به ۶ صفحه قبلی:
1. **UserMainPage** — خروجی CSV: شناسه, موبایل, نام, نام خانوادگی, کدملی
2. **DiscountOrdersMainPage** — خروجی CSV: شماره سفارش, تاریخ, مبلغ کل, تخفیف, قابل پرداخت, تعداد, وضعیت, پرداخت
3. **ManualPayments** — خروجی CSV: شناسه, شناسه کاربر, نام کاربر, مبلغ, نوع, وضعیت, تاریخ
4. **InventoryMainPage** — خروجی CSV: شناسه, محصول, انبار, موجودی, رزرو, قابل فروش, حد هشدار, وضعیت
- هر ۴ صفحه: دکمه «خروجی Excel» + `ExportToExcel()` + `EscapeCsv()` + UTF-8 BOM + `jsSaveAsFile`
- فایل‌های code-behind جدید: `ManualPayments.razor.cs`, `DiscountOrdersMainPage.razor.cs`
---
## فاز ۶ — تاریخ شمسی + تاریخچه‌ها + Export Excel (v6.0)
### تاریخ شمسی یکپارچه (3.6) — ✅ کامل
- **۱۹+ فایل** آپدیت شدند: تمام تاریخ‌ها از فرمت میلادی (`yyyy/MM/dd`) به شمسی تبدیل شدند
- از extension method‌های `DateTimeConverterCL` استفاده شد:
- `.MiladiToJalali()` — فرمت تاریخ فقط (مثال: ۱۴۰۴/۰۴/۱۲)
- `.MiladiToJalaliWithTime()` — فرمت تاریخ و ساعت
- **صفحات اصلاح‌شده شامل**: Club (Members, Statistics, Features, MemberDetailsDialog)، Commission (Dashboard, WeeklyReports, WithdrawalRequests, Payouts, WithdrawalReports)، Network (UserNetworkInfo, BalancesReport, Statistics, NetworkTreeViewer)، UserOrder, Products, Package, Inventory, DiscountShop, Blog, Content, و غیره
- `PropertyColumn Format="yyyy/MM/dd"``TemplateColumn` با `.MiladiToJalali()`
- `MudDatePicker DateFormat` تغییر نکرد (قابلیت تقویم شمسی نیاز به کتابخانه جداگانه دارد)
### تاریخچه عضویت باشگاه (5.7) — ✅
- **`MemberDetailsDialog.razor`**: تب تاریخچه با فیلدهای صحیح proto:
- ستون‌ها: `Action`, `Created`, `PackageName`, `Reason`, `PerformedBy`
- Request: `PageIndex = 0` (نه PageNumber)
### تاریخچه تغییرات شبکه (5.8) — ✅
- **`UserNetworkInfo.razor`**: تب تاریخچه با فیلدهای تخصصی:
- ستون‌ها: `Action`, `Created`, `OldParentId → NewParentId`, `OldNetworkLeg → NewNetworkLeg` (چپ/راست)
- ستون `Reason` و `PerformedBy` اضافه شد
- Request: `PageIndex = 0`
### لاگ اجرای Worker‌ها (5.6) — ✅
- در تب WorkerControl از SystemHub قبلاً پیاده‌سازی شده بود
### Export Excel / CSV (6.3) — ✅ ۶ صفحه
قابلیت خروجی CSV (سازگار با Excel) به ۶ صفحه اصلی اضافه شد:
| صفحه | فایل(ها) | ستون‌های خروجی |
|---|---|---|
| **Products** (قبلاً موجود) | ProductsMainPage.razor.cs | — |
| **Club Members** | ClubMembers.razor + .razor.cs | شناسه,کاربر,نام,پکیج,کد فعال‌سازی,تاریخ فعال‌سازی,تاریخ انقضا,فعال,منقضی |
| **Withdrawal Requests** | WithdrawalRequests.razor + .razor.cs | شناسه,کاربر,نام,مبلغ,وضعیت,روش,شبا,تاریخ درخواست,تاریخ پردازش,مرجع بانکی |
| **Weekly Reports** | WeeklyReports.razor + .razor.cs | هفته,مبلغ استخر,موجودی‌ها,ارزش,وضعیت,تاریخ محاسبه |
| **User Orders** | UserOrderMainPage.razor + .razor.cs | شناسه,کاربر,نام,مبلغ,وضعیت پرداخت,وضعیت ارسال,روش پرداخت,تاریخ |
| **Stock Movements** | MovementsPage.razor + .razor.cs | شناسه,محصول,نوع,تعداد,قبل,بعد,مرجع,یادداشت,تاریخ |
**الگوی پیاده‌سازی:**
- دکمه `<MudButton>` با آیکون `FileDownload` در ToolBarContent
- `StringBuilder` → CSV با هدرهای فارسی
- UTF-8 BOM (`Encoding.UTF8.GetPreamble()`) برای نمایش صحیح فارسی در Excel
- `jsSaveAsFile(filename, base64)` از `wwwroot/js/main.js`
- متد `EscapeCsv()` برای مقادیر حاوی کاما/کوتیشن
- تاریخ‌ها در خروجی نیز شمسی هستند
### فایل‌های جدید (این نسخه)
| فایل | عملکرد |
|---|---|
| `Pages/Commission/WeeklyReports.razor.cs` | Code-behind برای export (به‌دلیل محدودیت Razor parser با escaped quotes) |
---
## نسخه قبلی — Audit v5.0 (خرداد ۱۴۰۴)
### تم رنگی یکپارچه
- **`CustomMudTheme.cs`**: تم Primary از `#0380C0` به `#6366f1` (Indigo) تغییر کرد — هماهنگ با FrontOffice
- **`PaletteDark`** اضافه شد: Surface `#1a1a2e`, Background `#16213e`, AppbarBackground `#0f3460`
- **`app.css`**: رنگ loading از `#1b6ec2` به `var(--mud-palette-primary)` تغییر کرد
- **`NavMenu.razor.css`**: رنگ active از `#0380C0` هاردکد به CSS variable تغییر کرد
- **`MainLayout.razor`**: حذف inline style `border: 1px solid #5e4df9`
### Dark Mode Toggle
- آیکون ماه/آفتاب در AppBar اضافه شد
- `_isDarkMode` state با `MudThemeProvider` اتصال دارد
- تعویض بین `PaletteLight` و `PaletteDark` در CustomMudTheme
### عنوان اپلیکیشن
- "پنل مدیریت **فرصت**" → "پنل مدیریت **کارا بازار سلامت**"
---
## فاز ۲ — بازطراحی لایوت صفحات
### BasePageComponent بازنویسی کامل
- **قبل**: فیلتر ثابت سمت راست (20%) + جدول (80%)، ارتفاع 85vh هاردکد
- **بعد**: فیلتر collapsible بالای جدول، فیلدها در `MudGrid` با `MudItem`، responsive
- **۱۳ صفحه** آپدیت شدند: User, Products, Package, Category, UserOrder, UserAddress, UserRole, Role, BlogPost, BlogCategory, Tag, Transactions, Content
### DataGrid Height یکسان‌سازی
- همه DataGrid‌ها: `Height="calc(100vh - 240px)"`
- **صفحات اصلاح‌شده**: تمام ~33 صفحه دارای DataGrid
### Pager یکسان‌سازی
- **PageSize**: همه `20, 50, 100`
- **متن فارسی**: `RowsPerPageString="تعداد در صفحه"` و `InfoFormat="سطر {first_item} تا {last_item} از {all_items}"`
### MaxWidth یکسان‌سازی
- `MainLayout.razor` MudContainer wrapper اضافه شد → MaxWidth یکسان برای همه صفحات
- **۳۱ صفحه**: حذف `MudContainer` تکراری (double nesting)
### Breadcrumb
- **`Shared/AppBreadcrumb.razor`** ساخته شد
- جداکننده چپ‌به‌راست (`ChevronLeft`)
- نقشه فارسی URL segments (60+ مسیر)
- آخرین آیتم disabled (صفحه فعلی)
### Login + VerifyCode بازطراحی
- عرض ثابت 35% → responsive `MaxWidth.Small`
- لوگو + برندینگ "کارا بازار سلامت" اضافه شد
- طراحی gradient background
---
## فاز ۲.۵ — پاکسازی ساختاری
### NavMenu بازسازی کامل
- **لینک مرده `categories-dragdrop`**: حذف شد
- **Transactions**: حذف از NavMenu (صفحه stub خالی)
- **AlertsMonitoring**: حذف از NavMenu (100% mock)
- **WorkerControl**: به SystemHub منتقل شد (قبلاً لینک نداشت)
- **«پیام‌های عمومی»**: از بخش فروشگاه تخفیفی به «مدیریت محتوا» منتقل شد
- **ساختار گروه‌بندی** اصلاح شد:
- کمیسیون و شبکه: NavGroup واضح
- باشگاه مشتریان: اعضا و آمار + فیچرها
- فروشگاه: پکیج‌ها، محصولات (group)، سفارش‌ها، فروشگاه تخفیفی (group)
- مدیریت: کاربران، نقش‌ها، پرداخت دستی، **کیف‌پول (جدید)**، **قراردادها (جدید)**
- محتوا: بلاگ، صفحات سایت، پیام‌های عمومی
- سیستم: مدیریت سیستم، نسخه اپ‌ها
---
## فاز ۳ — UX بهبودها
### NoRecordsContent (Empty State)
- **۳۳ DataGrid** آپدیت شدند
- پیام: `<MudAlert Severity="Info">موردی یافت نشد.</MudAlert>`
### Delete Confirmation با نام آیتم
- **۱۱ صفحه** اصلاح شدند:
- Role → `«{model.Title}»`
- User → `«{model.FirstName} {model.LastName}»`
- Package → `«{model.Title}»`
- UserAddress → `«{model.Title}»`
- UserRole → `شناسه «{model.Id}»`
- UserOrder → `شماره «{model.Id}»`
- Category → `«{model.Title}»`
- Products → `«{model.Title}»`
- DiscountCategories → `«{category?.Title}»` (lookup اضافه شد)
- DiscountProducts → `«{product?.Title}»` (lookup اضافه شد)
- PublicMessages → `«{message?.Title}»` (lookup اضافه شد)
### User Menu در AppBar
- `MudMenu` جایگزین متن ساده شماره موبایل شد
- آیتم‌ها: نام کاربر (غیرفعال) + خروج
- آیکون `AccountCircle`
- خروج (`signout`) از NavMenu به AppBar منتقل شد
---
## فاز ۳.۵ — ادغام صفحات
### Dashboard Merge ← `Pages/Index.razor`
- **`Index.razor` + `SystemOverview.razor`** ادغام شدند
- Routes: `/` + `/dashboard/overview`
- بخش‌ها:
- 6 کارت آمار CMS (کاربران، محصولات، پکیج‌ها، دسته‌بندی‌ها، سفارش‌ها، مبلغ پرداخت‌شده)
- آمار کمیسیون (هفته جاری، استخر، موجودی‌ها، وضعیت محاسبه)
- آمار باشگاه (کل/فعال/غیرفعال)
- جدول آخرین ۵ سفارش پرداخت‌شده
- ۶ دکمه Quick Action
- بارگذاری موازی با `Task.WhenAll` + `.ResponseAsync`
- `SystemOverview.razor` **حذف شد**
### DiscountShopHub ← `Pages/DiscountShop/DiscountShopHub.razor`
- Routes: `/discount-shop`, `/discount-orders`, `/discount-sales-reports`
- ۲ تب: DiscountOrdersMainPage + SalesReports
- انتخاب تب از URL
### ClubHub ← `Pages/Club/ClubHub.razor`
- Routes: `/club`, `/club/members`, `/club/statistics`
- ۲ تب: ClubMembers + Statistics
### BlogHub ← `Pages/Blog/BlogHub.razor`
- Routes: `/blog`, `/blog/posts`, `/blog/categories`, `/tags`
- ۳ تب: BlogPostManagementPage + BlogCategoryManagementPage + TagManagementPage
### SystemHub ← `Pages/SystemManagement/SystemHub.razor`
- Routes: `/system`, `/system/configuration`, `/system/worker-control`, `/system/health`
- ۳ تب: Configuration + WorkerControl + HealthDashboard
### ۱۰ صفحه — `@page` directive حذف شد
صفحاتی که به hub منتقل شدند دیگر route مستقل ندارند:
DiscountOrdersMainPage, SalesReports, ClubMembers, Statistics, BlogPostManagementPage, BlogCategoryManagementPage, TagManagementPage, Configuration, WorkerControl, HealthDashboard
---
## فاز ۴ — فیکس‌های بیزینسی
### Dashboard آمار اصلاح شد
- **قبل**: آمار از ۱۰ رکورد اول محاسبه می‌شد (نادرست)
- **بعد**: از `metadata.TotalCount` و API‌های جداگانه استفاده می‌شود
### ManualPayments — تأیید/رد
- دکمه‌های Approve/Reject وقتی `Status==0` (در انتظار) نمایش داده می‌شوند
- Confirmation dialog قبل از اجرا
- از `ManualPaymentId` (نه `Id`) استفاده می‌شود
### UserOrder — فیلتر Cancelled
- `<MudSelectItem Value="@(5)">لغو شده</MudSelectItem>` اضافه شد
### WithdrawalRequests — بهبود کامل
- **Pager**: 10/25/50/100 → 20/50/100
- **`WithdrawalDetailsDialog.razor`** (جدید): نمایش کامل اطلاعات کاربر، درخواست، و بانکی
- **`RejectReasonDialog.razor`** (جدید): ورود دلیل رد با اعتبارسنجی
- ViewDetails: از Snackbar TODO به دیالوگ واقعی تغییر کرد
- RejectRequest: از دلیل هاردکد به دیالوگ custom تغییر کرد
---
## فاز ۵ — صفحات مفقود (CMS-Derived)
### WalletManagementPage ← `Pages/Wallet/WalletManagementPage.razor`
- Route: `/wallets`
- **تب ۱ — کیف‌پول‌ها**: DataGrid با ServerData، فیلتر UserAutoComplete
- ستون‌ها: شناسه، کاربر، موجودی اصلی، موجودی شبکه، مشاهده تغییرات
- نوع: `GetAllUserWalletByFilterResponseModel`
- **تب ۲ — تاریخچه تغییرات**: DataGrid با ServerData، فیلتر کاربر + نوع (افزایش/کاهش)
- ستون‌ها: شناسه، کیف‌پول، نوع (چیپ رنگی)، تغییر موجودی، موجودی فعلی، تغییر شبکه، موجودی شبکه فعلی، شناسه مرجع، تاریخ
- نوع: `GetAllUserWalletChangeLogByFilterResponseModel`
- فیلدهای proto با typo: `ChangeNerworkValue`, `RefrenceId`
### UserContractPage ← `Pages/Contract/UserContractPage.razor`
- Route: `/contracts`
- DataGrid با ServerData، فیلتر UserAutoComplete
- ستون‌ها: شناسه، کاربر، قرارداد، کد امضا، لینک PDF، جزئیات
- نوع: `GetAllUserContractByFilterResponseModel`
### ContractDetailsDialog ← `Pages/Contract/ContractDetailsDialog.razor`
- جدول ساده: شناسه، کاربر، قالب قرارداد، کد امضا، لینک دانلود PDF
### ClubFeaturesPage ← `Pages/Club/ClubFeaturesPage.razor`
- Route: `/club/features`
- **تب ۱ — فیچرهای تعریف‌شده**: کارت‌های MudPaper با عنوان، توضیحات، وضعیت (فعال/غیرفعال)، ترتیب
- از `ConfigClient.GetClubFeaturesAsync`
- **تب ۲ — فیچرهای کاربران**: DataGrid با فیلتر کاربر، سوئیچ فعال/غیرفعال
- از `ClubClient.GetUserClubFeaturesAsync` + `ToggleUserClubFeatureAsync`
---
## ⚠️ نکات فنی مهم
### الگوهای Proto
- **فیلتر**: `new GetAllXxxByFilterFilter()` (سطح بالا، بدون `.Types.`)
- **پاسخ لیست**: `GetAllXxxByFilterResponseModel` (نه `GetXxxResponse`)
- **Typo‌ها در proto**: `ChangeNerworkValue` و `RefrenceId` — باید دقیقاً همین‌ها استفاده شوند
### _Imports.razor — سرویس‌های Global
سرویس‌های زیر از `_Imports.razor` تزریق می‌شوند و نباید در code-behind با `[Inject]` دوباره تعریف شوند:
- `NavigationManager Navigation`
- `ILocalStorageService LocalStorageService`
- `ISnackbar Snackbar`
- `IJSRuntime jsRuntime`
- `IDialogService DialogService`
- `AuthenticationStateProvider`
### MudBlazor محدودیت‌ها
- `Variant.Flat` وجود ندارد → از `Variant.Text` استفاده شود
- `GutterSize` روی `MudGrid` deprecated است
---
## فاز ۴ (ادامه) — تکمیل فیکس‌های بیزینسی
### UserSettings — حذف تب‌های جعلی (2.5.5)
- **`UserSettings.razor`**: تب Notifications (۷ تاگل جعلی بدون بک‌اند) → placeholder «به‌زودی فعال می‌شود» با آیکون NotificationsActive
- **`UserSettings.razor`**: تب Security (فرم تغییر رمز + 2FA بدون بک‌اند) → placeholder «به‌زودی فعال می‌شود» با آیکون Lock
- **حذف کد مرده**: ~۱۵ متغیر notification/security + متدهای `SaveNotificationSettings()`, `SaveSecuritySettings()`, `ChangePassword()`
### HealthDashboard — حذف متریک‌های Mock (2.5.6)
- **`HealthDashboard.razor`**: بخش System Resources (۴ کارت CPU=45.2%, Memory=62.8%, Disk=73.5%, Network) → یک کارت با پیام «مانیتورینگ منابع سیستم در نسخه‌های آینده با اتصال به Prometheus/Grafana فعال خواهد شد»
- **حذف کد مرده**: ۹ متغیر mock (`_cpuUsage`, `_memoryUsage`, etc.) + متد `GetResourceColor()`
### حذف cursor:pointer زائد (4.10)
- `Style="cursor:pointer;"` از **۱۷ فایل** حذف شد (MudButton/MudIconButton خودشان cursor:pointer دارند)
- شامل: ProductsMainPage, GalleryDialog, Image.razor (base component), RoleMainPage, UserOrderMainPage, PackageMainPage, etc.
### حذف Console.WriteLine (4.11)
- از **۱۱ فایل** حذف شد: DiscountProductService, DiscountCategoryService, NetworkTreeViewer, UserSettings, CategoryMainPage, UserOrderMainPage, Commission/Dashboard, DiscountProductsMainPage, DiscountCategoryMultiSelectCombo, UserRole/CreateDialog
- **`Commission/Dashboard.razor.cs`**: `using System.Text.Json` هم حذف شد (دیگر استفاده نمی‌شد)
### DiscountProducts — فیلتر دسته‌بندی (4.7)
- **`DiscountProductsMainPage.razor`**: `@inject IDiscountCategoryService` اضافه شد
- دسته‌بندی‌های فعال از سرویس بارگذاری و در MudSelect نمایش داده می‌شوند
- جایگزینی `@* TODO: Load categories *@` با `@foreach` واقعی
### DiscountProducts — Items → ServerData (4.8)
- **`DiscountProductsMainPage.razor`**: تبدیل از `Items="@_products"` به `ServerData="LoadServerData"`
- متد `LoadServerData(GridState<T>)` اضافه شد با `PageNumber` و `PageSize` از state
- `Height="calc(100vh - 300px)"` + `FixedHeader="true"` اضافه شد
- Snackbar اطلاع‌رسانی اضافی در هر بارگذاری حذف شد (redundant)
### UserOrder — فعال‌سازی آمار (4.6)
- **`UserOrderMainPage.razor`**: بخش آمار (MudCard + MudChart) از حالت comment خارج شد
- نمایش تعداد سفارش‌ها + مجموع مبلغ + نمودار Bar وضعیت ارسال
### UserSettings — persist با localStorage (4.9)
- تب General از قبل با localStorage کار می‌کرد ✅ (فقط تب‌های جعلی حذف شدند)
---
## فاز ۴.۵ — Cross-Navigation
### لینک نام کاربر → پروفایل (4.5.1, 4.5.2)
- **`UserOrderMainPage.razor`**: ستون `UserFullName` از PropertyColumn به TemplateColumn تبدیل شد — MudLink به `/network/user-info/{UserId}`
- **`UserPayouts.razor`**: نام/شناسه کاربر به MudLink تبدیل شد → `/network/user-info/{UserId}`
- **`WithdrawalRequests.razor`**: نام کاربر به MudLink تبدیل شد → `/network/user-info/{UserId}`
### لینک محصول ↔ انبار (4.5.3, 4.5.4) — Skip
- صفحه جزئیات محصول وجود ندارد (فقط لیست + دیالوگ ویرایش)
- لینک cross-navigation بدون صفحه مقصد معنادار نیست
### Extract shared status helpers (3.5.6) — Skip
- بررسی نشان داد هر context مقادیر متفاوتی دارد:
- UserPayouts: status 1 = Success/پرداخت شده
- WithdrawalRequests: status 1 = Info/واریز شده
- PayoutDetailsDialog: فقط ۳ وضعیت (نه ۶)
- استخراج به helper مشترک باعث از دست رفتن معنای domain-specific می‌شود
---
## 📈 آمار تغییرات
| معیار | تعداد |
|---|---|
| فایل‌های جدید | 11 |
| فایل‌های حذف‌شده | 1 (SystemOverview.razor) |
| فایل‌های ویرایش‌شده | ~65+ |
| DataGrid‌های آپدیت‌شده | 33 |
| Delete confirmation اصلاح‌شده | 11 |
| صفحات ادغام‌شده | 14 → 5 |
| خطاهای build حل‌شده | 9+ |
| Console.WriteLine حذف‌شده | 22+ (از ۱۱ فایل) |
| cursor:pointer حذف‌شده | 17 فایل |
| Cross-link اضافه‌شده | 3 صفحه (UserOrder, UserPayouts, WithdrawalRequests) |