diff --git a/docs/future-plan-to-remove-sites-site-builder-wrong-plan/00-SYSTEM-ARCHITECTURE-MASTER-REFERENCE.md b/docs/00-SYSTEM-ARCHITECTURE-MASTER-REFERENCE.md similarity index 100% rename from docs/future-plan-to-remove-sites-site-builder-wrong-plan/00-SYSTEM-ARCHITECTURE-MASTER-REFERENCE.md rename to docs/00-SYSTEM-ARCHITECTURE-MASTER-REFERENCE.md diff --git a/docs/06-FEATURE-MODIFICATION-DEVELOPER-GUIDE.md b/docs/06-FEATURE-MODIFICATION-DEVELOPER-GUIDE.md new file mode 100644 index 00000000..a36508a9 --- /dev/null +++ b/docs/06-FEATURE-MODIFICATION-DEVELOPER-GUIDE.md @@ -0,0 +1,1121 @@ +# 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`