643 lines
22 KiB
Markdown
643 lines
22 KiB
Markdown
# Network Tree - Activation Week Feature
|
|
|
|
## نمای کلی (Overview)
|
|
|
|
این سند تغییرات مربوط به افزودن قابلیت فیلتر و نمایش هفته فعالسازی در درخت شبکه را توضیح میدهد.
|
|
|
|
**تاریخ پیادهسازی:** دسامبر 2025
|
|
|
|
**تغییرات کلیدی:**
|
|
- اضافه شدن فیلد `IsActivatedInTargetWeek` برای flagging (به جای filtering)
|
|
- حذف فیلتر سمت Backend و انتقال به UI
|
|
- نمایش بصری وضعیت فعالسازی در درخت
|
|
|
|
---
|
|
|
|
## منطق کسبوکار (Business Logic)
|
|
|
|
### رویکرد قبلی (❌ Removed)
|
|
- فیلتر میکرد و فقط نودهایی که در هفته هدف فعال شدهاند نمایش داده میشدند
|
|
- مشکل: کاربران نمیتوانستند کل ساختار شبکه را ببینند
|
|
|
|
### رویکرد جدید (✅ Current)
|
|
- **همه نودها نمایش داده میشوند** (بدون فیلتر در دیتابیس)
|
|
- هر نود یک flag دارد: `IsActivatedInTargetWeek`
|
|
- UI از این flag برای نمایش بصری استفاده میکند
|
|
|
|
### محاسبه هفته فعالسازی
|
|
|
|
```csharp
|
|
private static int CalculateWeekNumber(DateTimeOffset date)
|
|
{
|
|
var persianCalendar = new PersianCalendar();
|
|
int year = persianCalendar.GetYear(date.DateTime);
|
|
int dayOfYear = persianCalendar.GetDayOfYear(date.DateTime);
|
|
int weekNumber = (dayOfYear - 1) / 7 + 1;
|
|
|
|
return int.Parse($"{year}{weekNumber:D2}");
|
|
// مثال: 140352 = سال 1403، هفته 52
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## تغییرات Backend
|
|
|
|
### 1. DTO Changes
|
|
|
|
**فایل:** `CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkTree/NetworkTreeDto.cs`
|
|
|
|
```csharp
|
|
public class NetworkTreeDto
|
|
{
|
|
// ... existing fields
|
|
public string? ActivationWeekNumber { get; set; }
|
|
public bool IsActivatedInTargetWeek { get; set; } // ✅ NEW
|
|
public DateTimeOffset UserCreated { get; set; }
|
|
public NetworkTreeDto? LeftChild { get; set; }
|
|
public NetworkTreeDto? RightChild { get; set; }
|
|
}
|
|
```
|
|
|
|
### 2. Query Handler Changes
|
|
|
|
**فایل:** `CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs`
|
|
|
|
#### تغییر در BuildTree Method
|
|
|
|
```csharp
|
|
private NetworkTreeDto BuildTree(
|
|
User user,
|
|
int currentDepth,
|
|
int maxDepth,
|
|
string? requestActivationWeekNumber) // ✅ پارامتر اضافه شد
|
|
{
|
|
// محاسبه هفته فعالسازی
|
|
string? activationWeekNumber = null;
|
|
bool isActivatedInTargetWeek = false;
|
|
|
|
if (user.ClubMembership?.ActivatedAt != null)
|
|
{
|
|
activationWeekNumber = CalculateWeekNumber(user.ClubMembership.ActivatedAt.Value)
|
|
.ToString();
|
|
|
|
// چک کردن اینکه آیا در هفته هدف فعال شده
|
|
if (!string.IsNullOrEmpty(requestActivationWeekNumber))
|
|
{
|
|
isActivatedInTargetWeek = activationWeekNumber == requestActivationWeekNumber;
|
|
}
|
|
}
|
|
|
|
var node = new NetworkTreeDto
|
|
{
|
|
// ... existing fields
|
|
ActivationWeekNumber = activationWeekNumber,
|
|
IsActivatedInTargetWeek = isActivatedInTargetWeek, // ✅ تنظیم flag
|
|
};
|
|
|
|
// ... recursive calls
|
|
}
|
|
```
|
|
|
|
#### حذف فیلتر از GetFilteredChildren
|
|
|
|
**قبل (❌):**
|
|
```csharp
|
|
private IEnumerable<User> GetFilteredChildren(
|
|
IEnumerable<User> children,
|
|
bool? isClubActive,
|
|
string? activationWeekNumber)
|
|
{
|
|
var query = children.AsQueryable();
|
|
|
|
if (isClubActive.HasValue)
|
|
{
|
|
query = query.Where(u => u.ClubMembership != null &&
|
|
u.ClubMembership.IsActive == isClubActive.Value);
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(activationWeekNumber))
|
|
{
|
|
// ❌ فیلتر میکرد
|
|
query = query.Where(u => /* filter logic */);
|
|
}
|
|
|
|
return query.ToList();
|
|
}
|
|
```
|
|
|
|
**بعد (✅):**
|
|
```csharp
|
|
private IEnumerable<User> GetFilteredChildren(
|
|
IEnumerable<User> children,
|
|
bool? isClubActive)
|
|
{
|
|
var query = children.AsQueryable();
|
|
|
|
// فقط فیلتر IsClubActive باقی ماند
|
|
if (isClubActive.HasValue)
|
|
{
|
|
query = query.Where(u => u.ClubMembership != null &&
|
|
u.ClubMembership.IsActive == isClubActive.Value);
|
|
}
|
|
|
|
return query.ToList();
|
|
}
|
|
```
|
|
|
|
### 3. Proto Definition
|
|
|
|
**فایل:** `CMSMicroservice.Protobuf/Protos/networkmembership.proto`
|
|
|
|
```protobuf
|
|
message NetworkTreeNodeModel {
|
|
int64 user_id = 1;
|
|
string user_name = 2;
|
|
optional int64 parent_id = 3;
|
|
optional int32 network_leg = 4;
|
|
optional int32 network_level = 5;
|
|
optional bool is_active = 6;
|
|
optional google.protobuf.Timestamp joined_at = 7;
|
|
optional google.protobuf.Timestamp club_activated_at = 8;
|
|
bool is_club_active = 9;
|
|
string activation_week_number = 10;
|
|
bool is_activated_in_target_week = 11; // ✅ NEW
|
|
google.protobuf.Timestamp user_created = 12;
|
|
}
|
|
```
|
|
|
|
### 4. Mapping
|
|
|
|
**فایل:** `CMSMicroservice.WebApi/Common/Mappings/NetworkMembershipProfile.cs`
|
|
|
|
```csharp
|
|
var protoNode = new NetworkTreeNodeModel
|
|
{
|
|
UserId = node.UserId,
|
|
UserName = node.UserName,
|
|
ParentId = node.ParentId,
|
|
NetworkLeg = node.NetworkLeg,
|
|
NetworkLevel = node.NetworkLevel,
|
|
IsActive = node.IsActive,
|
|
JoinedAt = node.JoinedAt.HasValue
|
|
? Timestamp.FromDateTime(DateTime.SpecifyKind(node.JoinedAt.Value, DateTimeKind.Utc))
|
|
: null,
|
|
ClubActivatedAt = node.ClubActivatedAt.HasValue
|
|
? Timestamp.FromDateTime(DateTime.SpecifyKind(node.ClubActivatedAt.Value, DateTimeKind.Utc))
|
|
: null,
|
|
IsClubActive = node.IsClubActive,
|
|
ActivationWeekNumber = node.ActivationWeekNumber ?? string.Empty,
|
|
IsActivatedInTargetWeek = node.IsActivatedInTargetWeek, // ✅ NEW
|
|
UserCreated = Timestamp.FromDateTime(DateTime.SpecifyKind(node.UserCreated, DateTimeKind.Utc))
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## تغییرات BFF
|
|
|
|
### Proto & Mapping
|
|
|
|
همان تغییرات در CMS در BFF هم اعمال شد:
|
|
|
|
**فایلها:**
|
|
- `BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeResponseDto.cs`
|
|
- `BackOffice.BFF.WebApi/Common/Mappings/NetworkMembershipProfile.cs`
|
|
- `Protobufs/networkmembership.proto`
|
|
|
|
```csharp
|
|
public class NetworkTreeNodeDto
|
|
{
|
|
// ... existing properties
|
|
public bool IsActivatedInTargetWeek { get; set; } // ✅ NEW
|
|
public string ActivationWeekNumber { get; set; } = string.Empty;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## تغییرات Frontend
|
|
|
|
### 1. Razor Component
|
|
|
|
**فایل:** `BackOffice/Pages/Network/NetworkTreeViewer.razor`
|
|
|
|
#### تغییر در ستون "وضعیت"
|
|
|
|
**قبل (❌):**
|
|
```razor
|
|
<PropertyColumn Property="x => x.IsActive" Title="وضعیت">
|
|
<CellTemplate>
|
|
@if (context.Item.IsActive!=null) {
|
|
<MudChip Color="@((bool)context.Item.IsActive ? Color.Success : Color.Error)">
|
|
@((bool)context.Item.IsActive ? "فعال" : "غیرفعال")
|
|
</MudChip>
|
|
}
|
|
</CellTemplate>
|
|
</PropertyColumn>
|
|
```
|
|
|
|
**بعد (✅):**
|
|
```razor
|
|
<PropertyColumn Property="x => x.IsClubActive" Title="وضعیت">
|
|
<CellTemplate>
|
|
<MudChip T="string"
|
|
Color="@(context.Item.IsClubActive ? Color.Success : Color.Error)"
|
|
Size="Size.Small">
|
|
@(context.Item.IsClubActive ? "فعال" : "غیرفعال")
|
|
</MudChip>
|
|
</CellTemplate>
|
|
</PropertyColumn>
|
|
```
|
|
|
|
#### ارسال داده به JavaScript
|
|
|
|
```csharp
|
|
private async Task RenderTree()
|
|
{
|
|
if (_treeData == null || !_treeData.Nodes.Any()) return;
|
|
|
|
var jsNodes = _treeData.Nodes.Select(n => new
|
|
{
|
|
userId = n.UserId,
|
|
userName = n.UserName,
|
|
parentId = n.ParentId,
|
|
networkLevel = n.NetworkLevel,
|
|
networkLeg = n.NetworkLeg,
|
|
isActive = n.IsClubActive, // ✅ تغییر به IsClubActive
|
|
isClubActive = n.IsClubActive,
|
|
isActivatedInTargetWeek = n.IsActivatedInTargetWeek, // ✅ NEW
|
|
activationWeekNumber = _activationWeekFilter ?? "", // ✅ فیلتر UI
|
|
clubActivatedAt = n.ClubActivatedAt?.ToDateTime().ToLocalTime().ToString("yyyy/MM/dd") ?? "",
|
|
userCreated = n.UserCreated?.ToDateTime().ToLocalTime().ToString("yyyy/MM/dd") ?? ""
|
|
}).ToArray();
|
|
|
|
await JS.InvokeVoidAsync("NetworkTreeViewer.initialize", "network-tree-container", jsNodes);
|
|
}
|
|
```
|
|
|
|
**نکته مهم:** `activationWeekNumber` از فیلتر UI گرفته میشود (`_activationWeekFilter`) نه از Backend.
|
|
|
|
### 2. JavaScript Visualization
|
|
|
|
**فایل:** `BackOffice/wwwroot/js/network-tree.js`
|
|
|
|
#### منطق رنگ نود (دایره)
|
|
|
|
```javascript
|
|
node.append('circle')
|
|
.attr('r', 8)
|
|
.style('fill', d => {
|
|
// اگر هفتهای انتخاب نشده، همه سبز
|
|
if (!d.data.activationWeekNumber || d.data.activationWeekNumber === '') {
|
|
return '#4caf50';
|
|
}
|
|
// اگر در هفته هدف فعال شده، سبز، وگرنه قرمز
|
|
return d.data.isActivatedInTargetWeek ? '#4caf50' : '#f44336';
|
|
})
|
|
.style('stroke', '#fff')
|
|
.style('stroke-width', 2)
|
|
.style('cursor', 'pointer');
|
|
```
|
|
|
|
#### منطق رنگ تایتل (نام کاربر)
|
|
|
|
```javascript
|
|
node.append('text')
|
|
.attr('dy', -15)
|
|
.attr('text-anchor', 'middle')
|
|
.style('font-size', '12px')
|
|
.style('font-weight', 'bold')
|
|
.style('fill', d => d.data.isClubActive ? '#424242' : '#9e9e9e')
|
|
.text(d => d.data.userName || `User ${d.data.userId}`);
|
|
```
|
|
|
|
#### اضافه کردن فیلدها به buildHierarchy
|
|
|
|
```javascript
|
|
buildHierarchy: function(nodes) {
|
|
// ...
|
|
const nodeMap = new Map();
|
|
nodes.forEach(node => {
|
|
nodeMap.set(node.userId, {
|
|
userId: node.userId,
|
|
userName: node.userName,
|
|
parentId: node.parentId,
|
|
level: node.networkLevel,
|
|
networkLeg: node.networkLeg,
|
|
isActive: node.isActive,
|
|
isClubActive: node.isClubActive, // ✅ NEW
|
|
isActivatedInTargetWeek: node.isActivatedInTargetWeek, // ✅ NEW
|
|
activationWeekNumber: node.activationWeekNumber, // ✅ NEW
|
|
clubActivatedAt: node.clubActivatedAt,
|
|
userCreated: node.userCreated,
|
|
children: []
|
|
});
|
|
});
|
|
// ...
|
|
}
|
|
```
|
|
|
|
#### Legend (راهنمای رنگها)
|
|
|
|
```javascript
|
|
// Legend for title colors (club status)
|
|
legend.append('text')
|
|
.attr('x', 0)
|
|
.attr('y', 0)
|
|
.style('font-size', '12px')
|
|
.style('font-weight', 'bold')
|
|
.style('fill', '#424242')
|
|
.text('باشگاه فعال');
|
|
|
|
legend.append('text')
|
|
.attr('x', 0)
|
|
.attr('y', 20)
|
|
.style('font-size', '12px')
|
|
.style('font-weight', 'bold')
|
|
.style('fill', '#9e9e9e')
|
|
.text('باشگاه غیرفعال');
|
|
|
|
// Legend for circles (week status)
|
|
legend.append('circle')
|
|
.attr('cx', 0)
|
|
.attr('cy', 50)
|
|
.attr('r', 6)
|
|
.style('fill', '#4caf50');
|
|
|
|
legend.append('text')
|
|
.attr('x', 12)
|
|
.attr('y', 54)
|
|
.style('font-size', '12px')
|
|
.text('فعال در هفته هدف');
|
|
|
|
legend.append('circle')
|
|
.attr('cx', 0)
|
|
.attr('cy', 75)
|
|
.attr('r', 6)
|
|
.style('fill', '#f44336');
|
|
|
|
legend.append('text')
|
|
.attr('x', 12)
|
|
.attr('y', 79)
|
|
.style('font-size', '12px')
|
|
.text('خارج از هفته هدف');
|
|
```
|
|
|
|
---
|
|
|
|
## رفتار UI
|
|
|
|
### حالت 1: بدون فیلتر هفته
|
|
|
|
**وضعیت:** `_activationWeekFilter` خالی است
|
|
|
|
**رفتار:**
|
|
- **دایرهها:** همه سبز (#4caf50)
|
|
- **تایتل:** مشکی (#424242) برای باشگاه فعال، خاکستری (#9e9e9e) برای باشگاه غیرفعال
|
|
|
|
### حالت 2: با فیلتر هفته
|
|
|
|
**وضعیت:** مثلاً `_activationWeekFilter = "140352"`
|
|
|
|
**رفتار:**
|
|
- **دایرهها:**
|
|
- سبز (#4caf50) → کاربران فعال شده در هفته 52 سال 1403
|
|
- قرمز (#f44336) → کاربران فعال شده در هفتههای دیگر
|
|
- **تایتل:** همچنان بر اساس `isClubActive`
|
|
|
|
### حالت 3: فیلتر IsClubActive
|
|
|
|
این فیلتر در سمت Backend اعمال میشود و نودهای غیرفعال را حذف میکند.
|
|
|
|
---
|
|
|
|
## Flow Diagram
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ User Interface │
|
|
│ ┌────────────────┐ ┌──────────────────┐ │
|
|
│ │ IsClubActive │ │ActivationWeek │ │
|
|
│ │ Filter │ │ Filter │ │
|
|
│ └────────┬───────┘ └────────┬─────────┘ │
|
|
└───────────┼──────────────────┼────────────────────────────┘
|
|
│ │
|
|
▼ ▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Backend (CMS) │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ GetNetworkTreeQueryHandler │ │
|
|
│ │ │ │
|
|
│ │ 1. GetFilteredChildren (IsClubActive filter only) │ │
|
|
│ │ 2. BuildTree (calculate IsActivatedInTargetWeek) │ │
|
|
│ │ 3. Return ALL nodes with flags │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
└───────────────────────────┬─────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ BFF Layer │
|
|
│ - Proto mapping │
|
|
│ - Pass-through to Frontend │
|
|
└───────────────────────────┬─────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Frontend (Blazor) │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ NetworkTreeViewer.razor │ │
|
|
│ │ │ │
|
|
│ │ - Prepare data with UI filter (_activationWeekFilter)│ │
|
|
│ │ - Send to JavaScript │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
└───────────────────────────┬─────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ JavaScript (D3.js) │
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|
│ │ network-tree.js │ │
|
|
│ │ │ │
|
|
│ │ - Apply visual logic: │ │
|
|
│ │ * Circle color by activationWeekNumber + flag │ │
|
|
│ │ * Title color by isClubActive │ │
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Data Model
|
|
|
|
### Request
|
|
|
|
```csharp
|
|
public class GetNetworkTreeRequest
|
|
{
|
|
public long UserId { get; set; }
|
|
public int? MaxDepth { get; set; }
|
|
public bool? IsClubActive { get; set; } // Backend filter
|
|
public string? ActivationWeekNumber { get; set; } // For flag calculation only
|
|
}
|
|
```
|
|
|
|
### Response
|
|
|
|
```csharp
|
|
public class NetworkTreeDto
|
|
{
|
|
public long UserId { get; set; }
|
|
public string UserName { get; set; }
|
|
public long? ParentId { get; set; }
|
|
public int? NetworkLeg { get; set; }
|
|
public int? NetworkLevel { get; set; }
|
|
public bool? IsActive { get; set; } // Deprecated
|
|
public DateTime? JoinedAt { get; set; }
|
|
public DateTime? ClubActivatedAt { get; set; }
|
|
public bool IsClubActive { get; set; } // ✅ Use this
|
|
public string? ActivationWeekNumber { get; set; }
|
|
public bool IsActivatedInTargetWeek { get; set; } // ✅ NEW
|
|
public DateTimeOffset UserCreated { get; set; }
|
|
public NetworkTreeDto? LeftChild { get; set; }
|
|
public NetworkTreeDto? RightChild { get; set; }
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Testing Scenarios
|
|
|
|
### Test 1: بدون فیلتر
|
|
**Input:**
|
|
- `IsClubActive`: null
|
|
- `ActivationWeekNumber`: null
|
|
|
|
**Expected:**
|
|
- همه نودها نمایش داده شوند
|
|
- همه دایرهها سبز
|
|
- تایتلها بر اساس IsClubActive
|
|
|
|
### Test 2: فیلتر باشگاه فعال
|
|
**Input:**
|
|
- `IsClubActive`: true
|
|
- `ActivationWeekNumber`: null
|
|
|
|
**Expected:**
|
|
- فقط نودهای با باشگاه فعال
|
|
- همه دایرهها سبز
|
|
- همه تایتلها مشکی
|
|
|
|
### Test 3: فیلتر هفته
|
|
**Input:**
|
|
- `IsClubActive`: null
|
|
- `ActivationWeekNumber`: "140352"
|
|
|
|
**Expected:**
|
|
- همه نودها نمایش داده شوند
|
|
- دایره سبز: فعال شده در هفته 52
|
|
- دایره قرمز: فعال شده در هفتههای دیگر
|
|
- تایتلها بر اساس IsClubActive
|
|
|
|
### Test 4: ترکیب فیلترها
|
|
**Input:**
|
|
- `IsClubActive`: true
|
|
- `ActivationWeekNumber`: "140352"
|
|
|
|
**Expected:**
|
|
- فقط نودهای با باشگاه فعال
|
|
- دایره سبز: فعال شده در هفته 52
|
|
- دایره قرمز: فعال شده در هفتههای دیگر
|
|
- همه تایتلها مشکی (چون همه باشگاه فعال دارند)
|
|
|
|
---
|
|
|
|
## Performance Considerations
|
|
|
|
### Database Query
|
|
- ✅ فیلتر `ActivationWeekNumber` از Query حذف شد
|
|
- ✅ فقط فیلتر `IsClubActive` در سمت دیتابیس
|
|
- ⚠️ ممکن است تعداد نودهای بیشتری بازگردانده شود
|
|
|
|
### Memory
|
|
- Backend همه نودها را میفرستد
|
|
- Frontend/JavaScript فیلتر بصری اعمال میکند
|
|
- برای درختهای بسیار بزرگ (>1000 نود) ممکن است نیاز به pagination باشد
|
|
|
|
### UI Rendering
|
|
- D3.js برای درختهای متوسط (<500 نود) عملکرد خوبی دارد
|
|
- برای بهبود عملکرد میتوان از virtualization استفاده کرد
|
|
|
|
---
|
|
|
|
## Migration Notes
|
|
|
|
### Breaking Changes
|
|
- ❌ `IsActive` deprecated است → استفاده از `IsClubActive`
|
|
- ✅ فیلد جدید `IsActivatedInTargetWeek` اضافه شد
|
|
|
|
### Backward Compatibility
|
|
- Proto field numbers حفظ شدهاند
|
|
- Response structure تغییر نکرده (فقط فیلد جدید اضافه شده)
|
|
|
|
### Deployment Steps
|
|
1. Deploy Backend (CMS) با Proto جدید
|
|
2. Deploy BFF با Proto جدید
|
|
3. Deploy Frontend با visualization جدید
|
|
4. تست تمام scenarios
|
|
|
|
---
|
|
|
|
## نکات مهم (Key Points)
|
|
|
|
### ✅ Do's
|
|
- از `IsClubActive` برای وضعیت باشگاه استفاده کنید
|
|
- `IsActivatedInTargetWeek` فقط برای نمایش بصری است
|
|
- فیلتر UI را از Razor به JS بفرستید (`_activationWeekFilter`)
|
|
|
|
### ❌ Don'ts
|
|
- از `IsActive` استفاده نکنید (deprecated)
|
|
- `ActivationWeekNumber` را از Backend برای UI filtering استفاده نکنید
|
|
- فیلتر `ActivationWeekNumber` را در Query اعمال نکنید
|
|
|
|
### 💡 Best Practices
|
|
- همیشه فیلتر UI و Backend flag را sync نگه دارید
|
|
- برای درختهای بزرگ از lazy loading استفاده کنید
|
|
- Legend را همیشه با منطق UI sync کنید
|
|
|
|
---
|
|
|
|
## فایلهای تغییر یافته
|
|
|
|
### Backend (CMS)
|
|
- ✅ `NetworkTreeDto.cs` - اضافه `IsActivatedInTargetWeek`
|
|
- ✅ `GetNetworkTreeQueryHandler.cs` - محاسبه flag + حذف فیلتر
|
|
- ✅ `networkmembership.proto` - اضافه field 11
|
|
- ✅ `NetworkMembershipProfile.cs` - mapping فیلد جدید
|
|
|
|
### BFF
|
|
- ✅ `GetNetworkTreeResponseDto.cs` - اضافه property
|
|
- ✅ `NetworkMembershipProfile.cs` - mapping
|
|
- ✅ `networkmembership.proto` - sync با CMS
|
|
|
|
### Frontend
|
|
- ✅ `NetworkTreeViewer.razor` - تغییر `IsActive` → `IsClubActive`
|
|
- ✅ `NetworkTreeViewer.razor` - اضافه `isActivatedInTargetWeek` به jsNodes
|
|
- ✅ `network-tree.js` - منطق رنگ نود بر اساس flag
|
|
- ✅ `network-tree.js` - منطق رنگ تایتل بر اساس `isClubActive`
|
|
- ✅ `network-tree.js` - Legend جدید
|
|
|
|
---
|
|
|
|
## مراجع (References)
|
|
|
|
- [Binary Tree Guide](../../01-BUSINESS/binary-tree-guide.md)
|
|
- [Network Commission System](../../01-BUSINESS/network-commission-system.md)
|
|
- [CMS API Coverage](./api-coverage.md)
|
|
|
|
---
|
|
|
|
**تاریخ ایجاد:** 14 دسامبر 2025
|
|
**آخرین بهروزرسانی:** 14 دسامبر 2025
|
|
**نویسنده:** Development Team
|