# 📝 Changelog - ۵ دی ۱۴۰۴ (25 December 2025)

> **Session**: فلگ فعال‌سازی چتیکا، اصلاح DayaLoan، بازنویسی صفحه درخت شبکه با org-chart، Stored Procedure برای GetNetworkTree

---

## 🎯 خلاصه Session

این session شامل موارد زیر بود:
1. **Chatika Enabled Flag** - اضافه کردن فلگ فعال/غیرفعال برای Worker چتیکا
2. **DayaLoanCheckWorker Fix** - جلوگیری از استعلام مجدد مشتریان با قرارداد
3. **BackOffice Network Tree Rewrite** - بازنویسی کامل با d3-org-chart
4. **Tooltip on Hover** - نمایش اطلاعات کاربر روی hover
5. **Week Filter Visual** - تمایز بصری کاربران فعال شده در هفته فیلتر شده
6. **Stored Procedure** - SP برای GetNetworkTree جهت بهبود performance

---

## ✨ New Features

### 1. Chatika Enabled Flag ✅

**نیاز**: قابلیت غیرفعال کردن موقت Worker چتیکا از طریق Config

**راه‌حل**: اضافه کردن فلگ `Enabled` در تنظیمات

**تغییرات در `appsettings.json`**:
```json
"Chatika": {
    "Enabled": false,  // ← فلگ جدید
    "BaseUrl": "https://api.chatika.ir",
    "ApiKey": "..."
}
```

**تغییرات در `ChatikaAccountActivationJob.cs`**:
```csharp
public async Task ExecuteAsync()
{
    // Check if Chatika integration is enabled
    var isEnabled = _configuration.GetValue<bool>("Chatika:Enabled", false);
    if (!isEnabled)
    {
        _logger.LogDebug("Chatika integration is disabled. Skipping job execution.");
        return;
    }
    
    // ... rest of the job
}
```

**فایل‌های تغییر یافته**:
- `CMSMicroservice.WebApi/appsettings.json`
- `CMSMicroservice.Infrastructure/BackgroundJobs/ChatikaAccountActivationJob.cs`

---

### 2. DayaLoanCheckWorker - Exclude Existing Contracts ✅

**مشکل**: Worker استعلام دایا برای مشتریانی که قبلاً قرارداد گرفته‌اند مجدداً کال می‌شد

**راه‌حل**: فیلتر کردن کاربرانی که `ContractNumber` دارند

**کد اضافه شده**:
```csharp
// Get national codes of users who already have contracts
var existingContractNationalCodes = await _dbContext.DayaLoanContracts
    .Where(c => !c.IsDeleted && !string.IsNullOrEmpty(c.ContractNumber))
    .Select(c => c.NationalCode)
    .Distinct()
    .ToListAsync(stoppingToken);

// Filter out users who already have contracts
var usersToProcess = eligibleUsers
    .Where(u => !existingContractNationalCodes.Contains(u.NationalCode))
    .ToList();

_logger.LogInformation(
    "Filtered users: {TotalEligible} eligible, {WithContract} already have contracts, {ToProcess} to process",
    eligibleUsers.Count, 
    eligibleUsers.Count - usersToProcess.Count, 
    usersToProcess.Count);
```

**فایل**: `CMSMicroservice.WebApi/Workers/DayaLoanCheckWorker.cs`

---

### 3. 🌳 BackOffice Network Tree - Complete Rewrite with d3-org-chart ✅

**نیاز**: استفاده از پلاگین org-chart به جای D3 دستی برای نمایش درخت شبکه در پنل ادمین

**معماری جدید**:

```
┌─────────────────────────────────────────────────────────────┐
│                   NetworkTreeViewer.razor                    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ MudToolBar:                                          │    │
│  │ [Search] [Week Filter] [Expand] [Collapse] [Export]  │    │
│  └─────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ Navigation History (Breadcrumb)                      │    │
│  │ User 123 → User 456 → User 789                       │    │
│  └─────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ d3-org-chart Container (#admin-org-chart)            │    │
│  │                                                       │    │
│  │        ┌──────┐                                       │    │
│  │        │ Root │                                       │    │
│  │        └──┬───┘                                       │    │
│  │      ┌───┴───┐                                        │    │
│  │   ┌──┴──┐ ┌──┴──┐                                     │    │
│  │   │Left │ │Right│                                     │    │
│  │   └─────┘ └─────┘                                     │    │
│  │                                                       │    │
│  └─────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ MudDataGrid (Table View)                             │    │
│  │ Mobile | Name | Level | Position | Status | Actions  │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
```

**فایل‌های جدید**:

| فایل | توضیح |
|------|-------|
| `wwwroot/js/admin-org-chart.js` | Wrapper برای d3-org-chart با امکانات ادمین |
| `wwwroot/css/admin-org-chart.css` | استایل کارت‌های نود با رنگ‌بندی چپ/راست |
| `wwwroot/js/d3-org-chart3.js` | کتابخانه d3-org-chart (کپی از FrontOffice) |
| `wwwroot/js/d3-flextree.min.js` | Dependency برای org-chart |

