34 KiB
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
- Quick Navigation by Feature Type
- Feature Type Reference Matrix
- Frontend Feature Modifications
- Backend Feature Modifications
- Cross-Module Modifications
- Database Schema Changes
- Common Workflow Patterns
- File Organization Index
Quick Navigation by Feature Type
🎯 Find Your Feature Type
- Adding/Removing/Modifying a Database Column → Database Changes
- Adding a New Page/List View → New Page Feature
- Adding a Column to a Table/List → Table Column Addition
- Adding a Form Field → Form Field Addition
- Adding an API Endpoint → API Endpoint Addition
- Adding a Filter/Search → Filter/Search Addition
- Adding a Status/Choice Field → Status Field Addition
- Modifying API Response → API Response Modification
- Adding Validation → Validation Addition
- Adding Background Task → Background Task Addition
- Adding Webhook → Webhook Addition
- Adding Integration → 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":
# 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":
# 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":
// 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:
# 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:
# 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:
# 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:
# 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
# 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:
# 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
# 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
# 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:
# 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.pybackend/igny8_core/migrations/XXXX_*.pybackend/igny8_core/modules/*/serializers.pybackend/igny8_core/modules/*/views.pyfrontend/src/types/models.tsfrontend/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.pybackend/igny8_core/modules/*/serializers.pyfrontend/src/api/*.tsfrontend/src/types/models.tsfrontend/src/pages/*/*.tsxfrontend/src/App.tsxfrontend/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.pybackend/igny8_core/modules/*/views.pyfrontend/src/api/*.tsfrontend/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_fieldsif filterable - Add to ViewSet
ordering_fieldsif 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