# Session Log - December 20, 2025

## خلاصه Session

این session شامل رفع چندین باگ در صفحات BackOffice و فعال‌سازی قابلیت‌های Products بود.

---

## 1. فیکس صفحه `/network/balances`

### مشکل
```
ValidationException هنگام لود صفحه بالانس‌های هفتگی
```

### علت
Mapster mapping برای تبدیل `GetUserWeeklyBalancesRequest` به `GetUserWeeklyBalancesQuery` وجود نداشت.

### راه‌حل
اضافه کردن mapping در `CommissionProfile.cs`:

```csharp
// File: BackOffice.BFF/src/BackOffice.BFF.WebApi/Common/Mappings/CommissionProfile.cs

config.NewConfig<GetUserWeeklyBalancesRequest, GetUserWeeklyBalancesQuery>()
    .Map(dest => dest.PaginationState, src => src.PaginationState);
```

---

## 2. فیکس صفحه `/club/members`

### مشکل
```
صفحه لود می‌شد ولی هیچ داده‌ای نمایش نمی‌داد
```

### علت
Mapster mappings در CMS و BFF برای `GetAllClubMemberships` وجود نداشتند.

### راه‌حل
ایجاد `ClubMembershipProfile.cs` در هر دو لایه:

**CMS/src/CMSMicroservice.WebApi/Common/Mappings/ClubMembershipProfile.cs** (NEW):
```csharp
public class ClubMembershipProfile : IRegister
{
    void IRegister.Register(TypeAdapterConfig config)
    {
        // GetAllClubMemberships mappings
        config.NewConfig<GetAllClubMembershipsRequest, GetAllClubMembershipsQuery>()
            .Map(dest => dest.PaginationState, src => src.PaginationState)
            .Map(dest => dest.Filter, src => src.Filter);

        config.NewConfig<GetAllClubMembershipsResponseDto, GetAllClubMembershipsResponse>()
            .MapWith(src => new GetAllClubMembershipsResponse
            {
                MetaData = src.MetaData != null ? new CMSMicroservice.Protobuf.Common.MetaData
                {
                    PageIndex = src.MetaData.PageIndex,
                    TotalPages = src.MetaData.TotalPages,
                    TotalCount = src.MetaData.TotalCount
                } : null,
                Models = { src.Models?.Select(...) ?? Enumerable.Empty<...>() }
            });
    }
}
```

**BackOffice.BFF/src/BackOffice.BFF.WebApi/Common/Mappings/ClubMembershipProfile.cs** (REWRITTEN):
- Mapping از BFF Proto به Query
- Mapping از CMS Response به BFF Proto Response
- استفاده از alias imports برای disambiguation

---

## 3. فیکس صفحه `/club/statistics`

### مشکل
```
Status(StatusCode="Unimplemented", Detail="Method cms.ClubMembershipContract/GetClubStatistics is unimplemented")
```

### علت
متد `GetClubStatistics` در BFF Service override نشده بود.

### راه‌حل

**1. اضافه کردن override در ClubMembershipService.cs:**
```csharp
public override async Task<GetClubStatisticsResponse> GetClubStatistics(
    GetClubStatisticsRequest request, ServerCallContext context)
{
    return await _dispatchRequestToCQRS.Handle<GetClubStatisticsRequest, GetClubStatisticsQuery, GetClubStatisticsResponse>(request, context);
}
```

**2. اضافه کردن mappings در CMS ClubMembershipProfile:**
```csharp
config.NewConfig<GetClubStatisticsRequest, GetClubStatisticsQuery>();

config.NewConfig<GetClubStatisticsResponseDto, GetClubStatisticsResponse>()
    .MapWith(src => new GetClubStatisticsResponse
    {
        TotalMembers = src.TotalMembers,
        ActiveMembers = src.ActiveMembers,
        // ... سایر فیلدها
        PackageDistribution = { src.PackageDistribution?.Select(...) },
        MonthlyTrend = { src.MonthlyTrend?.Select(...) }
    });
```

**3. اضافه کردن mappings در BFF ClubMembershipProfile:**
- Mapping از BFF Proto به Query  
- Mapping از CMS Response DTO به BFF Proto Response

---

## 4. فعال‌سازی قابلیت‌های Products

### مشکل
```
همه دکمه‌های صفحه محصولات "در حال توسعه" نشان می‌دادند
```

### علت
کدهای دیالوگ‌ها comment شده بودند با TODO markers.

### راه‌حل
Uncomment کردن کدها در `ProductsMainPage.razor.cs`:

**فایل: BackOffice/src/BackOffice/Pages/Products/ProductsMainPage.razor.cs**

```csharp
// ✅ CreateNew() - فعال شد
public async Task CreateNew()
{
    var dialog = await DialogService.ShowAsync<CreateDialog>("افزودن محصول", 
        new DialogParameters<CreateDialog> { { x => x.Model, new CreateNewProductsRequest() } }, 
        new DialogOptions { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Small });
    // ...
}

// ✅ Update() - فعال شد
public async Task Update(DataModel model)
{
    var parameters = new DialogParameters<UpdateDialog> { { x => x.Model, model.Adapt<UpdateProductsRequest>() } };
    var dialog = await DialogService.ShowAsync<UpdateDialog>("ویرایش محصول", parameters, ...);
    // ...
}

// ✅ OpenGallery() - فعال شد
public async Task OpenGallery(DataModel model)
{
    var parameters = new DialogParameters<GalleryDialog>
    {
        { x => x.ProductId, model.Id },
        { x => x.ProductTitle, model.Title }
    };
    await DialogService.ShowAsync<GalleryDialog>("گالری تصاویر", parameters, ...);
}

// ✅ OpenTagAssignment() - فعال شد
public async Task OpenTagAssignment(DataModel model)
{
    var parameters = new DialogParameters<AssignTagsDialog>
    {
        { x => x.ProductId, model.Id },
        { x => x.ProductTitle, model.Title }
    };
    await DialogService.ShowAsync<AssignTagsDialog>("مدیریت تگ‌های محصول", parameters, ...);
}
```