**فایل‌های تغییر یافته**:
- `NetworkTreeViewer.razor` - بازنویسی کامل
- `wwwroot/index.html` - اضافه شدن رفرنس‌های JS/CSS

---

### 4. 💬 Tooltip on Hover ✅

**نیاز**: نمایش اطلاعات کامل کاربر هنگام hover روی نود

**پیاده‌سازی در `admin-org-chart.js`**:

```javascript
// Tooltip container
const tooltip = d3.select('body').append('div')
    .attr('class', 'admin-org-tooltip')
    .style('opacity', 0);

// Node hover events
.on('mouseover', function(event, d) {
    tooltip.transition().duration(200).style('opacity', .95);
    tooltip.html(`
        <div class="tooltip-header">${d.data.firstName} ${d.data.lastName}</div>
        <div class="tooltip-row"><span>📱</span> ${d.data.mobile}</div>
        <div class="tooltip-row"><span>📊</span> سطح: ${d.data.networkLevel}</div>
        <div class="tooltip-row"><span>📍</span> ${d.data.legPosition === 1 ? 'چپ' : 'راست'}</div>
        ${d.data.isClubActive ? 
            `<div class="tooltip-row active"><span>✅</span> باشگاه فعال</div>` : 
            `<div class="tooltip-row inactive"><span>❌</span> باشگاه غیرفعال</div>`
        }
        ${d.data.activationWeekDisplayName ? 
            `<div class="tooltip-row"><span>📅</span> ${d.data.activationWeekDisplayName}</div>` : ''
        }
    `)
    .style('left', (event.pageX + 15) + 'px')
    .style('top', (event.pageY - 10) + 'px');
})
```

**استایل Tooltip**:
```css
.admin-org-tooltip {
    position: absolute;
    background: rgba(33, 33, 33, 0.95);
    color: white;
    padding: 12px 16px;
    border-radius: 8px;
    font-size: 13px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.3);
    pointer-events: none;
    z-index: 10000;
    direction: rtl;
}
```

---

### 5. 🎨 Week Filter Visual Distinction ✅

**نیاز**: وقتی هفته فیلتر می‌شه، کاربران فعال شده در اون هفته متمایز باشن

**پیاده‌سازی**:

```javascript
// Apply week filter styling
applyWeekFilter: function(weekId) {
    if (!this.chart || !this.chartData) return;
    
    d3.selectAll('.admin-node-card').each(function() {
        const nodeData = d3.select(this).datum();
        if (nodeData && nodeData.data) {
            if (weekId && nodeData.data.activationWeekDefinitionId !== weekId) {
                d3.select(this).classed('disabled', true);
            } else {
                d3.select(this).classed('disabled', false);
            }
        }
    });
}
```

**استایل**:
```css
/* کاربرانی که در هفته فیلتر شده فعال نشدن */
.admin-node-card.disabled {
    opacity: 0.35;
    filter: grayscale(70%);
}

/* کاربران فعال شده در هفته هدف */
.admin-node-card:not(.disabled) {
    animation: target-glow 2s ease-in-out infinite;
}

@keyframes target-glow {
    0%, 100% { box-shadow: 0 0 5px rgba(76, 175, 80, 0.3); }
    50% { box-shadow: 0 0 20px rgba(76, 175, 80, 0.6); }
}
```

---

### 6. ⚡ Stored Procedure for GetNetworkTree ✅

**نیاز**: بهبود سرعت لود درخت شبکه + حذف محدودیت عمق

**Stored Procedure** (`dbbkup/SP_GetNetworkTree.sql`):

```sql
CREATE PROCEDURE [CMS].[GetNetworkTree]
    @RootUserId BIGINT,
    @MaxDepth INT = 100,
    @IsClubActive BIT = NULL,
    @ActivationWeekDefinitionId BIGINT = NULL
AS
BEGIN
    SET NOCOUNT ON;

    -- CTE برای پیمایش درخت باینری به صورت recursive
    ;WITH NetworkTreeCTE AS (
        -- Base case: ریشه درخت
        SELECT 
            u.Id AS UserId,
            u.Mobile,
            u.FirstName,
            u.LastName,
            u.LegPosition,
            u.NetworkParentId AS ParentId,
            0 AS NetworkLevel,
            u.Created AS UserCreated
        FROM [CMS].[Users] u
        WHERE u.Id = @RootUserId AND u.IsDeleted = 0

        UNION ALL

        -- Recursive case: فرزندان
        SELECT 
            u.Id, u.Mobile, u.FirstName, u.LastName, u.LegPosition,
            u.NetworkParentId, parent.NetworkLevel + 1, u.Created
        FROM [CMS].[Users] u
        INNER JOIN NetworkTreeCTE parent ON u.NetworkParentId = parent.UserId
        WHERE u.IsDeleted = 0 AND parent.NetworkLevel < @MaxDepth
    )
    -- Join با ClubMemberships و WeekDefinitions
    SELECT 
        t.UserId, t.Mobile, t.FirstName, t.LastName, t.LegPosition,
        t.ParentId, t.NetworkLevel,
        cm.ActivatedAt AS ClubActivatedAt,
        ISNULL(cm.IsActive, 0) AS IsClubActive,
        wd.Id AS ActivationWeekDefinitionId,
        wd.DisplayName AS ActivationWeekDisplayName,
        CASE WHEN @ActivationWeekDefinitionId IS NOT NULL 
             AND wd.Id = @ActivationWeekDefinitionId THEN 1 ELSE 0 
        END AS IsActivatedInTargetWeek,
        t.UserCreated
    FROM NetworkTreeCTE t
    LEFT JOIN [CMS].[ClubMemberships] cm ON cm.UserId = t.UserId AND cm.IsDeleted = 0
    LEFT JOIN [CMS].[WeekDefinitions] wd ON cm.ActivatedAt >= wd.StartDate 
                                         AND cm.ActivatedAt < wd.EndDate
    WHERE (@IsClubActive IS NULL OR ISNULL(cm.IsActive, 0) = @IsClubActive 
           OR t.NetworkLevel = 0)
    ORDER BY t.NetworkLevel, t.ParentId, t.LegPosition
    OPTION (MAXRECURSION 0);  -- بدون محدودیت recursion
END
```

