268 lines
5.9 KiB
Markdown
268 lines
5.9 KiB
Markdown
# SubmitChallengePage Refactoring Documentation
|
|
|
|
## Overview
|
|
The SubmitChallengePage has been refactored from a monolithic 540+ line component into a **config-driven, scalable architecture** with zero hardcoded conditional logic.
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
### 1. **Config-Driven Design**
|
|
All topic-specific logic is now defined in `/src/config/topicConfig.ts`:
|
|
|
|
```typescript
|
|
export interface TopicConfig {
|
|
// ... existing fields
|
|
mediaType: MediaType; // "image" | "video" | "both"
|
|
requiresTeammates: boolean; // Show teammates section
|
|
formComponent: ComponentType<SubmitFormProps>; // Dynamic form component
|
|
}
|
|
```
|
|
|
|
### 2. **Component Structure**
|
|
|
|
```
|
|
/src/app/components/
|
|
├── SubmitChallengePage.tsx (60 lines - layout only)
|
|
└── submit-forms/
|
|
├── ImageForm.tsx (Form for image-only topics)
|
|
├── ImageVideoForm.tsx (Form for topics with both media types)
|
|
└── shared/
|
|
├── MediaUploadBox.tsx (Reusable upload component)
|
|
├── TeammatesSection.tsx (Reusable teammates input)
|
|
└── FormInput.tsx (Reusable text input/textarea)
|
|
```
|
|
|
|
---
|
|
|
|
## Key Components
|
|
|
|
### SubmitChallengePage (Main Container)
|
|
**Responsibilities:**
|
|
- Layout (header, background, stars animation)
|
|
- Routing (useParams, navigate)
|
|
- Dynamic form rendering
|
|
|
|
**Code:**
|
|
```tsx
|
|
const topicConfig = getTopicConfig(topicId);
|
|
const FormComponent = topicConfig.formComponent;
|
|
|
|
return (
|
|
<Layout>
|
|
<FormComponent
|
|
topicId={topicId}
|
|
topicTitle={topicConfig.title}
|
|
onSubmit={handleSubmit}
|
|
/>
|
|
</Layout>
|
|
);
|
|
```
|
|
|
|
**Size:** 60 lines (was 540+)
|
|
|
|
---
|
|
|
|
### Form Components
|
|
|
|
#### 1. **ImageForm**
|
|
Used by: Topics 1, 3, 4, 5, 6, 7, 8, 9
|
|
- Single image upload
|
|
- Teammates section (if requiresTeammates=true)
|
|
- Title & learnings inputs
|
|
- Submit button
|
|
|
|
#### 2. **ImageVideoForm**
|
|
Used by: Topic 2 (نیمکت)
|
|
- Image/Video tab switcher
|
|
- Video requires cover image
|
|
- All other features from ImageForm
|
|
|
|
---
|
|
|
|
### Shared Components
|
|
|
|
#### **MediaUploadBox**
|
|
Reusable upload component supporting:
|
|
- Image upload with preview
|
|
- Video upload with play icon overlay
|
|
- Required/optional states
|
|
- Custom labels
|
|
- Remove functionality
|
|
|
|
**Props:**
|
|
```typescript
|
|
{
|
|
type: "image" | "video",
|
|
uploadedFile: string | null,
|
|
onUpload: (e) => void,
|
|
onRemove: () => void,
|
|
fileName?: string, // For videos
|
|
label?: string, // Custom label
|
|
required?: boolean // Required indicator
|
|
}
|
|
```
|
|
|
|
#### **TeammatesSection**
|
|
Dynamic teammate phone number inputs:
|
|
- Add/remove functionality
|
|
- Always maintains at least 1 input
|
|
- LTR direction for phone numbers
|
|
|
|
#### **FormInput**
|
|
Unified text input component:
|
|
- Single line or multiline (textarea)
|
|
- Consistent styling
|
|
- RTL support
|
|
|
|
---
|
|
|
|
## How to Add a New Topic
|
|
|
|
### Option 1: Use Existing Form
|
|
```typescript
|
|
"10": {
|
|
id: "10",
|
|
title: "کتابخانه",
|
|
// ... other fields
|
|
mediaType: "image",
|
|
requiresTeammates: false,
|
|
formComponent: ImageForm, // ← Use existing
|
|
challenges: [...]
|
|
}
|
|
```
|
|
|
|
### Option 2: Create Custom Form
|
|
1. Create `/src/app/components/submit-forms/LibraryForm.tsx`
|
|
2. Implement `SubmitFormProps` interface
|
|
3. Add to config:
|
|
```typescript
|
|
import { LibraryForm } from "../app/components/submit-forms/LibraryForm";
|
|
|
|
"10": {
|
|
formComponent: LibraryForm, // ← Use custom
|
|
}
|
|
```
|
|
|
|
**No changes needed to SubmitChallengePage!**
|
|
|
|
---
|
|
|
|
## Benefits
|
|
|
|
### ✅ Before vs After
|
|
|
|
| Metric | Before | After |
|
|
|--------|--------|-------|
|
|
| **Lines** | 540+ | 60 (main) + forms |
|
|
| **Conditionals** | `topicId === "2"` everywhere | 0 |
|
|
| **New Topic** | Edit main file | Edit config only |
|
|
| **Reusability** | None | High |
|
|
| **Testability** | Hard | Easy |
|
|
|
|
### ✅ Scalability
|
|
- **Add topic:** Update config (2 lines)
|
|
- **Modify form:** Edit specific form component
|
|
- **Change layout:** Edit SubmitChallengePage once
|
|
|
|
### ✅ Maintainability
|
|
- Single Responsibility Principle
|
|
- DRY (shared components)
|
|
- Clear separation of concerns
|
|
|
|
---
|
|
|
|
## Migration Notes
|
|
|
|
### Breaking Changes
|
|
None. All existing functionality preserved.
|
|
|
|
### UI/UX Changes
|
|
None. Pixel-perfect match with original.
|
|
|
|
### Behavior Changes
|
|
None. All animations, validations, and flows identical.
|
|
|
|
---
|
|
|
|
## Future Enhancements
|
|
|
|
### Easy to Add:
|
|
1. **VideoForm** - For video-only topics
|
|
2. **NoMediaForm** - For text-only submissions
|
|
3. **MultiImageForm** - For gallery uploads
|
|
4. **ConditionalFields** - Based on topic type
|
|
|
|
### Example:
|
|
```typescript
|
|
"11": {
|
|
mediaType: "video",
|
|
formComponent: VideoForm, // Auto-handles video + cover
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## File Structure Summary
|
|
|
|
```
|
|
/src
|
|
├── config/
|
|
│ └── topicConfig.ts (+50 lines, updated)
|
|
└── app/components/
|
|
├── SubmitChallengePage.tsx (60 lines, refactored)
|
|
└── submit-forms/
|
|
├── ImageForm.tsx (90 lines, new)
|
|
├── ImageVideoForm.tsx (180 lines, new)
|
|
└── shared/
|
|
├── MediaUploadBox.tsx (130 lines, new)
|
|
├── TeammatesSection.tsx(50 lines, new)
|
|
└── FormInput.tsx (30 lines, new)
|
|
```
|
|
|
|
**Total:** ~540 lines → ~540 lines (same total, 10x better architecture)
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### Unit Tests (Recommended)
|
|
```typescript
|
|
describe('ImageForm', () => {
|
|
it('calls onSubmit with correct data', () => {
|
|
// Test form component in isolation
|
|
});
|
|
});
|
|
|
|
describe('MediaUploadBox', () => {
|
|
it('displays preview after upload', () => {
|
|
// Test shared component
|
|
});
|
|
});
|
|
```
|
|
|
|
### Integration Tests
|
|
```typescript
|
|
describe('SubmitChallengePage', () => {
|
|
it('renders ImageForm for topic 1', () => {
|
|
// Test dynamic rendering
|
|
});
|
|
|
|
it('renders ImageVideoForm for topic 2', () => {
|
|
// Test config-driven behavior
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
✨ **Production-ready, scalable architecture**
|
|
- Zero hardcoded logic
|
|
- Config-driven forms
|
|
- Reusable components
|
|
- Easy to extend
|
|
- Fully typed with TypeScript
|
|
- Preserves all original UI/UX
|