283 lines
9.3 KiB
Markdown
283 lines
9.3 KiB
Markdown
# استقلال کامل بخش Teammates از Media
|
||
|
||
## تضمین استقلال ✅
|
||
|
||
بخش teammates در هر دو فرم (`ImageForm` و `ImageVideoForm`) **کاملاً مستقل** از بخش انتخاب رسانه (تصویر/ویدیو) است.
|
||
|
||
---
|
||
|
||
## State Management
|
||
|
||
### ✅ Teammates State (مستقل)
|
||
```typescript
|
||
const [teammates, setTeammates] = useState<string[]>([""]);
|
||
```
|
||
|
||
**ویژگیها:**
|
||
- هیچ وابستگی به `mediaType` ندارد
|
||
- هیچ وابستگی به `uploadedImage` ندارد
|
||
- هیچ وابستگی به `uploadedVideo` ندارد
|
||
- هیچ وابستگی به `videoCover` ندارد
|
||
- هیچگاه reset نمیشود مگر توسط خود کاربر
|
||
|
||
---
|
||
|
||
## Handlers (هندلرها)
|
||
|
||
### ✅ handleAddTeammate
|
||
```typescript
|
||
const handleAddTeammate = () => setTeammates([...teammates, ""]);
|
||
```
|
||
- **فقط** به `teammates` دسترسی دارد
|
||
- هیچ وابستگی به media ندارد
|
||
|
||
### ✅ handleRemoveTeammate
|
||
```typescript
|
||
const handleRemoveTeammate = (index: number) => {
|
||
const next = teammates.filter((_, i) => i !== index);
|
||
setTeammates(next.length > 0 ? next : [""]);
|
||
};
|
||
```
|
||
- **فقط** به `teammates` دسترسی دارد
|
||
- هیچ وابستگی به media ندارد
|
||
|
||
### ✅ handleTeammateChange
|
||
```typescript
|
||
const handleTeammateChange = (index: number, value: string) => {
|
||
const next = [...teammates];
|
||
next[index] = value;
|
||
setTeammates(next);
|
||
};
|
||
```
|
||
- **فقط** به `teammates` دسترسی دارد
|
||
- هیچ وابستگی به media ندارد
|
||
|
||
---
|
||
|
||
## Media Handlers (هندلرهای رسانه)
|
||
|
||
### ✅ handleSwitchMediaType (ImageVideoForm)
|
||
```typescript
|
||
const handleSwitchMediaType = (type: "image" | "video") => {
|
||
setMediaType(type); // ✅ فقط media
|
||
setUploadedImage(null); // ✅ فقط media
|
||
setUploadedVideo(null); // ✅ فقط media
|
||
setVideoCover(null); // ✅ فقط media
|
||
// ❌ teammates لمس نمیشود!
|
||
};
|
||
```
|
||
|
||
**تضمین:** teammates هیچگاه reset نمیشود
|
||
|
||
### ✅ handleImageUpload
|
||
```typescript
|
||
const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||
const file = e.target.files?.[0];
|
||
if (file) {
|
||
const reader = new FileReader();
|
||
reader.onloadend = () => setUploadedImage(reader.result as string);
|
||
reader.readAsDataURL(file);
|
||
}
|
||
// ❌ teammates لمس نمیشود!
|
||
};
|
||
```
|
||
|
||
### ✅ handleVideoUpload
|
||
```typescript
|
||
const handleVideoUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||
const file = e.target.files?.[0];
|
||
if (file) {
|
||
const url = URL.createObjectURL(file);
|
||
setUploadedVideo({ url, name: file.name });
|
||
setVideoCover(null);
|
||
}
|
||
// ❌ teammates لمس نمیشود!
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## Rendering (رندر)
|
||
|
||
### ✅ TeammatesSection - مشروط اما مستقل
|
||
```typescript
|
||
{topicConfig.requiresTeammates && (
|
||
<TeammatesSection
|
||
teammates={teammates}
|
||
onAdd={handleAddTeammate}
|
||
onRemove={handleRemoveTeammate}
|
||
onChange={handleTeammateChange}
|
||
/>
|
||
)}
|
||
```
|
||
|
||
**ویژگیها:**
|
||
- فقط بر اساس `topicConfig.requiresTeammates` نمایش داده میشود
|
||
- هیچ ارتباطی با `mediaType` ندارد
|
||
- هیچ ارتباطی با media uploads ندارد
|
||
- اگر نمایش داده شود، کاملاً مستقل عمل میکند
|
||
|
||
---
|
||
|
||
## سناریوهای تست
|
||
|
||
### ✅ سناریو 1: تعویض Media Type
|
||
```
|
||
1. کاربر 3 همتیمی اضافه میکند
|
||
2. کاربر از "عکس" به "ویدیو" تغییر میدهد
|
||
3. نتیجه: همتیمیها حفظ میشوند ✅
|
||
```
|
||
|
||
### ✅ سناریو 2: آپلود تصویر
|
||
```
|
||
1. کاربر 2 همتیمی اضافه میکند
|
||
2. کاربر یک تصویر آپلود میکند
|
||
3. نتیجه: همتیمیها حفظ میشوند ✅
|
||
```
|
||
|
||
### ✅ سناریو 3: حذف تصویر
|
||
```
|
||
1. کاربر همتیمی وارد میکند
|
||
2. کاربر تصویر آپلود و سپس حذف میکند
|
||
3. نتیجه: اطلاعات همتیمی حفظ میشود ✅
|
||
```
|
||
|
||
### ✅ سناریو 4: آپلود ویدیو + کاور
|
||
```
|
||
1. کاربر 4 همتیمی با شماره تلفن وارد میکند
|
||
2. کاربر ویدیو آپلود میکند
|
||
3. کاربر کاور ویدیو آپلود میکند
|
||
4. نتیجه: تمام اطلاعات همتیمیها حفظ میشوند ✅
|
||
```
|
||
|
||
### ✅ سناریو 5: تعویض چند باره
|
||
```
|
||
1. کاربر همتیمی اضافه میکند
|
||
2. کاربر 10 بار بین عکس/ویدیو تعویض میکند
|
||
3. نتیجه: همتیمیها دستنخورده باقی میمانند ✅
|
||
```
|
||
|
||
---
|
||
|
||
## کد بررسی سریع
|
||
|
||
### ❌ الگوهای ممنوع (که وجود ندارند):
|
||
```typescript
|
||
// ❌ این الگوها در کد وجود ندارند:
|
||
const handleSwitchMediaType = () => {
|
||
setTeammates([""]); // ❌ NEVER!
|
||
};
|
||
|
||
const handleImageUpload = () => {
|
||
setTeammates([]); // ❌ NEVER!
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (mediaType === "video") {
|
||
setTeammates([""]); // ❌ NEVER!
|
||
}
|
||
}, [mediaType]);
|
||
```
|
||
|
||
### ✅ الگوهای صحیح (که پیادهسازی شده):
|
||
```typescript
|
||
// ✅ teammates فقط توسط teammate handlers تغییر میکند
|
||
const handleAddTeammate = () => setTeammates([...teammates, ""]);
|
||
const handleRemoveTeammate = (index) => { /* ... */ };
|
||
const handleTeammateChange = (index, value) => { /* ... */ };
|
||
|
||
// ✅ media handlers فقط media state را تغییر میدهند
|
||
const handleSwitchMediaType = () => {
|
||
setMediaType(type);
|
||
setUploadedImage(null);
|
||
setUploadedVideo(null);
|
||
setVideoCover(null);
|
||
// teammates untouched!
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## دیاگرام استقلال
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ Form Component │
|
||
├─────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ Media State │ │Teammates State│ │
|
||
│ │ │ │ │ │
|
||
│ │ mediaType │ │ teammates[] │ │
|
||
│ │ uploadedImage│ │ │ │
|
||
│ │ uploadedVideo│ │ │ │
|
||
│ │ videoCover │ │ │ │
|
||
│ └──────┬───────┘ └───────┬───────┘ │
|
||
│ │ NO │ │
|
||
│ │ INTERACTION │ │
|
||
│ │ ✘ │ │
|
||
│ └─────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │Media Handlers│ │Teammate │ │
|
||
│ │ │ │Handlers │ │
|
||
│ │ handleSwitch │ │ handleAdd │ │
|
||
│ │ handleImage │ │ handleRemove │ │
|
||
│ │ handleVideo │ │ handleChange │ │
|
||
│ │ handleCover │ │ │ │
|
||
│ └──────────────┘ └──────────────┘ │
|
||
│ ↓ ↓ │
|
||
│ ONLY Media ONLY Teammates │
|
||
│ │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## تضمینهای معماری
|
||
|
||
### ✅ تضمین 1: State Isolation
|
||
- State های media و teammates جدا از هم هستند
|
||
- هیچ shared state وجود ندارد
|
||
|
||
### ✅ تضمین 2: Handler Isolation
|
||
- Media handlers فقط media state را تغییر میدهند
|
||
- Teammate handlers فقط teammate state را تغییر میدهند
|
||
|
||
### ✅ تضمین 3: No Side Effects
|
||
- تغییر mediaType هیچ side effect روی teammates ندارد
|
||
- آپلود/حذف media هیچ side effect روی teammates ندارد
|
||
|
||
### ✅ تضمین 4: Independent Rendering
|
||
- نمایش TeammatesSection فقط به config بستگی دارد
|
||
- نه به mediaType، نه به uploaded files
|
||
|
||
### ✅ تضمین 5: Data Integrity
|
||
- اطلاعات teammates تا زمان submit حفظ میشود
|
||
- فقط کاربر میتواند teammates را تغییر دهد
|
||
- هیچ کد دیگری teammates را reset نمیکند
|
||
|
||
---
|
||
|
||
## نتیجهگیری
|
||
|
||
✅ **بخش teammates کاملاً مستقل است**
|
||
|
||
- ❌ هیچ وابستگی به media type
|
||
- ❌ هیچ وابستگی به uploaded files
|
||
- ❌ هیچ reset خودکار
|
||
- ❌ هیچ side effect
|
||
- ✅ کنترل کامل توسط کاربر
|
||
- ✅ Data integrity تضمین شده
|
||
- ✅ معماری تمیز و قابل نگهداری
|
||
|
||
---
|
||
|
||
**این استقلال باعث میشود:**
|
||
- کاربر بتواند آزادانه بین media type ها تعویض کند
|
||
- اطلاعات همتیمیها هیچوقت از دست نرود
|
||
- فرم رفتار قابل پیشبینی داشته باشد
|
||
- باگهای مربوط به state management کاهش یابد
|
||
|
||
**تست شده و تضمین شده** ✅
|