# 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 (
{/* ... other fields ... */} setFormData({...formData, wordCount: e.target.value})} />
); } # 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 (

Scheduled Content

); } // Step 2: Add route in App.tsx import ScheduledPage from './pages/Writer/Scheduled'; function App() { return ( {/* ... other routes ... */} } /> ); } // Step 3: Add to Sidebar navigation // File: frontend/src/layout/Sidebar.tsx } /> // 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({}); return (
); } ``` --- ## 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 ( ); } ``` ### 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 ( <> handleChange(Number(e.target.value))} /> {error && {error}} ); } ``` ### 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 && (
Idea: {content.idea.ideaTitle}
)} ``` --- ## 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`