Files
igny8/old-docs/igny8-app/06-FEATURE-MODIFICATION-DEVELOPER-GUIDE.md
2025-12-07 16:49:30 +05:00

1122 lines
34 KiB
Markdown

# Feature Modification & Development Guide
## Complete File Reference for Adding, Modifying, and Removing Features
**Version:** 1.0.0
**Last Updated:** November 29, 2025
**Purpose:** Quick reference for developers to locate exactly which files to modify for specific feature types, across both frontend and backend.
---
## Table of Contents
1. [Quick Navigation by Feature Type](#quick-navigation-by-feature-type)
2. [Feature Type Reference Matrix](#feature-type-reference-matrix)
3. [Frontend Feature Modifications](#frontend-feature-modifications)
4. [Backend Feature Modifications](#backend-feature-modifications)
5. [Cross-Module Modifications](#cross-module-modifications)
6. [Database Schema Changes](#database-schema-changes)
7. [Common Workflow Patterns](#common-workflow-patterns)
8. [File Organization Index](#file-organization-index)
---
## Quick Navigation by Feature Type
### 🎯 Find Your Feature Type
- **Adding/Removing/Modifying a Database Column** → [Database Changes](#database-changes)
- **Adding a New Page/List View** → [New Page Feature](#new-page-feature)
- **Adding a Column to a Table/List** → [Table Column Addition](#table-column-addition)
- **Adding a Form Field** → [Form Field Addition](#form-field-addition)
- **Adding an API Endpoint** → [API Endpoint Addition](#api-endpoint-addition)
- **Adding a Filter/Search** → [Filter/Search Addition](#filtersearch-addition)
- **Adding a Status/Choice Field** → [Status Field Addition](#status-field-addition)
- **Modifying API Response** → [API Response Modification](#api-response-modification)
- **Adding Validation** → [Validation Addition](#validation-addition)
- **Adding Background Task** → [Background Task Addition](#background-task-addition)
- **Adding Webhook** → [Webhook Addition](#webhook-addition)
- **Adding Integration** → [Integration Addition](#integration-addition)
---
## Feature Type Reference Matrix
| Feature Type | Frontend Files | Backend Files | Database | Global Config |
|--------------|---|---|---|---|
| **Database Column** | - | Model + Serializer | Migration | - |
| **Table Column** | List Component | ViewSet Filter | - | - |
| **Form Field** | Form Component | Serializer | Model Migration | - |
| **Page/View** | Page Component | ViewSet | - | Routes |
| **API Endpoint** | API Hook | ViewSet + Router | - | urls.py |
| **Status Field** | Select/Enum | Model Choice | Migration | - |
| **Filter/Search** | Filter UI | filterset_fields | Index | - |
| **Validation** | Form Rules | Validator Service | - | - |
| **Background Task** | UI Trigger | Celery Task | - | settings.py |
| **Webhook** | - | Handler Function | WebhookLog Model | urls.py |
| **Integration** | - | Adapter Class | Integration Model | - |
---
## Frontend Feature Modifications
### Directory Structure
```
frontend/src/
├── pages/
│ ├── Planner/
│ │ ├── Dashboard.tsx
│ │ ├── Keywords.tsx
│ │ ├── Clusters.tsx
│ │ ├── ClusterDetail.tsx
│ │ ├── Ideas.tsx
│ │ └── KeywordOpportunities.tsx
│ ├── Writer/
│ │ ├── Dashboard.tsx
│ │ ├── Tasks.tsx
│ │ ├── Content.tsx
│ │ ├── ContentView.tsx
│ │ ├── Drafts.tsx
│ │ ├── Images.tsx
│ │ ├── Published.tsx
│ │ └── Review.tsx
│ ├── Linker/
│ │ └── [Linker Pages]
│ ├── Optimizer/
│ │ └── [Optimizer Pages]
│ ├── Settings/
│ │ └── [Settings Pages]
│ └── [Other Modules]
├── components/
│ ├── common/
│ │ ├── Table.tsx
│ │ ├── Modal.tsx
│ │ ├── Form.tsx
│ │ ├── Button.tsx
│ │ └── [Shared UI Components]
│ ├── [Module-Specific Components]
│ └── [Feature Components]
├── api/
│ ├── client.ts # Axios setup
│ ├── auth.ts # Auth API calls
│ ├── planner.ts # Planner endpoints
│ ├── writer.ts # Writer endpoints
│ ├── [module].ts # Module-specific APIs
│ └── hooks/
│ └── useApi.ts # API hook
├── store/
│ ├── authStore.ts # Auth state
│ ├── siteStore.ts # Site/Sector state
│ ├── [module]Store.ts # Module state
│ └── globalStore.ts # Global state
├── hooks/
│ ├── useAuth.ts
│ ├── useApi.ts
│ ├── useSite.ts
│ └── [Custom Hooks]
├── types/
│ ├── api.ts # API type definitions
│ ├── models.ts # Data model types
│ └── [Module Types]
└── utils/
├── formatters.ts
├── validators.ts
└── [Utility Functions]
```
### Feature: Adding a Column to a List/Table
**Module Example: Writer/Tasks List**
#### Files to Modify:
| Layer | File | Change Type | Action |
|-------|------|-------------|--------|
| **Data Type** | `frontend/src/types/models.ts` | Modify | Add field to Task interface |
| **API Response** | `frontend/src/api/writer.ts` | Auto | API will return new field from backend |
| **Page Component** | `frontend/src/pages/Writer/Tasks.tsx` | Modify | Add column to table columns config |
| **Table Component** | `frontend/src/components/Writer/TasksTable.tsx` | Modify | Render new column in JSX |
| **Backend ViewSet** | `backend/igny8_core/modules/writer/views.py` | Modify | Add to `filterset_fields` if filterable, `ordering_fields` if sortable |
| **Backend Serializer** | `backend/igny8_core/modules/writer/serializers.py` | Modify | Add field to serializer `fields` list |
| **Backend Model** | `backend/igny8_core/business/content/models.py` | Modify | Add field to Tasks model class |
| **Backend Migration** | `backend/igny8_core/migrations/` | Create | `python manage.py makemigrations` |
#### Step-by-Step for "Adding 'priority' column to Tasks list":
```python
# Step 1: Backend Model (models.py)
class Tasks(SiteSectorBaseModel):
# ... existing fields ...
priority = models.IntegerField(
choices=[(1, 'Low'), (2, 'Medium'), (3, 'High')],
default=2
)
# Step 2: Backend Serializer (serializers.py)
class TasksSerializer(serializers.ModelSerializer):
class Meta:
model = Tasks
fields = ['id', 'title', 'status', 'priority', ...] # Add 'priority'
# Step 3: Backend ViewSet (views.py) - if filterable/sortable
class TasksViewSet(SiteSectorModelViewSet):
filterset_fields = ['status', 'cluster_id', 'priority'] # Add 'priority'
ordering_fields = ['title', 'created_at', 'priority'] # Add 'priority'
# Step 4: Frontend Type (types/models.ts)
interface Task {
id: number;
title: string;
status: string;
priority: number; // Add this
// ... other fields
}
# Step 5: Frontend API (api/writer.ts) - No changes needed if using generic fetch
# Step 6: Frontend Component (pages/Writer/Tasks.tsx)
const columns = [
{ key: 'id', label: 'ID', width: '10%' },
{ key: 'title', label: 'Title', width: '40%' },
{ key: 'status', label: 'Status', width: '20%' },
{ key: 'priority', label: 'Priority', width: '15%' }, // Add this
];
# Step 7: Database Migration
# bash: python manage.py makemigrations
# bash: python manage.py migrate
```
### Feature: Adding a Form Field
**Module Example: Writer/Tasks Create Form**
#### Files to Modify:
| Layer | File | Change Type | Description |
|-------|------|-------------|-------------|
| **Backend Model** | `backend/.../models.py` | Add | New model field with validators |
| **Backend Serializer** | `backend/.../serializers.py` | Add | Serializer field with validation |
| **Backend ViewSet** | `backend/.../views.py` | Modify | Add to `filterset_fields` if needed |
| **Frontend Type** | `frontend/src/types/models.ts` | Add | Add to interface |
| **Frontend Form Component** | `frontend/src/pages/Writer/[FormPage].tsx` | Add | Add form input JSX |
| **Frontend Validation** | `frontend/src/utils/validators.ts` | Add | Client-side validation rule |
| **Frontend State** | `frontend/src/store/[module]Store.ts` | Add | Add to form state if needed |
| **Database Migration** | `backend/migrations/` | Create | If model field is added |
#### Example: "Adding 'estimatedWordCount' field to Tasks form":
```python
# Step 1: Backend Model
class Tasks(SiteSectorBaseModel):
# ... existing fields ...
word_count = models.IntegerField(
default=1000,
validators=[MinValueValidator(100), MaxValueValidator(10000)],
help_text="Target word count (100-10000)"
)
# Step 2: Backend Serializer
class TasksSerializer(serializers.ModelSerializer):
word_count = serializers.IntegerField(
required=False,
min_value=100,
max_value=10000
)
class Meta:
model = Tasks
fields = [..., 'word_count']
# Step 3: Frontend Type
interface Task {
id: number;
title: string;
wordCount: number; // Add this (camelCase)
}
# Step 4: Frontend Form Component
function TaskForm() {
const [formData, setFormData] = useState({
title: '',
wordCount: 1000, // Add this
});
return (
<form>
{/* ... other fields ... */}
<input
type="number"
name="wordCount"
label="Word Count"
min={100}
max={10000}
value={formData.wordCount}
onChange={e => setFormData({...formData, wordCount: e.target.value})}
/>
</form>
);
}
# Step 5: Frontend Validation
export const validateTaskForm = (data) => {
if (data.wordCount < 100 || data.wordCount > 10000) {
return 'Word count must be between 100 and 10000';
}
return null;
};
```
### Feature: Adding a New Page/View
**Module Example: Adding "Writer/Scheduled" Page**
#### Files to Create/Modify:
| Layer | File | Change Type | Action |
|-------|------|-------------|--------|
| **New Page Component** | `frontend/src/pages/Writer/Scheduled.tsx` | Create | New React component |
| **Page Router** | `frontend/src/App.tsx` | Modify | Add route entry |
| **Navigation** | `frontend/src/layout/Sidebar.tsx` | Modify | Add menu item |
| **API Hooks** | `frontend/src/api/writer.ts` | Modify | Add API call if needed |
| **Type Definitions** | `frontend/src/types/models.ts` | Modify | Add types if needed |
#### Step-by-Step for "Adding Writer/Scheduled Page":
```typescript
// Step 1: Create new page component
// File: frontend/src/pages/Writer/Scheduled.tsx
import React, { useEffect, useState } from 'react';
import { getScheduledContent } from '../../api/writer';
import ContentTable from '../../components/Writer/ContentTable';
export default function ScheduledPage() {
const [content, setContent] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadContent = async () => {
try {
const response = await getScheduledContent({
siteId: currentSite.id,
sectorId: currentSector.id,
status: 'scheduled'
});
setContent(response.data);
} catch (error) {
console.error('Failed to load scheduled content:', error);
} finally {
setLoading(false);
}
};
loadContent();
}, []);
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-6">Scheduled Content</h1>
<ContentTable content={content} loading={loading} />
</div>
);
}
// Step 2: Add route in App.tsx
import ScheduledPage from './pages/Writer/Scheduled';
function App() {
return (
<Routes>
{/* ... other routes ... */}
<Route path="/writer/scheduled" element={<ScheduledPage />} />
</Routes>
);
}
// Step 3: Add to Sidebar navigation
// File: frontend/src/layout/Sidebar.tsx
<NavItem
to="/writer/scheduled"
label="Scheduled"
icon={<CalendarIcon />}
/>
// Step 4: Add API function if needed
// File: frontend/src/api/writer.ts
export const getScheduledContent = async (params) => {
return fetchAPI('/api/v1/writer/content/', {
query: { ...params, status: 'scheduled' }
});
};
```
### Feature: Adding a Filter/Search
**Module Example: Adding "Priority Filter" to Tasks**
#### Files to Modify:
| Layer | File | Change Type | Description |
|-------|------|-------------|-------------|
| **Backend ViewSet** | `backend/.../views.py` | Modify | Add to `filterset_fields` |
| **Backend Model** | `backend/.../models.py` | Check | Field must have choices |
| **Frontend Filter UI** | `frontend/src/components/Writer/TasksFilter.tsx` | Add | Add filter control |
| **Frontend State** | `frontend/src/store/writerStore.ts` | Modify | Add filter state |
#### Example:
```python
# Backend (views.py)
class TasksViewSet(SiteSectorModelViewSet):
filterset_fields = ['status', 'cluster_id', 'priority'] # Add 'priority'
# Frontend - Add Filter Component
interface TaskFilter {
priority?: number;
status?: string;
}
function TasksFilter() {
const [filters, setFilters] = useState<TaskFilter>({});
return (
<div className="flex gap-4 mb-6">
<select
value={filters.priority || ''}
onChange={(e) => setFilters({...filters, priority: e.target.value})}
>
<option value="">All Priorities</option>
<option value="1">Low</option>
<option value="2">Medium</option>
<option value="3">High</option>
</select>
</div>
);
}
```
---
## Backend Feature Modifications
### Directory Structure
```
backend/igny8_core/
├── modules/ # API ViewSets (REST endpoints)
│ ├── planner/
│ │ ├── views.py # ViewSets
│ │ ├── serializers.py # Serializers
│ │ ├── urls.py # Route definitions
│ │ └── apps.py
│ ├── writer/
│ │ ├── views.py
│ │ ├── serializers.py
│ │ ├── urls.py
│ │ └── apps.py
│ ├── [other modules]
│ └── urls.py # Root routing
├── business/ # Business logic & models
│ ├── planning/
│ │ ├── models.py # Database models
│ │ └── services/ # Business logic
│ ├── content/
│ │ ├── models.py
│ │ └── services/
│ ├── [other domains]
│ └── models.py
├── auth/
│ ├── models.py # User, Account, Site, Sector
│ ├── views.py
│ ├── serializers.py
│ └── urls.py
├── api/
│ ├── base.py # Base ViewSet classes
│ ├── response.py # Response utilities
│ ├── pagination.py
│ ├── permissions.py
│ ├── throttles.py
│ └── exception_handlers.py
├── ai/ # AI Engine
│ ├── engine.py
│ ├── ai_core.py
│ ├── registry.py
│ └── functions/
├── middleware/
├── utils/
├── tasks/ # Celery tasks
├── migrations/ # Database migrations
├── settings.py # Django settings
├── urls.py # Root URL config
└── wsgi.py
```
### Feature: Adding an API Endpoint
**Module Example: Adding "GET /writer/tasks/{id}/brief/" endpoint**
#### Files to Create/Modify:
| File | Change Type | Description |
|------|-------------|-------------|
| `backend/igny8_core/modules/writer/views.py` | Add | Add `@action` method to ViewSet |
| `backend/igny8_core/modules/writer/serializers.py` | Check | Serializer for response |
| `backend/igny8_core/modules/writer/urls.py` | Auto | Routes auto-generated by DRF `DefaultRouter` |
#### Step-by-Step Example:
```python
# Step 1: Add action to ViewSet (views.py)
class TasksViewSet(SiteSectorModelViewSet):
# ... existing code ...
@action(detail=True, methods=['get'])
def brief(self, request, pk=None):
"""
Get task brief
GET /api/v1/writer/tasks/{id}/brief/
"""
task = self.get_object()
# Business logic to generate brief
brief_data = {
'task_id': task.id,
'title': task.title,
'keywords': task.keywords,
'cluster': task.cluster.name if task.cluster else None,
'description': task.description
}
return success_response(
success=True,
data=brief_data,
message='Task brief retrieved'
)
# Step 2: That's it! DRF auto-generates the route
# URL will be: /api/v1/writer/tasks/123/brief/
# Method: GET
# Response format: { success: true, data: {...}, message: '...' }
# Step 3: Frontend API hook
// frontend/src/api/writer.ts
export const getTaskBrief = async (taskId: number) => {
return fetchAPI(`/api/v1/writer/tasks/${taskId}/brief/`);
};
```
### Feature: Adding a Status/Choice Field
**Module Example: Adding "priority" choice field to Tasks**
#### Files to Modify:
| Layer | File | Change Type | Action |
|-------|------|-------------|--------|
| **Backend Model** | `backend/.../models.py` | Add | Add field with CHOICES |
| **Database Migration** | `backend/migrations/` | Create | Auto from model |
| **Backend Serializer** | `backend/.../serializers.py` | Auto | Choices auto-included |
| **Backend ViewSet** | `backend/.../views.py` | Modify | Add to filters if needed |
| **Frontend Types** | `frontend/src/types/models.ts` | Add | Add type definition |
| **Frontend Enum/Constant** | `frontend/src/config/constants.ts` | Add | Add choice options |
| **Frontend UI** | Components using field | Use | Render as select/dropdown |
#### Example:
```python
# Step 1: Backend Model (models.py)
class Tasks(SiteSectorBaseModel):
PRIORITY_CHOICES = [
(1, 'Low'),
(2, 'Medium'),
(3, 'High'),
(4, 'Urgent'),
]
priority = models.IntegerField(
choices=PRIORITY_CHOICES,
default=2,
help_text="Task priority level"
)
# Step 2: Backend Serializer (serializers.py) - No changes needed!
# DRF automatically includes choices in API response as:
# {
# "priority": 2,
# "priority_display": "Medium",
# "priority_choices": [[1, "Low"], [2, "Medium"], ...]
# }
# Step 3: Frontend Constant (config/constants.ts)
export const TASK_PRIORITIES = {
LOW: 1,
MEDIUM: 2,
HIGH: 3,
URGENT: 4,
};
export const TASK_PRIORITY_LABELS = {
1: 'Low',
2: 'Medium',
3: 'High',
4: 'Urgent',
};
# Step 4: Frontend Type (types/models.ts)
interface Task {
id: number;
priority: 1 | 2 | 3 | 4; // Use enum values
}
# Step 5: Frontend Component
function TaskSelect() {
return (
<select value={task.priority}>
<option value={1}>Low</option>
<option value={2}>Medium</option>
<option value={3}>High</option>
<option value={4}>Urgent</option>
</select>
);
}
```
### Feature: Adding Validation
**Module Example: Validating task word count (100-10000)**
#### Files to Create/Modify:
| File | Change Type | Description |
|------|-------------|-------------|
| `backend/igny8_core/business/content/services/validation_service.py` | Create | Validation logic |
| `backend/igny8_core/modules/writer/serializers.py` | Modify | Add field validation |
| `frontend/src/utils/validators.ts` | Add | Client-side validation |
#### Example:
```python
# Step 1: Backend Model Field Validators (models.py)
from django.core.validators import MinValueValidator, MaxValueValidator
class Tasks(SiteSectorBaseModel):
word_count = models.IntegerField(
default=1000,
validators=[
MinValueValidator(100, message="Minimum word count is 100"),
MaxValueValidator(10000, message="Maximum word count is 10000")
]
)
# Step 2: Backend Serializer Validation (serializers.py)
class TasksSerializer(serializers.ModelSerializer):
word_count = serializers.IntegerField(
min_value=100,
max_value=10000,
required=False
)
def validate_word_count(self, value):
if value and (value < 100 or value > 10000):
raise serializers.ValidationError(
"Word count must be between 100 and 10000"
)
return value
class Meta:
model = Tasks
fields = ['id', 'title', 'word_count', ...]
# Step 3: Frontend Validation (utils/validators.ts)
export const validateTaskWordCount = (wordCount: number): string | null => {
if (wordCount < 100) {
return 'Word count must be at least 100';
}
if (wordCount > 10000) {
return 'Word count cannot exceed 10000';
}
return null;
};
# Step 4: Frontend Component
function TaskForm() {
const [wordCount, setWordCount] = useState(1000);
const [error, setError] = useState('');
const handleChange = (value) => {
setWordCount(value);
const err = validateTaskWordCount(value);
setError(err || '');
};
return (
<>
<input
type="number"
value={wordCount}
onChange={(e) => handleChange(Number(e.target.value))}
/>
{error && <span className="text-red-500">{error}</span>}
</>
);
}
```
### Feature: Adding a Background Task (Celery)
**Module Example: Adding "Async content generation" task**
#### Files to Create/Modify:
| File | Change Type | Description |
|------|-------------|-------------|
| `backend/igny8_core/tasks/[domain]_tasks.py` | Create | Celery task function |
| `backend/igny8_core/modules/[module]/views.py` | Modify | Queue task from ViewSet |
| `backend/igny8_core/settings.py` | Check | Celery config (usually pre-configured) |
#### Example: Adding "generate_content_images" async task
```python
# Step 1: Create Celery Task (tasks/writer_tasks.py)
from celery import shared_task
import logging
logger = logging.getLogger(__name__)
@shared_task(bind=True, max_retries=3)
def generate_content_images(self, content_id):
"""
Generate AI images for content
"""
from igny8_core.business.content.models import Content
from igny8_core.ai.engine import AIEngine
logger.info(f"[generate_content_images] 🎬 Starting image generation for content {content_id}")
try:
content = Content.objects.get(id=content_id)
# Generate images
engine = AIEngine()
images = engine.generate_images(
content_id=content.id,
title=content.title,
keywords=content.secondary_keywords
)
# Save images
for image_data in images:
Image.objects.create(
content=content,
url=image_data['url'],
prompt=image_data['prompt']
)
logger.info(f"[generate_content_images] ✅ Successfully generated {len(images)} images")
return {'success': True, 'image_count': len(images)}
except Content.DoesNotExist:
logger.error(f"[generate_content_images] ❌ Content {content_id} not found")
return {'success': False, 'error': 'Content not found'}
except Exception as exc:
logger.error(f"[generate_content_images] ❌ Task failed: {str(exc)}")
# Retry with exponential backoff
raise self.retry(exc=exc, countdown=2 ** self.request.retries)
# Step 2: Queue task from ViewSet (views.py)
class ContentViewSet(SiteSectorModelViewSet):
@action(detail=True, methods=['post'])
def generate_images(self, request, pk=None):
"""
Trigger async image generation
POST /api/v1/writer/content/{id}/generate_images/
"""
content = self.get_object()
# Queue background task
from igny8_core.tasks.writer_tasks import generate_content_images
task = generate_content_images.delay(content.id)
return success_response(
success=True,
data={'task_id': task.id},
message='Image generation started'
)
# Step 3: Frontend to trigger task
// frontend/src/api/writer.ts
export const generateContentImages = async (contentId: number) => {
return fetchAPI(`/api/v1/writer/content/${contentId}/generate_images/`, {
method: 'POST'
});
};
// frontend/src/pages/Writer/Content.tsx
async function handleGenerateImages(contentId) {
try {
const response = await generateContentImages(contentId);
toast.success('Image generation started');
} catch (error) {
toast.error('Failed to start image generation');
}
}
```
---
## Database Schema Changes
### Feature: Adding/Modifying a Database Column
#### Complete Workflow:
```bash
# Step 1: Add field to model
# File: backend/igny8_core/business/content/models.py
class Content(SiteSectorBaseModel):
# ... existing fields ...
seo_score = models.IntegerField(
default=0,
validators=[MinValueValidator(0), MaxValueValidator(100)],
help_text="SEO optimization score (0-100)"
)
# Step 2: Create migration
$ python manage.py makemigrations writer
# Output: Migrations for 'writer':
# migrations/0015_content_seo_score.py
# Step 3: Review migration (optional)
$ cat migrations/0015_content_seo_score.py
# Step 4: Apply migration
$ python manage.py migrate
# Step 5: Update serializer to include new field
# File: backend/igny8_core/modules/writer/serializers.py
class ContentSerializer(serializers.ModelSerializer):
class Meta:
model = Content
fields = [
'id', 'title', 'content_html', 'status',
'seo_score', # Add this
# ... other fields ...
]
# Step 6: Update frontend types
// frontend/src/types/models.ts
interface Content {
id: number;
title: string;
contentHtml: string;
seoScore: number; // Add this
}
# Step 7: Update components to use new field
```
### Feature: Removing a Database Column
```python
# Step 1: Create migration to remove field
$ python manage.py makemigrations writer --remove-field
# Step 2: Or manually edit model - comment out field
class Content(SiteSectorBaseModel):
# ... existing fields ...
# seo_score = models.IntegerField(...) # REMOVED
# Step 3: Auto-create migration
$ python manage.py makemigrations writer
# Step 4: Apply migration
$ python manage.py migrate
# Step 5: Remove from serializer
# backend/igny8_core/modules/writer/serializers.py
# Remove 'seo_score' from fields list
# Step 6: Remove from frontend types and components
```
### Feature: Modifying a Database Column
```python
# Example: Change word_count max value from 10000 to 20000
# Step 1: Update model
class Tasks(SiteSectorBaseModel):
word_count = models.IntegerField(
default=1000,
validators=[MinValueValidator(100), MaxValueValidator(20000)] # Changed
)
# Step 2: Create migration
$ python manage.py makemigrations writer
# Step 3: Apply migration
$ python manage.py migrate
# Step 4: Update serializer if validation changed
class TasksSerializer(serializers.ModelSerializer):
word_count = serializers.IntegerField(
min_value=100,
max_value=20000 # Update
)
# Step 5: Update frontend validation constants
export const MAX_WORD_COUNT = 20000; // Update
```
---
## Cross-Module Modifications
### Feature: Adding a Relationship Between Modules
**Example: Link Content to Ideas (Writer → Planner)**
#### Files to Modify:
```python
# Step 1: Backend Model - Add ForeignKey
# backend/igny8_core/business/content/models.py
class Content(SiteSectorBaseModel):
# ... existing fields ...
idea = models.ForeignKey(
'planner.ContentIdeas',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='contents',
help_text="Source content idea"
)
# Step 2: Create migration
$ python manage.py makemigrations writer
# Step 3: Apply migration
$ python manage.py migrate
# Step 4: Update serializer to include nested relationship
# backend/igny8_core/modules/writer/serializers.py
from igny8_core.modules.planner.serializers import ContentIdeasSerializer
class ContentSerializer(serializers.ModelSerializer):
idea = ContentIdeasSerializer(read_only=True)
idea_id = serializers.PrimaryKeyRelatedField(
queryset=ContentIdeas.objects.all(),
source='idea',
write_only=True
)
class Meta:
model = Content
fields = ['id', 'title', 'idea', 'idea_id', ...]
# Step 5: Update ViewSet to support filtering by idea
# backend/igny8_core/modules/writer/views.py
class ContentViewSet(SiteSectorModelViewSet):
filterset_fields = ['status', 'cluster_id', 'idea_id'] # Add idea_id
ordering_fields = ['title', 'created_at', 'idea_id'] # Add idea_id
# Step 6: Frontend - Update type
// frontend/src/types/models.ts
interface Content {
id: number;
title: string;
idea?: Idea; // Add nested object
ideaId?: number; // Add ID for create/update
}
# Step 7: Frontend - Display related idea
// frontend/src/pages/Writer/Content.tsx
{content.idea && (
<div>Idea: {content.idea.ideaTitle}</div>
)}
```
---
## Common Workflow Patterns
### Pattern 1: Adding a Simple Field to List Page
**Timeline: ~15 minutes**
```
1. Backend Model → Add field with validators
2. DB Migration → makemigrations + migrate
3. Backend Serializer → Add to fields list
4. Backend ViewSet → Add to filters/ordering if needed
5. Frontend Type → Add to interface
6. Frontend Component → Add to table columns
7. Test → Verify in both UI and API
```
**Files Modified:**
- `backend/igny8_core/business/*/models.py`
- `backend/igny8_core/migrations/XXXX_*.py`
- `backend/igny8_core/modules/*/serializers.py`
- `backend/igny8_core/modules/*/views.py`
- `frontend/src/types/models.ts`
- `frontend/src/pages/*/[Page].tsx`
### Pattern 2: Adding a New Feature Page
**Timeline: ~1 hour**
```
1. Backend ViewSet/Action → Create endpoint
2. Backend Serializer → Define response format
3. Frontend API Hook → Create fetch function
4. Frontend Type → Define data interface
5. Frontend Page Component → Create page
6. Frontend Router → Add route
7. Frontend Navigation → Add menu item
8. Test → End-to-end flow
```
**Files Modified:**
- `backend/igny8_core/modules/*/views.py`
- `backend/igny8_core/modules/*/serializers.py`
- `frontend/src/api/*.ts`
- `frontend/src/types/models.ts`
- `frontend/src/pages/*/*.tsx`
- `frontend/src/App.tsx`
- `frontend/src/layout/Sidebar.tsx`
### Pattern 3: Adding Async Processing
**Timeline: ~30 minutes**
```
1. Backend Celery Task → Create task function
2. Backend ViewSet Action → Queue task from endpoint
3. Frontend API Hook → Create trigger function
4. Frontend Component → Add button to trigger
5. Frontend Polling/Polling → Show task status
6. Test → Verify task execution and completion
```
**Files Modified:**
- `backend/igny8_core/tasks/*_tasks.py`
- `backend/igny8_core/modules/*/views.py`
- `frontend/src/api/*.ts`
- `frontend/src/pages/*/*.tsx`
---
## File Organization Index
### Module File Structure Template
For each module (Planner, Writer, Linker, etc.):
```
backend/igny8_core/
├── modules/[module]/
│ ├── views.py # ViewSets, @actions
│ ├── serializers.py # Serializers
│ ├── urls.py # Route registration
│ └── apps.py
└── business/[domain]/
├── models.py # Data models
├── services/
│ ├── service1.py # Business logic
│ └── service2.py
└── migrations/
└── XXXX_*.py # DB migrations
frontend/src/
├── pages/[Module]/
│ ├── Dashboard.tsx # Overview page
│ ├── [Feature].tsx # Feature pages
│ └── [Detail].tsx # Detail views
├── components/[Module]/
│ ├── [Feature]Table.tsx # Table components
│ ├── [Feature]Form.tsx # Form components
│ └── [Feature]Filter.tsx # Filter components
├── api/
│ └── [module].ts # API calls
├── store/
│ └── [module]Store.ts # State management
└── types/
└── models.ts # Type definitions
```
### Global Files (All Modules)
| File | Purpose | Where Located |
|------|---------|---------------|
| **API Base Classes** | ViewSet, Serializer base | `backend/igny8_core/api/base.py` |
| **Response Format** | Unified responses | `backend/igny8_core/api/response.py` |
| **Permissions** | Role-based access | `backend/igny8_core/api/permissions.py` |
| **Pagination** | List pagination | `backend/igny8_core/api/pagination.py` |
| **Root Router** | Route registration | `backend/igny8_core/urls.py` |
| **Settings** | Django config | `backend/igny8_core/settings.py` |
| **Auth Models** | User, Site, Sector | `backend/igny8_core/auth/models.py` |
| **API Client** | Frontend HTTP | `frontend/src/api/client.ts` |
| **Auth Store** | User state | `frontend/src/store/authStore.ts` |
| **Site Store** | Site/Sector context | `frontend/src/store/siteStore.ts` |
| **App Router** | Frontend routes | `frontend/src/App.tsx` |
| **Sidebar** | Navigation menu | `frontend/src/layout/Sidebar.tsx` |
| **Constants** | Global constants | `frontend/src/config/constants.ts` |
| **Validators** | Validation rules | `frontend/src/utils/validators.ts` |
---
## Quick Checklists
### Adding a New Column: Checklist
- [ ] Add field to Backend Model (`models.py`)
- [ ] Run `makemigrations`
- [ ] Run `migrate`
- [ ] Add field to Backend Serializer (`serializers.py`)
- [ ] Add to ViewSet `filterset_fields` if filterable
- [ ] Add to ViewSet `ordering_fields` if sortable
- [ ] Add to Frontend Type (`types/models.ts`)
- [ ] Add to Frontend Component columns config
- [ ] Add to Frontend Table JSX rendering
- [ ] Test API response includes field
- [ ] Test frontend displays column
- [ ] Test filter/sort works (if applicable)
### Adding a New Page: Checklist
- [ ] Create new Page component (`pages/[Module]/[Page].tsx`)
- [ ] Create or update API hook (`api/[module].ts`)
- [ ] Add route to router (`App.tsx`)
- [ ] Add menu item to navigation (`layout/Sidebar.tsx`)
- [ ] Add types if needed (`types/models.ts`)
- [ ] Test page loads
- [ ] Test data fetches
- [ ] Test filters/search work
- [ ] Test pagination works
- [ ] Test error states
### Adding an API Endpoint: Checklist
- [ ] Define endpoint action in ViewSet (`views.py`)
- [ ] Use `@action(detail=...)` decorator
- [ ] Define response format using serializer
- [ ] Add permission check if needed
- [ ] Document docstring
- [ ] Test endpoint with API client
- [ ] Verify unified response format
- [ ] Create frontend API hook (`api/[module].ts`)
- [ ] Test from frontend component
- [ ] Verify error handling
---
**End of Document**
For questions on specific features, refer to:
- Architecture: `00-SYSTEM-ARCHITECTURE-MASTER-REFERENCE.md`
- API Details: `01-IGNY8-REST-API-COMPLETE-REFERENCE.md`
- Workflows: `02-PLANNER-WRITER-WORKFLOW-TECHNICAL-GUIDE.md`