**تغییرات در Application Layer**:

**فایل جدید**: `NetworkTreeNodeDto.cs`
```csharp
public class NetworkTreeNodeDto
{
    public long UserId { get; set; }
    public string? Mobile { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public int? LegPosition { get; set; }
    public long? ParentId { get; set; }
    public int NetworkLevel { get; set; }
    public DateTime? ClubActivatedAt { get; set; }
    public bool IsClubActive { get; set; }
    public long? ActivationWeekDefinitionId { get; set; }
    public string? ActivationWeekDisplayName { get; set; }
    public bool IsActivatedInTargetWeek { get; set; }
    public DateTimeOffset UserCreated { get; set; }
}
```

**بازنویسی `GetNetworkTreeQueryHandler.cs`**:
- استفاده از ADO.NET برای اجرای SP
- تبدیل نتیجه flat به ساختار درختی
- پشتیبانی از همه پارامترهای فیلتر

**پکیج جدید**:
```xml
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.11" />
```

---

## 📁 فایل‌های تغییر یافته

### CMS Microservice

| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `WebApi/appsettings.json` | Modified | اضافه شدن `Chatika.Enabled` |
| `Infrastructure/BackgroundJobs/ChatikaAccountActivationJob.cs` | Modified | چک فلگ Enabled |
| `WebApi/Workers/DayaLoanCheckWorker.cs` | Modified | فیلتر کاربران با قرارداد |
| `Application/CMSMicroservice.Application.csproj` | Modified | پکیج Relational |
| `Application/Common/Interfaces/IApplicationDbContext.cs` | Modified | اضافه شدن DatabaseFacade |
| `Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs` | **REWRITTEN** | استفاده از SP |
| `Application/NetworkMembershipCQ/Queries/GetNetworkTree/NetworkTreeNodeDto.cs` | **NEW** | DTO برای نتیجه SP |

### BackOffice

| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `Pages/Network/NetworkTreeViewer.razor` | **REWRITTEN** | استفاده از org-chart |
| `wwwroot/index.html` | Modified | رفرنس‌های JS/CSS |
| `wwwroot/js/admin-org-chart.js` | **NEW** | Wrapper برای org-chart |
| `wwwroot/css/admin-org-chart.css` | **NEW** | استایل نودها |
| `wwwroot/js/d3-org-chart3.js` | **NEW** | کتابخانه org-chart |
| `wwwroot/js/d3-flextree.min.js` | **NEW** | Dependency |

### SQL Scripts

| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `dbbkup/SP_GetNetworkTree.sql` | **NEW** | Stored Procedure |

---

## 🔧 تنظیمات جدید

### appsettings.json - Chatika
```json
{
  "Chatika": {
    "Enabled": false,  // ← جدید: فعال/غیرفعال کردن Worker
    "BaseUrl": "https://api.chatika.ir",
    "ApiKey": "YOUR_API_KEY_HERE"
  }
}
```

---

## 🗄️ Database Changes

### اجرای Stored Procedure
```bash
# اجرای اسکریپت در SQL Server
sqlcmd -S <server> -d <database> -i dbbkup/SP_GetNetworkTree.sql
```

---

## ✅ Build Status

```bash
cd /home/masoud/Apps/project/FourSat/CMS/src
dotnet build
# Result: Build succeeded. 0 Error(s)

cd /home/masoud/Apps/project/FourSat/BackOffice/src
dotnet build BackOffice/BackOffice.csproj
# Result: Build succeeded
```

---

## 📊 آمار Session

| متریک | مقدار |
|-------|-------|
| فایل‌های جدید | 6 |
| فایل‌های تغییر یافته | 8 |
| قابلیت‌های جدید | 6 |
| خطوط کد اضافه شده | ~800 |
| Stored Procedure | 1 |

---

## 🔗 Related Changelogs

- [CHANGELOG-2025-12-23.md](CHANGELOG-2025-12-23.md) - پیاده‌سازی Worker چتیکا
- [CHANGELOG-2025-12-20.md](CHANGELOG-2025-12-20.md) - بهبودات قبلی