**Using statement uncomment شد:**
```csharp
using BackOffice.Pages.Tag.Components; // برای AssignTagsDialog
```

---

## 5. اضافه کردن فیلد "تعداد موجودی" به Products

### نیاز
نمایش و ویرایش تعداد موجودی محصول در فرم‌ها و لیست

### تغییرات

**1. فرم‌های دیالوگ (CreateDialog.razor & UpdateDialog.razor):**
```razor
<MudStack Row="true" AlignItems="AlignItems.Center">
    <MudItem xs="6">
        <MudNumericField T="int" HideSpinButtons="true" @bind-Value="Model.Discount" 
            Disabled="_isLoading" Label="تخفیف (%)" Variant="Variant.Outlined" Margin="Margin.Dense" />
    </MudItem>
    <MudItem xs="6">
        <MudNumericField T="int" HideSpinButtons="true" @bind-Value="Model.RemainingCount" 
            Disabled="_isLoading" Label="تعداد موجودی" Variant="Variant.Outlined" Margin="Margin.Dense" />
    </MudItem>
</MudStack>
```

**2. ستون جدید در لیست (ProductsMainPage.razor):**
```razor
<TemplateColumn Title="موجودی" CellStyle="text-wrap: nowrap;">
    <CellTemplate>
        @if (context.Item.RemainingCount <= 0)
        {
            <MudChip T="string" Color="Color.Error" Size="Size.Small">ناموجود</MudChip>
        }
        else if (context.Item.RemainingCount < 10)
        {
            <MudChip T="string" Color="Color.Warning" Size="Size.Small">@context.Item.RemainingCount</MudChip>
        }
        else
        {
            <MudChip T="string" Color="Color.Success" Size="Size.Small">@context.Item.RemainingCount</MudChip>
        }
    </CellTemplate>
</TemplateColumn>
```

**3. اضافه کردن فیلد به BFF Commands (فیکس مهم!):**

مشکل: فیلد `RemainingCount` در BFF Application Commands نبود و باعث می‌شد مقدار ارسال/دریافت نشه.

**CreateNewProductsCommand.cs:**
```csharp
public int Discount { get; init; }
public int Rate { get; init; }
public int RemainingCount { get; init; }  // ← اضافه شد
public ImageFileModel ImageFile { get; init; }
```

**UpdateProductsCommand.cs:**
```csharp
public int Discount { get; init; }
public int Rate { get; init; }
public int RemainingCount { get; init; }  // ← اضافه شد
public string ImagePath { get; init; }
```

---

## لیست کامل فایل‌های تغییر یافته

### BackOffice.BFF
| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `WebApi/Common/Mappings/CommissionProfile.cs` | MODIFIED | اضافه شدن mapping برای GetUserWeeklyBalances |
| `WebApi/Common/Mappings/ClubMembershipProfile.cs` | REWRITTEN | Mappings کامل برای ClubMembership |
| `WebApi/Services/ClubMembershipService.cs` | MODIFIED | اضافه شدن GetClubStatistics override |
| `Application/ProductsCQ/Commands/CreateNewProducts/CreateNewProductsCommand.cs` | MODIFIED | اضافه شدن RemainingCount |
| `Application/ProductsCQ/Commands/UpdateProducts/UpdateProductsCommand.cs` | MODIFIED | اضافه شدن RemainingCount |

### CMS
| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `WebApi/Common/Mappings/ClubMembershipProfile.cs` | NEW | Mappings برای ClubMembership |

### BackOffice UI
| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `Pages/Products/ProductsMainPage.razor.cs` | MODIFIED | فعال‌سازی CreateNew, Update, OpenGallery, OpenTagAssignment |
| `Pages/Products/ProductsMainPage.razor` | MODIFIED | اضافه شدن ستون موجودی |
| `Pages/Products/Components/CreateDialog.razor` | MODIFIED | اضافه شدن فیلد موجودی |
| `Pages/Products/Components/UpdateDialog.razor` | MODIFIED | اضافه شدن فیلد موجودی |

---

## نکات فنی مهم

### 1. Mapster با Proto Types
برای proto types که immutable هستند، باید از `MapWith` استفاده کرد:

```csharp
config.NewConfig<SourceDto, ProtoResponse>()
    .MapWith(src => new ProtoResponse
    {
        Field1 = src.Field1,
        RepeatedField = { src.List?.Select(...) ?? Enumerable.Empty<...>() }
    });
```

### 2. Alias Imports برای Proto Disambiguation
وقتی دو proto با اسم یکسان داریم:

```csharp
using BffProtos = BackOffice.BFF.ClubMembership.Protobuf.Protos.ClubMembership;
using CmsProtos = CMSMicroservice.Protobuf.Protos.ClubMembership;
```

### 3. Null-Safe MetaData Mapping
```csharp
MetaData = src.MetaData != null ? new MetaData
{
    PageIndex = src.MetaData.PageIndex,
    TotalPages = src.MetaData.TotalPages,
    TotalCount = src.MetaData.TotalCount
} : null
```

---

## Build Status پایان Session

```
BackOffice.BFF: ✅ Build succeeded (0 errors)
CMS: ✅ Build succeeded (0 errors)  
BackOffice UI: ✅ Build succeeded (0 errors)
```
