# 📝 BackOffice — Changelog **آخرین بروزرسانی:** بهمن ۱۴۰۴ (February 2026) --- ## 🏷 فاز ۱۴ — فیکس 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` با Debounce 400ms - دو نوع جستجو: ۱) نام صفحات (۱۵ صفحه اصلی) ۲) کاربران از gRPC (`UserContract.GetAllUserByFilterAsync`) - Template سفارشی با آیکون + عنوان + زیرنویس - ناوبری خودکار با `Navigation.NavigateTo` - اضافه شده در `MainLayout.razor` داخل `` - 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 | شناسه,محصول,نوع,تعداد,قبل,بعد,مرجع,یادداشت,تاریخ | **الگوی پیاده‌سازی:** - دکمه `` با آیکون `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** آپدیت شدند - پیام: `موردی یافت نشد.` ### 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 - `لغو شده` اضافه شد ### 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)` اضافه شد با `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) |