docs cleanup
This commit is contained in:
8
.cursor/igny8.code-workspace
Normal file
8
.cursor/igny8.code-workspace
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": ".."
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
# Button Component Standardization - Complete
|
||||
|
||||
## Overview
|
||||
Fixed Button component and all Sites pages to match IGNY8 global design standard:
|
||||
- Secondary button color now uses `text-dim` (#64748b)
|
||||
- Removed ALL `Link` component usage from buttons
|
||||
- All buttons now use `onClick` navigation with `useNavigate()`
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Button Component (`/frontend/src/components/ui/button/Button.tsx`)
|
||||
|
||||
#### Removed Link/Anchor Support
|
||||
- **Removed imports**: `Link` from react-router-dom
|
||||
- **Removed props**: `as`, `href`, `to`, `target`, `rel`
|
||||
- **Simplified component**: Always renders `<button>` element (no conditional rendering)
|
||||
- **Result**: Button component ONLY supports `onClick` navigation
|
||||
|
||||
#### Fixed Secondary Color
|
||||
Changed ALL secondary variants to use text-dim color (#64748b):
|
||||
|
||||
**Before (WRONG):**
|
||||
```tsx
|
||||
brand: {
|
||||
secondary: "bg-brand-50 text-brand-600 hover:bg-brand-100"
|
||||
}
|
||||
success: {
|
||||
secondary: "bg-success-50 text-success-600 hover:bg-success-100"
|
||||
}
|
||||
// etc...
|
||||
```
|
||||
|
||||
**After (CORRECT):**
|
||||
```tsx
|
||||
brand: {
|
||||
secondary: "bg-transparent text-[#64748b] hover:bg-gray-100 dark:hover:bg-white/[0.08]"
|
||||
}
|
||||
success: {
|
||||
secondary: "bg-transparent text-[#64748b] hover:bg-gray-100 dark:hover:bg-white/[0.08]"
|
||||
}
|
||||
// etc...
|
||||
```
|
||||
|
||||
**Affected tones:**
|
||||
- ✅ brand
|
||||
- ✅ success
|
||||
- ✅ warning
|
||||
- ✅ danger
|
||||
- ✅ neutral (already correct)
|
||||
|
||||
### 2. Sites Pages - Removed All Link Usage
|
||||
|
||||
#### Dashboard.tsx (`/frontend/src/pages/Sites/Dashboard.tsx`)
|
||||
- **Removed import**: `Link` from react-router-dom
|
||||
- **Changed**: 5 Quick Action cards from `<Link to={...}>` to `<button onClick={() => navigate(...)}>>`
|
||||
- **Actions updated**:
|
||||
- Manage Pages
|
||||
- Manage Content
|
||||
- Integrations
|
||||
- Sync Dashboard
|
||||
- Deploy Site
|
||||
|
||||
**Before:**
|
||||
```tsx
|
||||
<Link to={`/sites/${siteId}/pages`} className="...">
|
||||
{/* card content */}
|
||||
</Link>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```tsx
|
||||
<button onClick={() => navigate(`/sites/${siteId}/pages`)} className="...">
|
||||
{/* card content */}
|
||||
</button>
|
||||
```
|
||||
|
||||
#### List.tsx (`/frontend/src/pages/Sites/List.tsx`)
|
||||
- **Removed import**: `Link` from react-router-dom
|
||||
- **Changed**: Site card buttons from `Button as={Link} to={...}` to `Button onClick={() => navigate(...)}`
|
||||
- **Buttons updated**:
|
||||
- Dashboard (primary variant)
|
||||
- Content (secondary variant)
|
||||
- Pages (secondary variant)
|
||||
- Settings (secondary variant)
|
||||
|
||||
**Before:**
|
||||
```tsx
|
||||
<Button
|
||||
as={Link}
|
||||
to={`/sites/${site.id}`}
|
||||
variant="primary"
|
||||
size="sm"
|
||||
startIcon={<EyeIcon className="w-4 h-4" />}
|
||||
>
|
||||
Dashboard
|
||||
</Button>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```tsx
|
||||
<Button
|
||||
onClick={() => navigate(`/sites/${site.id}`)}
|
||||
variant="primary"
|
||||
size="sm"
|
||||
startIcon={<EyeIcon className="w-4 h-4" />}
|
||||
>
|
||||
Dashboard
|
||||
</Button>
|
||||
```
|
||||
|
||||
#### Content.tsx (`/frontend/src/pages/Sites/Content.tsx`)
|
||||
- **Removed import**: `Link` from react-router-dom
|
||||
- **Changed**: 2 buttons from `Button as={Link}` to `Button onClick`
|
||||
- **Buttons updated**:
|
||||
- "New Post" button (top of page)
|
||||
- "Create Your First Post" button (empty state)
|
||||
|
||||
#### Editor.tsx (`/frontend/src/pages/Sites/Editor.tsx`)
|
||||
- **Removed import**: `Link` from react-router-dom (was imported but not yet used)
|
||||
|
||||
## Verification
|
||||
|
||||
### TypeScript Compilation
|
||||
✅ No errors found in:
|
||||
- `/frontend/src/components/ui/button/Button.tsx`
|
||||
- `/frontend/src/pages/Sites/Dashboard.tsx`
|
||||
- `/frontend/src/pages/Sites/List.tsx`
|
||||
- `/frontend/src/pages/Sites/Content.tsx`
|
||||
- `/frontend/src/pages/Sites/Editor.tsx`
|
||||
|
||||
### Code Search
|
||||
✅ No remaining instances of:
|
||||
- `<Button as={Link}` in Sites pages
|
||||
- `Button.*as={Link}` anywhere in frontend
|
||||
|
||||
## Button Standard Summary
|
||||
|
||||
### Correct Usage (ONLY allowed format)
|
||||
```tsx
|
||||
import { Button } from '@/components/ui/button/Button';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Simple button
|
||||
<Button onClick={() => navigate('/path')} variant="primary">
|
||||
Click Me
|
||||
</Button>
|
||||
|
||||
// Button with icon
|
||||
<Button
|
||||
onClick={() => navigate('/path')}
|
||||
variant="secondary"
|
||||
startIcon={<IconComponent className="w-4 h-4" />}
|
||||
>
|
||||
Action
|
||||
</Button>
|
||||
|
||||
// Multiple buttons (primary + secondary)
|
||||
<div className="flex gap-2">
|
||||
<Button onClick={handlePrimary} variant="primary">
|
||||
Primary Action
|
||||
</Button>
|
||||
<Button onClick={handleSecondary} variant="secondary">
|
||||
Secondary Action
|
||||
</Button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Variants
|
||||
- **primary**: Brand color background (#0693e3), white text
|
||||
- **secondary**: Text-dim color (#64748b), transparent background, gray hover
|
||||
- **outline**: Colored text with ring border
|
||||
- **ghost**: Colored text with subtle hover
|
||||
- **gradient**: Gradient background with shadow
|
||||
|
||||
### Tones
|
||||
- **brand**: Primary blue (#0693e3)
|
||||
- **success**: Teal-green (#0bbf87)
|
||||
- **warning**: Vivid orange (#ff7a00)
|
||||
- **danger**: Red (#ef4444)
|
||||
- **neutral**: Gray
|
||||
|
||||
### Sizes
|
||||
- **xs**: h-7 px-2.5 text-xs
|
||||
- **sm**: h-9 px-3 text-sm (most common)
|
||||
- **md**: h-10 px-4 text-sm
|
||||
- **lg**: h-12 px-5 text-base
|
||||
|
||||
## Color Reference
|
||||
|
||||
### Text-Dim Color
|
||||
```css
|
||||
/* tokens.css */
|
||||
--color-text-dim: #64748b; /* Light mode */
|
||||
|
||||
.dark {
|
||||
--color-text-dim: #9ca3af; /* Dark mode */
|
||||
}
|
||||
```
|
||||
|
||||
This is Tailwind's `slate-500` equivalent, used for:
|
||||
- Secondary button text
|
||||
- Subdued/helper text
|
||||
- Less prominent UI elements
|
||||
|
||||
## What Was Wrong Before
|
||||
|
||||
1. **Secondary color mismatch**: Used tone-specific colors (brand-600, success-600, etc.) instead of universal text-dim
|
||||
2. **Link component usage**: Button component had `as={Link}` prop support (accessibility anti-pattern for navigation buttons)
|
||||
3. **Mixed patterns**: Some pages used `<Link>`, others used `Button as={Link}`, creating inconsistency
|
||||
4. **Wrong variants**: Some buttons still used old "solid"/"soft" names
|
||||
|
||||
## Result
|
||||
|
||||
✅ **Consistent design**: All secondary buttons now have same text-dim color across all tones
|
||||
✅ **Single pattern**: ALL buttons use `onClick` with `useNavigate()` - no Link components
|
||||
✅ **Proper semantics**: Navigation uses button elements with click handlers (matches HTML standard)
|
||||
✅ **Type safe**: Removed unnecessary props from Button component
|
||||
✅ **Maintainable**: Single source of truth for button styles in Button.tsx
|
||||
|
||||
---
|
||||
|
||||
**Date**: 2024
|
||||
**Status**: ✅ Complete - All Sites pages standardized
|
||||
@@ -1,264 +0,0 @@
|
||||
# Database Schema vs Model Field Mapping Guide
|
||||
|
||||
## Overview
|
||||
This guide documents the critical database schema mismatches in the IGNY8 project that cause 500 errors. These mismatches occur because the database uses OLD column names while Django models use NEW refactored field names.
|
||||
|
||||
---
|
||||
|
||||
## Critical Issue: Database Column Name Mismatches
|
||||
|
||||
### Problem Statement
|
||||
During the "Stage 1 Refactor", model field names were changed but database column names were NOT renamed. This causes:
|
||||
- **500 Internal Server Error** - When Django tries to query non-existent columns
|
||||
- **AttributeError** - When code references old field names on model instances
|
||||
- **FieldError** - When filtering/querying with old field names
|
||||
|
||||
### Root Cause
|
||||
The refactor changed the **Python model field names** but the **actual PostgreSQL database columns** still use the old names. Django expects the new field names but the database has the old column names.
|
||||
|
||||
---
|
||||
|
||||
## Known Database vs Model Mismatches
|
||||
|
||||
### Content Model (`igny8_content` table)
|
||||
|
||||
| Model Field Name | Database Column Name | Fix Required |
|
||||
|-----------------|---------------------|--------------|
|
||||
| `content_html` | `html_content` | Add `db_column='html_content'` |
|
||||
| `content_type` | `entity_type` | Add `db_column='entity_type'` |
|
||||
| `content_structure` | `cluster_role` | Add `db_column='cluster_role'` |
|
||||
|
||||
**Location:** `backend/igny8_core/business/content/models.py` - Content model
|
||||
|
||||
**Additional Fields to Include:**
|
||||
- `external_type` - exists in database, must be in model
|
||||
- `sync_status` - exists in database, must be in model
|
||||
|
||||
### Tasks Model (`igny8_tasks` table)
|
||||
|
||||
| Model Field Name | Database Column Name | Fix Required |
|
||||
|-----------------|---------------------|--------------|
|
||||
| `content_type` | `entity_type` | Add `db_column='entity_type'` |
|
||||
| `content_structure` | `cluster_role` | Add `db_column='cluster_role'` |
|
||||
| `taxonomy_term` | `taxonomy_id` | Add `db_column='taxonomy_id'` |
|
||||
| `idea` | `idea_id` | Add `db_column='idea_id'` |
|
||||
| `keywords` | `keywords` (text) | Change from ManyToManyField to TextField |
|
||||
|
||||
**Location:** `backend/igny8_core/business/content/models.py` - Tasks model
|
||||
|
||||
### ContentTaxonomy Relations (`igny8_content_taxonomy_relations` table)
|
||||
|
||||
| Model Expects | Database Has | Fix Required |
|
||||
|--------------|-------------|--------------|
|
||||
| `contenttaxonomy_id` | `taxonomy_id` | Create through model with `db_column` |
|
||||
| Auto M2M table | Custom table | Use `through='ContentTaxonomyRelation'` |
|
||||
|
||||
**Location:** Must create `ContentTaxonomyRelation` through model with explicit `db_column` settings
|
||||
|
||||
---
|
||||
|
||||
## Common Code Reference Errors
|
||||
|
||||
### Issue: Code Still Uses Old Field Names
|
||||
|
||||
Even after fixing the model, **view code** might still reference old field names:
|
||||
|
||||
**Wrong References to Fix:**
|
||||
- `content.entity_type` → Should be `content.content_type`
|
||||
- `content.cluster_role` → Should be `content.content_structure`
|
||||
- `content.html_content` → Should be `content.content_html`
|
||||
- `content.taxonomies` → Should be `content.taxonomy_terms`
|
||||
|
||||
**Files to Check:**
|
||||
- `backend/igny8_core/modules/writer/views.py`
|
||||
- `backend/igny8_core/modules/integration/views.py`
|
||||
- `backend/igny8_core/business/*/services/*.py`
|
||||
- `backend/igny8_core/ai/functions/*.py`
|
||||
|
||||
**Search Commands:**
|
||||
```bash
|
||||
grep -r "\.entity_type" backend/igny8_core/
|
||||
grep -r "\.cluster_role" backend/igny8_core/
|
||||
grep -r "\.html_content" backend/igny8_core/
|
||||
grep -r "\.taxonomies" backend/igny8_core/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Diagnostic Checklist for 500 Errors
|
||||
|
||||
When encountering 500 Internal Server Errors, follow this checklist:
|
||||
|
||||
### Step 1: Check Backend Logs
|
||||
```bash
|
||||
docker logs --tail=100 igny8_backend 2>&1 | grep -A 20 "Traceback"
|
||||
docker logs igny8_backend 2>&1 | grep "UndefinedColumn\|does not exist"
|
||||
```
|
||||
|
||||
### Step 2: Identify Error Type
|
||||
|
||||
**A. ProgrammingError: column does not exist**
|
||||
- Error shows: `column igny8_content.field_name does not exist`
|
||||
- **Action:** Database has different column name than model expects
|
||||
- **Fix:** Add `db_column='actual_database_column_name'` to model field
|
||||
|
||||
**B. AttributeError: object has no attribute**
|
||||
- Error shows: `'Content' object has no attribute 'entity_type'`
|
||||
- **Action:** Code references old field name
|
||||
- **Fix:** Update code to use new field name from model
|
||||
|
||||
**C. FieldError: Cannot resolve keyword**
|
||||
- Error shows: `Cannot resolve keyword 'entity_type' into field`
|
||||
- **Action:** Query/filter uses old field name
|
||||
- **Fix:** Update queryset filters to use new field names
|
||||
|
||||
### Step 3: Verify Database Schema
|
||||
```bash
|
||||
docker exec igny8_backend python manage.py shell -c "
|
||||
from django.db import connection
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT column_name FROM information_schema.columns WHERE table_name = %s ORDER BY ordinal_position', ['igny8_content'])
|
||||
print('\n'.join([row[0] for row in cursor.fetchall()]))
|
||||
"
|
||||
```
|
||||
|
||||
### Step 4: Compare Against Model
|
||||
Check if model field names match database column names. If not, add `db_column` attribute.
|
||||
|
||||
### Step 5: Search for Code References
|
||||
After fixing model, search all code for references to old field names and update them.
|
||||
|
||||
---
|
||||
|
||||
## Prevention: Pre-Refactor Checklist
|
||||
|
||||
Before doing ANY database model refactoring:
|
||||
|
||||
### 1. Document Current State
|
||||
- [ ] List all current database column names
|
||||
- [ ] List all current model field names
|
||||
- [ ] Identify any existing `db_column` mappings
|
||||
|
||||
### 2. Plan Migration Strategy
|
||||
- [ ] Decide: Rename database columns OR add `db_column` mappings?
|
||||
- [ ] If renaming: Create migration to rename columns
|
||||
- [ ] If mapping: Document which fields need `db_column`
|
||||
|
||||
### 3. Update All Code References
|
||||
- [ ] Search codebase for old field name references
|
||||
- [ ] Update view code, serializers, services
|
||||
- [ ] Update filters, querysets, aggregations
|
||||
- [ ] Update AI functions and background tasks
|
||||
|
||||
### 4. Test Before Deploy
|
||||
- [ ] Run migrations on development database
|
||||
- [ ] Test all API endpoints with authentication
|
||||
- [ ] Check logs for database errors
|
||||
- [ ] Verify no AttributeError or FieldError exceptions
|
||||
|
||||
### 5. Maintain Documentation
|
||||
- [ ] Update this guide with new mappings
|
||||
- [ ] Document why `db_column` is needed
|
||||
- [ ] Note any related field name changes
|
||||
|
||||
---
|
||||
|
||||
## Quick Fix Template
|
||||
|
||||
When you find a database mismatch:
|
||||
|
||||
```python
|
||||
# In the model file (e.g., backend/igny8_core/business/content/models.py)
|
||||
|
||||
# OLD (causes 500 error):
|
||||
content_type = models.CharField(max_length=100)
|
||||
|
||||
# FIXED (maps to actual database column):
|
||||
content_type = models.CharField(
|
||||
max_length=100,
|
||||
db_column='entity_type', # <- Maps to actual database column name
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
```
|
||||
|
||||
Then search and replace code references:
|
||||
```bash
|
||||
# Find all references to old field name
|
||||
grep -rn "\.entity_type" backend/
|
||||
|
||||
# Update to new field name
|
||||
sed -i 's/\.entity_type/.content_type/g' backend/igny8_core/modules/writer/views.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend Common Issues
|
||||
|
||||
### Issue: Undefined Variables
|
||||
**Error:** `ReferenceError: variableName is not defined`
|
||||
|
||||
**Common Causes:**
|
||||
- Variable declared but removed during refactor
|
||||
- Variable in dependency array but not defined in component
|
||||
- Import removed but variable still referenced
|
||||
|
||||
**Files to Check:**
|
||||
- `frontend/src/pages/Writer/Tasks.tsx`
|
||||
- `frontend/src/pages/Writer/Content.tsx`
|
||||
- Any component showing console loops
|
||||
|
||||
**Fix:** Remove from dependency arrays or add proper state declaration
|
||||
|
||||
---
|
||||
|
||||
## Testing Endpoints After Fixes
|
||||
|
||||
Use this command to verify all critical endpoints:
|
||||
|
||||
```bash
|
||||
TOKEN=$(curl -s -X POST "https://api.igny8.com/api/v1/auth/login/" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"dev@igny8.com","password":"dev123456"}' | \
|
||||
python3 -c "import sys, json; print(json.load(sys.stdin)['data']['access'])")
|
||||
|
||||
# Test each endpoint
|
||||
curl -s "https://api.igny8.com/api/v1/writer/content/?site_id=5&page_size=1" \
|
||||
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
|
||||
|
||||
curl -s "https://api.igny8.com/api/v1/planner/clusters/?site_id=5&page_size=1" \
|
||||
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
|
||||
|
||||
curl -s "https://api.igny8.com/api/v1/writer/tasks/?site_id=5&page_size=1" \
|
||||
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
|
||||
```
|
||||
|
||||
**Success Indicator:** Response should have `"success": true`, not `"success": false` with error.
|
||||
|
||||
---
|
||||
|
||||
## Status Codes Quick Reference
|
||||
|
||||
| Code | Meaning | Common Cause | Fix |
|
||||
|------|---------|--------------|-----|
|
||||
| 500 | Internal Server Error | Database column mismatch, code error | Check logs, fix model or code |
|
||||
| 403 | Forbidden / Auth Required | Normal - means endpoint works but needs auth | Login first, use Bearer token |
|
||||
| 404 | Not Found | URL wrong or object doesn't exist | Check URL, verify ID exists |
|
||||
| 400 | Bad Request | Invalid data sent | Check request payload |
|
||||
|
||||
**Important:** If you see 403 instead of 500 after a fix, that's SUCCESS - it means the endpoint works and just needs authentication.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Key Principle:** The database schema is the source of truth. Models must map to actual database column names, not the other way around (unless you run migrations to rename columns).
|
||||
|
||||
**Golden Rule:** After ANY refactor that changes field names:
|
||||
1. Check if database columns were actually renamed
|
||||
2. If not, add `db_column` mappings in models
|
||||
3. Update all code references to use NEW field names
|
||||
4. Test all endpoints with authentication
|
||||
5. Check logs for database errors
|
||||
|
||||
**Remember:** A 500 error from a database column mismatch will cascade - fixing the model isn't enough, you must also update all code that references the old field names.
|
||||
@@ -1,178 +0,0 @@
|
||||
# Documentation vs Codebase Discrepancies Report
|
||||
|
||||
**Date:** 2025-01-XX
|
||||
**Purpose:** Identify mismatches between master documentation (01-05) and actual codebase
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The master documentation files (01-05) are **mostly accurate** but have some **missing modules** and minor version discrepancies.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Accurate Sections
|
||||
|
||||
### 1. Technology Stack (01-TECH-STACK-AND-INFRASTRUCTURE.md)
|
||||
- ✅ Django 5.2.7+ - **MATCHES** (requirements.txt: `Django>=5.2.7`)
|
||||
- ✅ React 19.0.0 - **MATCHES** (package.json: `"react": "^19.0.0"`)
|
||||
- ✅ TypeScript 5.7.2 - **MATCHES** (package.json: `"typescript": "~5.7.2"`)
|
||||
- ✅ Vite 6.1.0 - **MATCHES** (package.json: `"vite": "^6.1.0"`)
|
||||
- ✅ Tailwind CSS 4.0.8 - **MATCHES** (package.json: `"tailwindcss": "^4.0.8"`)
|
||||
- ✅ Zustand 5.0.8 - **MATCHES** (package.json: `"zustand": "^5.0.8"`)
|
||||
- ✅ All UI libraries versions - **MATCHES**
|
||||
|
||||
### 2. Frontend Architecture (03-FRONTEND-ARCHITECTURE.md)
|
||||
- ✅ Project structure - **MATCHES**
|
||||
- ✅ Component architecture - **MATCHES**
|
||||
- ✅ State management (Zustand stores) - **MATCHES**
|
||||
- ✅ Routing structure - **MATCHES**
|
||||
|
||||
### 3. AI Framework (05-AI-FRAMEWORK-IMPLEMENTATION.md)
|
||||
- ✅ AI framework structure - **MATCHES**
|
||||
- ✅ Base classes and engine - **MATCHES**
|
||||
- ✅ Function registry - **MATCHES**
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Discrepancies Found
|
||||
|
||||
### 1. Missing Modules in Documentation
|
||||
|
||||
**Issue:** Backend documentation (04-BACKEND-IMPLEMENTATION.md) only lists 4 modules, but codebase has **10 modules**.
|
||||
|
||||
**Documented Modules:**
|
||||
- ✅ planner
|
||||
- ✅ writer
|
||||
- ✅ system
|
||||
- ✅ billing
|
||||
|
||||
**Missing Modules (in codebase but not documented):**
|
||||
- ❌ **automation** - Not documented
|
||||
- ❌ **integration** - Not documented
|
||||
- ❌ **linker** - Not documented
|
||||
- ❌ **optimizer** - Not documented
|
||||
- ❌ **publisher** - Not documented
|
||||
- ❌ **site_builder** - Not documented
|
||||
|
||||
**Location:** `backend/igny8_core/modules/`
|
||||
|
||||
**Impact:** Medium - These modules exist and are functional but not documented.
|
||||
|
||||
---
|
||||
|
||||
### 2. React Router Version Discrepancy
|
||||
|
||||
**Issue:** Minor version difference in documentation.
|
||||
|
||||
**Documentation says:**
|
||||
- React Router: v7.9.5
|
||||
|
||||
**Actual codebase:**
|
||||
- `react-router`: ^7.1.5
|
||||
- `react-router-dom`: ^7.9.5
|
||||
|
||||
**Impact:** Low - Both are v7, minor version difference. Documentation should note both packages.
|
||||
|
||||
---
|
||||
|
||||
### 3. Module Organization Documentation
|
||||
|
||||
**Issue:** Application Architecture (02-APPLICATION-ARCHITECTURE.md) only mentions 5 core modules, but there are more.
|
||||
|
||||
**Documented:**
|
||||
- Planner
|
||||
- Writer
|
||||
- Thinker (mentioned but may not exist)
|
||||
- System
|
||||
- Billing
|
||||
|
||||
**Actual modules in codebase:**
|
||||
- planner ✅
|
||||
- writer ✅
|
||||
- system ✅
|
||||
- billing ✅
|
||||
- automation ❌ (not documented)
|
||||
- integration ❌ (not documented)
|
||||
- linker ❌ (not documented)
|
||||
- optimizer ❌ (not documented)
|
||||
- publisher ❌ (not documented)
|
||||
- site_builder ❌ (not documented)
|
||||
|
||||
**Impact:** Medium - Complete module list is missing.
|
||||
|
||||
---
|
||||
|
||||
### 4. Site Builder Module Status
|
||||
|
||||
**Issue:** Site Builder module exists but documentation may not reflect current state after wizard removal.
|
||||
|
||||
**Current State:**
|
||||
- ✅ `backend/igny8_core/modules/site_builder/` exists
|
||||
- ✅ Site Builder APIs are active
|
||||
- ✅ Models are active (SiteBlueprint, PageBlueprint, etc.)
|
||||
- ❌ Wizard UI removed (correctly documented in 06-FUNCTIONAL-BUSINESS-LOGIC.md)
|
||||
|
||||
**Impact:** Low - Status is correctly documented in workflow docs, but module structure may need updating in 04-BACKEND-IMPLEMENTATION.md.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Recommended Updates
|
||||
|
||||
### Priority 1: Update Module Documentation
|
||||
|
||||
**File:** `master-docs/04-BACKEND-IMPLEMENTATION.md`
|
||||
|
||||
**Action:** Add missing modules to Project Structure section:
|
||||
|
||||
```markdown
|
||||
├── modules/ # Feature modules
|
||||
│ ├── planner/ # Keywords, Clusters, Ideas
|
||||
│ ├── writer/ # Tasks, Content, Images
|
||||
│ ├── system/ # Settings, Prompts, Integration
|
||||
│ ├── billing/ # Credits, Transactions, Usage
|
||||
│ ├── automation/ # Automation workflows
|
||||
│ ├── integration/ # External integrations
|
||||
│ ├── linker/ # Internal linking
|
||||
│ ├── optimizer/ # Content optimization
|
||||
│ ├── publisher/ # Publishing workflows
|
||||
│ └── site_builder/ # Site blueprint management
|
||||
```
|
||||
|
||||
### Priority 2: Update Application Architecture
|
||||
|
||||
**File:** `master-docs/02-APPLICATION-ARCHITECTURE.md`
|
||||
|
||||
**Action:** Add complete module list with descriptions for all 10 modules.
|
||||
|
||||
### Priority 3: Minor Version Updates
|
||||
|
||||
**File:** `master-docs/01-TECH-STACK-AND-INFRASTRUCTURE.md`
|
||||
|
||||
**Action:** Update React Router to show both packages:
|
||||
- `react-router`: ^7.1.5
|
||||
- `react-router-dom`: ^7.9.5
|
||||
|
||||
---
|
||||
|
||||
## ✅ Overall Assessment
|
||||
|
||||
**Accuracy Level:** ~85%
|
||||
|
||||
**Strengths:**
|
||||
- Technology stack versions are accurate
|
||||
- Core architecture is well documented
|
||||
- Frontend structure matches
|
||||
- AI framework documentation is complete
|
||||
|
||||
**Weaknesses:**
|
||||
- Missing 6 backend modules in documentation
|
||||
- Module organization incomplete
|
||||
- Minor version discrepancies
|
||||
|
||||
**Recommendation:** Update module documentation to include all 10 modules for complete accuracy.
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-01-XX
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
# Field Rename Implementation Complete
|
||||
|
||||
## Summary
|
||||
Complete removal of old field names (`site_entity_type`, `cluster_role`, `entity_type`, `html_content`) with NO backward compatibility. All references updated to use new field names (`content_type`, `content_structure`, `content_html`) across entire codebase.
|
||||
|
||||
## Changes Completed
|
||||
|
||||
### Backend Models (3 files)
|
||||
✅ `/backend/igny8_core/business/planning/models.py` - ContentIdeas
|
||||
- Renamed: `site_entity_type` → `content_type`
|
||||
- Renamed: `cluster_role` → `content_structure`
|
||||
- Removed: ALL `db_column` mappings
|
||||
- Added: Comprehensive CHOICES (4 types, 14 structures)
|
||||
- Updated: Defaults to `post` / `article`
|
||||
|
||||
✅ `/backend/igny8_core/business/content/models.py` - Tasks & Content
|
||||
- Tasks: Removed `db_column` mappings for `content_type` and `content_structure`
|
||||
- Content: Removed `db_column` mapping for `content_html`
|
||||
- Added: Comprehensive CHOICES to both models
|
||||
- Updated: All help text
|
||||
|
||||
### Backend Views & Services (5 files)
|
||||
✅ `/backend/igny8_core/modules/planner/views.py`
|
||||
- Removed: `role_to_structure` mapping dict
|
||||
- Direct field copy now: `idea.content_type` → `task.content_type`
|
||||
|
||||
✅ `/backend/igny8_core/modules/planner/serializers.py`
|
||||
- Already using correct field names
|
||||
|
||||
✅ `/backend/igny8_core/ai/functions/generate_ideas.py`
|
||||
- Removed: `structure_to_role` mapping
|
||||
- Direct assignment of `content_type`/`content_structure`
|
||||
|
||||
✅ `/backend/igny8_core/business/publishing/services/adapters/sites_renderer_adapter.py`
|
||||
- Updated: All `entity_type` → `content_type`
|
||||
- Updated: All `cluster_role` → `content_structure`
|
||||
- Removed: All backward compatibility logic
|
||||
|
||||
✅ `/backend/igny8_core/business/site_building/services/page_generation_service.py`
|
||||
- Updated: `entity_type` → `content_type`
|
||||
- Updated: `cluster_role` → `content_structure`
|
||||
- Removed: Old field references
|
||||
|
||||
### Backend AI & Prompts (2 files)
|
||||
✅ `/backend/igny8_core/ai/prompts.py`
|
||||
- Updated: Documentation for new values
|
||||
- Updated: Prompt templates to reference `content_structure`
|
||||
- Removed: Old hub/supporting/attribute references
|
||||
|
||||
### Frontend TypeScript Interfaces (1 file)
|
||||
✅ `/frontend/src/services/api.ts`
|
||||
- Updated: `ContentIdea`, `ContentIdeaCreateData`, `ContentIdeasFilters`
|
||||
- All interfaces now use `content_type` / `content_structure`
|
||||
|
||||
### Frontend Pages (3 files)
|
||||
✅ `/frontend/src/pages/Planner/Ideas.tsx`
|
||||
- Updated: All 6 field references
|
||||
- Updated: Form data, filters, handlers
|
||||
|
||||
✅ `/frontend/src/pages/Sites/PostEditor.tsx`
|
||||
- Updated: `content.cluster_role` → `content.content_structure`
|
||||
|
||||
✅ `/frontend/src/pages/Settings/DebugStatus.tsx`
|
||||
- Updated: Help text to reference new field names
|
||||
- Noted old names as removed
|
||||
|
||||
### Frontend Config Files (4 files)
|
||||
✅ `/frontend/src/config/structureMapping.ts` - NEW
|
||||
- Created: Shared constants for all structure mappings
|
||||
- Exports: `CONTENT_TYPE_OPTIONS` (4 types)
|
||||
- Exports: `CONTENT_STRUCTURE_BY_TYPE` (14 structures)
|
||||
- Exports: `STRUCTURE_LABELS`, `TYPE_LABELS`, `getStructureOptions()`
|
||||
|
||||
✅ `/frontend/src/config/pages/ideas.config.tsx`
|
||||
- Updated: Interface, columns, filters, form fields
|
||||
- Comprehensive 14-structure options in filters and forms
|
||||
|
||||
✅ `/frontend/src/config/pages/tasks.config.tsx`
|
||||
- Updated: All references to use new field names
|
||||
- Comprehensive 14-structure options in filters and forms
|
||||
- Uses structureMapping constants
|
||||
|
||||
✅ `/frontend/src/config/pages/content.config.tsx`
|
||||
- Updated: All references to use new field names
|
||||
- Comprehensive 14-structure options in filters
|
||||
- Uses structureMapping constants
|
||||
|
||||
## New Value Schema
|
||||
|
||||
### Content Types (4)
|
||||
- `post` - Blog posts, articles
|
||||
- `page` - Static pages
|
||||
- `product` - Product pages
|
||||
- `taxonomy` - Category/tag/attribute archives
|
||||
|
||||
### Content Structures (14)
|
||||
|
||||
**Post Structures (5):**
|
||||
- `article` - Standard blog post
|
||||
- `guide` - How-to guides
|
||||
- `comparison` - Comparison posts
|
||||
- `review` - Review posts
|
||||
- `listicle` - List-based articles
|
||||
|
||||
**Page Structures (5):**
|
||||
- `landing_page` - Marketing landing pages
|
||||
- `business_page` - Business info pages
|
||||
- `service_page` - Service description pages
|
||||
- `general` - General static pages
|
||||
- `cluster_hub` - Topic cluster hub pages
|
||||
|
||||
**Product Structures (1):**
|
||||
- `product_page` - Product detail pages
|
||||
|
||||
**Taxonomy Structures (3):**
|
||||
- `category_archive` - Category listing pages
|
||||
- `tag_archive` - Tag listing pages
|
||||
- `attribute_archive` - Attribute filter pages
|
||||
|
||||
## Database Migration
|
||||
|
||||
### SQL Migration File
|
||||
✅ `/backend/rename_fields_migration.sql`
|
||||
- Renames 5 tables' columns
|
||||
- Conditional checks (only rename if old column exists)
|
||||
- Index renames
|
||||
- Tables affected:
|
||||
1. `igny8_content_ideas` (2 columns)
|
||||
2. `igny8_tasks` (2 columns)
|
||||
3. `igny8_content` (3 columns)
|
||||
4. `igny8_content_taxonomy_map` (1 column)
|
||||
5. `igny8_taxonomy_terms` (1 column)
|
||||
|
||||
### How to Execute
|
||||
```bash
|
||||
# Option 1: Via psql (if available)
|
||||
psql -U username -d database_name -f /data/app/igny8/backend/rename_fields_migration.sql
|
||||
|
||||
# Option 2: Via Django shell
|
||||
cd /data/app/igny8/backend
|
||||
source .venv/bin/activate
|
||||
python manage.py dbshell < rename_fields_migration.sql
|
||||
|
||||
# Option 3: Via Django migration (recommended)
|
||||
python manage.py makemigrations
|
||||
python manage.py migrate
|
||||
```
|
||||
|
||||
## Testing & Validation
|
||||
|
||||
### Backend Testing Script
|
||||
✅ `/backend/test_field_rename.py`
|
||||
- Tests model field access
|
||||
- Verifies database column names
|
||||
- Validates CHOICES definitions
|
||||
- Checks for old column names
|
||||
|
||||
### Run Test
|
||||
```bash
|
||||
cd /data/app/igny8/backend
|
||||
source .venv/bin/activate
|
||||
python manage.py shell < test_field_rename.py
|
||||
```
|
||||
|
||||
### Manual API Testing
|
||||
Test these endpoints after migration:
|
||||
|
||||
1. **Ideas API:**
|
||||
- GET `/api/planner/ideas/` - List ideas
|
||||
- POST `/api/planner/ideas/` - Create idea with new fields
|
||||
- POST `/api/planner/ideas/{id}/bulk_queue_to_writer/` - Queue to writer
|
||||
|
||||
2. **Tasks API:**
|
||||
- GET `/api/writer/tasks/` - List tasks
|
||||
- GET `/api/writer/tasks/?content_type=post` - Filter by type
|
||||
- GET `/api/writer/tasks/?content_structure=article` - Filter by structure
|
||||
|
||||
3. **Content API:**
|
||||
- GET `/api/writer/content/` - List content
|
||||
- GET `/api/writer/content/?content_type=page` - Filter by type
|
||||
|
||||
### Frontend Testing
|
||||
1. Navigate to Ideas page - verify:
|
||||
- Filters show all 14 structures
|
||||
- Create form has all types/structures
|
||||
- Table displays correctly
|
||||
- Bulk queue works
|
||||
|
||||
2. Navigate to Tasks page - verify:
|
||||
- Filters work with new values
|
||||
- Table columns show type/structure badges
|
||||
- No console errors
|
||||
|
||||
3. Navigate to Content page - verify:
|
||||
- Filters work
|
||||
- Table displays correctly
|
||||
- PostEditor shows content_structure
|
||||
|
||||
## Breaking Changes
|
||||
⚠️ **NO BACKWARD COMPATIBILITY** - This is a breaking change:
|
||||
- Old API field names (`site_entity_type`, `cluster_role`) no longer work
|
||||
- Old database columns will be renamed (data preserved)
|
||||
- Any external integrations must update to new field names
|
||||
|
||||
## Rollback Plan
|
||||
If issues occur:
|
||||
1. Reverse SQL migration (see `rename_fields_migration.sql` comments)
|
||||
2. Revert all code changes via git
|
||||
3. Original columns: `site_entity_type`, `cluster_role`, `entity_type`, `html_content`
|
||||
|
||||
## Files Modified
|
||||
**Backend (7 files):**
|
||||
- Models: 2 files
|
||||
- Views: 1 file
|
||||
- Serializers: 1 file (already correct)
|
||||
- Services: 2 files
|
||||
- AI: 2 files
|
||||
|
||||
**Frontend (8 files):**
|
||||
- Interfaces: 1 file
|
||||
- Pages: 3 files
|
||||
- Configs: 4 files (1 new)
|
||||
|
||||
**Database:**
|
||||
- SQL migration: 1 file (not yet executed)
|
||||
- Test script: 1 file
|
||||
|
||||
**Total: 17 files modified/created**
|
||||
|
||||
## Next Steps
|
||||
1. ✅ All code changes complete
|
||||
2. ⏳ Execute SQL migration
|
||||
3. ⏳ Run backend test script
|
||||
4. ⏳ Test APIs manually
|
||||
5. ⏳ Test frontend pages
|
||||
6. ⏳ Verify no 500/403 errors
|
||||
7. ⏳ Update any external documentation
|
||||
8. ⏳ Deploy to production
|
||||
|
||||
## Verification Checklist
|
||||
- [ ] SQL migration executed successfully
|
||||
- [ ] Backend test script passes
|
||||
- [ ] Ideas API works (list, create, bulk queue)
|
||||
- [ ] Tasks API works (list, filter by type/structure)
|
||||
- [ ] Content API works (list, filter)
|
||||
- [ ] Ideas page loads without errors
|
||||
- [ ] Tasks page loads without errors
|
||||
- [ ] Content page loads without errors
|
||||
- [ ] PostEditor displays content_structure
|
||||
- [ ] All filters show 14 structure options
|
||||
- [ ] No 500 errors in backend logs
|
||||
- [ ] No console errors in frontend
|
||||
- [ ] WordPress sync still works (if applicable)
|
||||
|
||||
---
|
||||
**Implementation Date:** 2024
|
||||
**Status:** CODE COMPLETE - AWAITING MIGRATION EXECUTION & TESTING
|
||||
@@ -1,515 +0,0 @@
|
||||
# IGNY8 Design Standard Reference
|
||||
**Standardized UI patterns used across Planner, Writer, and Dashboard modules**
|
||||
|
||||
This document defines the locked design patterns and component usage standards for the IGNY8 application. All modules (including Sites) should follow these patterns to maintain visual consistency.
|
||||
|
||||
---
|
||||
|
||||
## Core Component Library
|
||||
|
||||
### 1. Button Component
|
||||
**Location:** `frontend/src/components/ui/button/Button.tsx`
|
||||
**Status:** 🔒 STYLE LOCKED - See `DESIGN_SYSTEM.md`
|
||||
|
||||
#### Variants (5 total)
|
||||
- `solid` - Filled background (primary action)
|
||||
- `soft` - Light background (secondary action)
|
||||
- `outline` - Border only (tertiary action)
|
||||
- `ghost` - No border or background (minimal action)
|
||||
- `gradient` - Gradient background with shadow (premium/highlight action)
|
||||
|
||||
#### Sizes (4 total)
|
||||
- `xs` - Extra small
|
||||
- `sm` - Small
|
||||
- `md` - Medium (default)
|
||||
- `lg` - Large
|
||||
|
||||
#### Tones (5 total)
|
||||
- `brand` - Primary brand color (blue)
|
||||
- `success` - Green
|
||||
- `warning` - Orange
|
||||
- `danger` - Red/Error
|
||||
- `neutral` - Gray
|
||||
|
||||
#### Usage Example
|
||||
```tsx
|
||||
import Button from '../../components/ui/button/Button';
|
||||
|
||||
<Button variant="solid" tone="brand" size="md" startIcon={<Icon />}>
|
||||
Click Me
|
||||
</Button>
|
||||
```
|
||||
|
||||
#### ⚠️ Anti-Pattern
|
||||
```tsx
|
||||
// ❌ DON'T: Raw HTML buttons with inline Tailwind
|
||||
<button className="px-4 py-2 bg-blue-500 text-white rounded-lg">
|
||||
Click Me
|
||||
</button>
|
||||
|
||||
// ✅ DO: Use Button component
|
||||
<Button variant="solid" tone="brand">
|
||||
Click Me
|
||||
</Button>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. ComponentCard
|
||||
**Location:** `frontend/src/components/common/ComponentCard.tsx`
|
||||
**Purpose:** Standard card wrapper for sections with title and description
|
||||
|
||||
#### Props
|
||||
- `title` (required) - Section title (string or ReactNode)
|
||||
- `desc` (optional) - Description text below title
|
||||
- `children` (required) - Card content
|
||||
- `className` (optional) - Additional styling
|
||||
|
||||
#### Usage Example
|
||||
```tsx
|
||||
import ComponentCard from '../../components/common/ComponentCard';
|
||||
|
||||
<ComponentCard title="Quick Actions" desc="Common planning tasks and shortcuts">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{/* Content */}
|
||||
</div>
|
||||
</ComponentCard>
|
||||
```
|
||||
|
||||
#### Structure
|
||||
- **Header:** Title + optional description (gray text)
|
||||
- **Body:** Border-top separated content area with padding
|
||||
- **Dark mode:** Automatic theme support
|
||||
|
||||
#### ⚠️ Anti-Pattern
|
||||
```tsx
|
||||
// ❌ DON'T: Raw Card component with manual header
|
||||
<Card>
|
||||
<div className="px-6 py-5">
|
||||
<h3 className="text-base font-medium">Quick Actions</h3>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
{/* Content */}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
// ✅ DO: Use ComponentCard
|
||||
<ComponentCard title="Quick Actions">
|
||||
{/* Content */}
|
||||
</ComponentCard>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. EnhancedMetricCard
|
||||
**Location:** `frontend/src/components/dashboard/EnhancedMetricCard.tsx`
|
||||
**Purpose:** Display metrics with optional trends, tooltips, and navigation
|
||||
|
||||
#### Key Props
|
||||
- `title` (required) - Metric label
|
||||
- `value` (required) - Main metric value (string | number)
|
||||
- `subtitle` (optional) - Additional context below value
|
||||
- `icon` (optional) - Icon component
|
||||
- `accentColor` (required) - Border accent color
|
||||
- `trend` (optional) - { direction: 'up' | 'down', value: string }
|
||||
- `href` (optional) - React Router navigation path
|
||||
- `onClick` (optional) - Click handler (alternative to href)
|
||||
- `tooltip` (optional) - Tooltip text on hover
|
||||
- `details` (optional) - Array of tooltip detail breakdowns
|
||||
|
||||
#### Accent Colors (6 total)
|
||||
- `blue` - Primary/default metrics
|
||||
- `green` - Success/positive metrics
|
||||
- `orange` - Warning/attention metrics
|
||||
- `purple` - Special/premium metrics
|
||||
- `red` - Error/critical metrics
|
||||
- `success` - Alternative green (var(--color-success))
|
||||
|
||||
#### Usage Example
|
||||
```tsx
|
||||
import EnhancedMetricCard from '../../components/dashboard/EnhancedMetricCard';
|
||||
|
||||
<EnhancedMetricCard
|
||||
title="Active Keywords"
|
||||
value={1234}
|
||||
subtitle="Across all clusters"
|
||||
icon={<ListIcon className="h-5 w-5" />}
|
||||
accentColor="blue"
|
||||
trend={{ direction: 'up', value: '+12%' }}
|
||||
href="/planner/keywords"
|
||||
tooltip="View all active keywords"
|
||||
details={[
|
||||
{ label: 'Tracked', value: '800' },
|
||||
{ label: 'Untracked', value: '434' },
|
||||
]}
|
||||
/>
|
||||
```
|
||||
|
||||
#### Features
|
||||
- Automatic Link wrapping when `href` provided
|
||||
- Hover effects and transitions
|
||||
- Dark mode support
|
||||
- Tooltip with optional details breakdown
|
||||
- Trend indicators with arrows
|
||||
|
||||
#### ⚠️ Anti-Pattern
|
||||
```tsx
|
||||
// ❌ DON'T: Custom metric cards with inline styles
|
||||
<div className="bg-white rounded-xl border-2 border-slate-200 p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">Active Keywords</p>
|
||||
<p className="text-3xl font-bold">1,234</p>
|
||||
</div>
|
||||
<ListIcon className="h-8 w-8 text-blue-500" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
// ✅ DO: Use EnhancedMetricCard
|
||||
<EnhancedMetricCard
|
||||
title="Active Keywords"
|
||||
value={1234}
|
||||
icon={<ListIcon className="h-5 w-5" />}
|
||||
accentColor="blue"
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. PageHeader
|
||||
**Location:** `frontend/src/components/common/PageHeader.tsx`
|
||||
**Purpose:** Standardized page header with title, site/sector info, and selectors
|
||||
|
||||
#### Key Props
|
||||
- `title` (required) - Page title
|
||||
- `lastUpdated` (optional) - Last refresh timestamp
|
||||
- `badge` (optional) - { icon: ReactNode, color: 'blue' | 'green' | ... }
|
||||
- `showRefresh` (optional) - Show refresh button
|
||||
- `onRefresh` (optional) - Refresh button handler
|
||||
- `hideSiteSector` (optional) - Hide site/sector info for global pages
|
||||
- `className` (optional) - Additional styling
|
||||
|
||||
#### Badge Colors (6 total)
|
||||
Same as Button/Metric: `blue`, `green`, `purple`, `orange`, `red`, `indigo`
|
||||
|
||||
#### Usage Example
|
||||
```tsx
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import { ListIcon } from '../../icons';
|
||||
|
||||
<PageHeader
|
||||
title="Keyword Dashboard"
|
||||
lastUpdated={new Date()}
|
||||
badge={{ icon: <ListIcon className="h-5 w-5" />, color: 'blue' }}
|
||||
showRefresh={true}
|
||||
onRefresh={handleRefresh}
|
||||
/>
|
||||
```
|
||||
|
||||
#### Features
|
||||
- Automatic site/sector display from stores
|
||||
- SiteAndSectorSelector integration
|
||||
- Responsive layout (stack on mobile)
|
||||
- Badge with icon support
|
||||
- Last updated timestamp
|
||||
|
||||
#### ⚠️ Anti-Pattern
|
||||
```tsx
|
||||
// ❌ DON'T: Custom page headers with manual layout
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold">Keyword Dashboard</h2>
|
||||
<p className="text-sm text-gray-500">
|
||||
Site: {site.name} • Sector: {sector.name}
|
||||
</p>
|
||||
</div>
|
||||
<button onClick={refresh}>Refresh</button>
|
||||
</div>
|
||||
|
||||
// ✅ DO: Use PageHeader
|
||||
<PageHeader
|
||||
title="Keyword Dashboard"
|
||||
showRefresh={true}
|
||||
onRefresh={refresh}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. Link Component (React Router)
|
||||
**Location:** `react-router-dom`
|
||||
**Purpose:** Standard navigation with automatic prefetching and accessibility
|
||||
|
||||
#### Usage Example
|
||||
```tsx
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
<Link
|
||||
to="/planner/keywords"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div>Navigate to Keywords</div>
|
||||
</Link>
|
||||
```
|
||||
|
||||
#### Benefits Over Button + Navigate
|
||||
- ✅ Proper semantic HTML (`<a>` tag)
|
||||
- ✅ Keyboard navigation (Tab + Enter)
|
||||
- ✅ Right-click "Open in new tab" support
|
||||
- ✅ Screen reader accessibility
|
||||
- ✅ Browser history support
|
||||
- ✅ Automatic prefetching
|
||||
|
||||
#### ⚠️ Anti-Pattern
|
||||
```tsx
|
||||
// ❌ DON'T: Button with onClick navigate
|
||||
<button onClick={() => navigate('/planner/keywords')}>
|
||||
Go to Keywords
|
||||
</button>
|
||||
|
||||
// ✅ DO: Use Link component
|
||||
<Link to="/planner/keywords">
|
||||
Go to Keywords
|
||||
</Link>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Design Patterns
|
||||
|
||||
### Quick Actions Grid
|
||||
**Standard pattern used in:** Planner Dashboard, Writer Dashboard
|
||||
|
||||
#### Structure
|
||||
```tsx
|
||||
<ComponentCard title="Quick Actions" desc="Common planning tasks and shortcuts">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<Link
|
||||
to="/path"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
{/* Gradient Icon */}
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<Icon className="h-6 w-6" />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Action Title</h4>
|
||||
<p className="text-sm text-slate-600">Action description</p>
|
||||
</div>
|
||||
|
||||
{/* Arrow Icon */}
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-primary)] transition" />
|
||||
</Link>
|
||||
</div>
|
||||
</ComponentCard>
|
||||
```
|
||||
|
||||
#### Key Elements
|
||||
1. **ComponentCard wrapper** - Title + description
|
||||
2. **Responsive grid** - 1 col mobile, 2 col tablet, 4 col desktop
|
||||
3. **Link component** - Not button
|
||||
4. **Gradient icon box** - 48px (size-12), gradient from primary to primary-dark
|
||||
5. **Content area** - Title (font-semibold) + description (text-sm)
|
||||
6. **Arrow icon** - Right-pointing, changes color on hover
|
||||
7. **Hover effects** - Border color + shadow on hover
|
||||
|
||||
#### Gradient Color Variants
|
||||
```tsx
|
||||
// Primary (Blue)
|
||||
from-[var(--color-primary)] to-[var(--color-primary-dark)]
|
||||
|
||||
// Success (Green)
|
||||
from-[var(--color-success)] to-[var(--color-success-dark)]
|
||||
|
||||
// Warning (Orange)
|
||||
from-[var(--color-warning)] to-[var(--color-warning-dark)]
|
||||
|
||||
// Purple
|
||||
from-[var(--color-purple)] to-[var(--color-purple-dark)]
|
||||
```
|
||||
|
||||
#### ⚠️ Anti-Pattern - Sites Dashboard Current Implementation
|
||||
```tsx
|
||||
// ❌ DON'T: Button with navigate + manual styling
|
||||
<button
|
||||
onClick={() => navigate(`/sites/${siteId}/pages`)}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
{/* ... */}
|
||||
</button>
|
||||
|
||||
// ✅ DO: Link component
|
||||
<Link
|
||||
to={`/sites/${siteId}/pages`}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
{/* ... */}
|
||||
</Link>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Metrics Dashboard Grid
|
||||
**Standard pattern used in:** Planner Dashboard, Writer Dashboard
|
||||
|
||||
#### Structure
|
||||
```tsx
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<EnhancedMetricCard
|
||||
title="Metric Name"
|
||||
value={1234}
|
||||
icon={<Icon className="h-5 w-5" />}
|
||||
accentColor="blue"
|
||||
href="/path"
|
||||
/>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Grid Breakpoints
|
||||
- **Mobile (< 768px):** 1 column
|
||||
- **Tablet (768px - 1024px):** 2 columns
|
||||
- **Desktop (> 1024px):** 3 columns
|
||||
|
||||
#### Best Practices
|
||||
- Use `href` prop for navigation (not `onClick`)
|
||||
- Consistent icon sizing: `h-5 w-5`
|
||||
- Map accent colors to metric meaning (blue = neutral, green = success, orange = warning, red = error)
|
||||
- Include tooltips for complex metrics
|
||||
- Add trend indicators when comparing periods
|
||||
|
||||
---
|
||||
|
||||
## Color System
|
||||
|
||||
### CSS Variables
|
||||
```css
|
||||
--color-primary: #0693e3; /* Brand Blue */
|
||||
--color-primary-dark: #0570b8;
|
||||
--color-success: #0bbf87; /* Green */
|
||||
--color-success-dark: #089968;
|
||||
--color-warning: #ff7a00; /* Orange */
|
||||
--color-warning-dark: #cc6200;
|
||||
--color-purple: #5d4ae3;
|
||||
--color-purple-dark: #4a3bb5;
|
||||
--color-error: #f44336; /* Red */
|
||||
```
|
||||
|
||||
### Tailwind Color Classes
|
||||
- `brand-*` - Primary blue (50-900)
|
||||
- `success-*` - Green (50-900)
|
||||
- `warning-*` - Orange (50-900)
|
||||
- `error-*` - Red (50-900)
|
||||
- `purple-*` - Purple (50-900)
|
||||
- `gray-*` - Neutral (50-900)
|
||||
|
||||
---
|
||||
|
||||
## Sites Module Refactor Checklist
|
||||
|
||||
### Current Inconsistencies (Sites Dashboard Example)
|
||||
- ❌ Uses `<button onClick={() => navigate(...)}` instead of `<Link to={...}>`
|
||||
- ❌ Missing ComponentCard wrapper for Quick Actions section
|
||||
- ❌ Manual heading instead of ComponentCard title prop
|
||||
- ⚠️ Uses Button component correctly (partial compliance)
|
||||
- ✅ Uses EnhancedMetricCard correctly
|
||||
|
||||
### Required Changes
|
||||
1. **Replace all `<button onClick={() => navigate(...)}` with `<Link to={...}>`**
|
||||
- Better accessibility
|
||||
- Standard keyboard navigation
|
||||
- Consistent with Planner/Writer modules
|
||||
|
||||
2. **Wrap Quick Actions in ComponentCard**
|
||||
- Current: Manual `<h2>` heading
|
||||
- Target: `<ComponentCard title="Quick Actions" desc="...">`
|
||||
|
||||
3. **Extract ActionCard component (if repeated)**
|
||||
- DRY principle for Quick Action cards
|
||||
- Reusable across Sites module
|
||||
|
||||
4. **Standardize Button usage**
|
||||
- Verify all buttons use `variant` prop (not custom classes)
|
||||
- Ensure consistent tone/size across module
|
||||
|
||||
5. **Add missing EnhancedMetricCard features**
|
||||
- Tooltips for complex metrics
|
||||
- Trend indicators where applicable
|
||||
|
||||
---
|
||||
|
||||
## Implementation Priority
|
||||
|
||||
### Phase 1: Navigation (High Impact)
|
||||
1. Replace `button + navigate` with `Link` components
|
||||
2. Update click handlers to href props
|
||||
3. Test keyboard navigation and accessibility
|
||||
|
||||
### Phase 2: Component Wrapping (Medium Impact)
|
||||
1. Wrap sections in ComponentCard
|
||||
2. Replace manual headings with ComponentCard title prop
|
||||
3. Verify consistent spacing and styling
|
||||
|
||||
### Phase 3: Component Extraction (Low Impact)
|
||||
1. Create reusable ActionCard component
|
||||
2. Create SiteMetricCard if Sites-specific logic needed
|
||||
3. Update DESIGN_SYSTEM.md with new components
|
||||
|
||||
### Phase 4: Polish (Continuous)
|
||||
1. Add missing tooltips
|
||||
2. Add trend indicators
|
||||
3. Verify dark mode consistency
|
||||
4. Test responsive layouts
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Visual Consistency
|
||||
- [ ] Quick Actions match Planner/Writer pattern
|
||||
- [ ] Metrics grid matches dashboard standards
|
||||
- [ ] Button variants consistent across pages
|
||||
- [ ] Color usage matches design system
|
||||
|
||||
### Accessibility
|
||||
- [ ] All navigation uses Link (not button)
|
||||
- [ ] Keyboard navigation works (Tab, Enter)
|
||||
- [ ] Screen reader labels present
|
||||
- [ ] Focus indicators visible
|
||||
|
||||
### Functionality
|
||||
- [ ] All routes navigate correctly
|
||||
- [ ] Hover states work consistently
|
||||
- [ ] Dark mode renders properly
|
||||
- [ ] Responsive breakpoints work
|
||||
|
||||
### Code Quality
|
||||
- [ ] No raw `<button>` for navigation
|
||||
- [ ] No inline Tailwind for common patterns
|
||||
- [ ] TypeScript errors resolved
|
||||
- [ ] Component props properly typed
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
### Key Files
|
||||
- `frontend/DESIGN_SYSTEM.md` - Locked component variants
|
||||
- `frontend/src/components/ui/button/Button.tsx` - Button component
|
||||
- `frontend/src/components/common/ComponentCard.tsx` - Card wrapper
|
||||
- `frontend/src/components/dashboard/EnhancedMetricCard.tsx` - Metric display
|
||||
- `frontend/src/components/common/PageHeader.tsx` - Page header
|
||||
- `frontend/src/pages/Planner/Dashboard.tsx` - Reference implementation
|
||||
- `frontend/src/pages/Writer/Dashboard.tsx` - Reference implementation
|
||||
|
||||
### Related Documentation
|
||||
- `master-docs/API-COMPLETE-REFERENCE.md` - API contracts
|
||||
- `REFACTOR_DOCS_INDEX.md` - Refactor documentation
|
||||
- `.github/copilot-instructions.md` - AI agent guidelines
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-01-21
|
||||
**Maintained By:** IGNY8 Development Team
|
||||
**Status:** Living Document - Update when design patterns change
|
||||
@@ -1,978 +0,0 @@
|
||||
# IGNY8 Implementation Audit Report
|
||||
**Date:** December 2024
|
||||
**Auditor:** GitHub Copilot (Claude Sonnet 4.5)
|
||||
**Scope:** IGNY8 Cluster + Site Refactor Plan (Nov 24) - IGNY8 App Only
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This audit reviews the **IGNY8 application** (frontend React/TypeScript + backend Django/Python) implementation against the "IGNY8 Cluster + Site Refactor Plan (Nov 24)" specifications. The WordPress plugin is **excluded** from this audit except where it integrates with IGNY8 for content import/sync.
|
||||
|
||||
**Overall Status:** 🔴 **MAJOR GAPS IDENTIFIED** (~33% Complete)
|
||||
|
||||
- ✅ **Fully Implemented:** 5 features (33%)
|
||||
- 🟡 **Partially Implemented:** 3 features (20%)
|
||||
- ❌ **Not Implemented:** 7 features (47%)
|
||||
|
||||
**Critical Issues:**
|
||||
1. ❌ Cluster detail page missing (`/planner/clusters/:id` route doesn't exist)
|
||||
2. ❌ Sites page UI not refactored (Builder/Blueprints buttons still visible)
|
||||
3. ❌ Settings page SEO tabs not merged into 2x2 grid
|
||||
4. ❌ Linker and Optimizer still visible in sidebar navigation
|
||||
|
||||
---
|
||||
|
||||
## Detailed Feature Audit
|
||||
|
||||
### 1. Persistent Login (localStorage)
|
||||
|
||||
**Status:** ✅ **FULLY IMPLEMENTED**
|
||||
|
||||
**Implementation Details:**
|
||||
- **File:** `frontend/src/store/authStore.ts`
|
||||
- **Lines:** 1-283
|
||||
- **Implementation:** Zustand store with `persist` middleware
|
||||
- **Storage Backend:** `localStorage` with key `auth-storage`
|
||||
- **Persisted State:**
|
||||
- `user` (User object with account/plan/role)
|
||||
- `token` (JWT access token)
|
||||
- `refreshToken` (JWT refresh token)
|
||||
- `isAuthenticated` (boolean flag)
|
||||
|
||||
**Code Evidence:**
|
||||
```typescript
|
||||
// frontend/src/store/authStore.ts
|
||||
export const useAuthStore = create<AuthState>()(
|
||||
persist<AuthState>(
|
||||
(set, get) => ({
|
||||
user: null,
|
||||
token: null,
|
||||
refreshToken: null,
|
||||
isAuthenticated: false,
|
||||
// ... auth actions: login, logout, refreshUser, refreshToken
|
||||
}),
|
||||
{
|
||||
name: 'auth-storage',
|
||||
partialize: (state) => ({
|
||||
user: state.user,
|
||||
token: state.token,
|
||||
refreshToken: state.refreshToken,
|
||||
isAuthenticated: state.isAuthenticated,
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
- ✅ Uses Zustand `persist` middleware
|
||||
- ✅ Stores token and refreshToken in localStorage
|
||||
- ✅ `refreshUser()` method validates session on app load
|
||||
- ✅ Auto-logout on token expiration
|
||||
|
||||
**Recommendation:** ✅ **No changes needed**
|
||||
|
||||
---
|
||||
|
||||
### 2. Writer Task Fields (`entity_type`, `cluster_role`)
|
||||
|
||||
**Status:** ✅ **FULLY IMPLEMENTED**
|
||||
|
||||
**Implementation Details:**
|
||||
- **File:** `backend/igny8_core/business/content/models.py`
|
||||
- **Model:** `Tasks`
|
||||
- **Lines:** 6-97
|
||||
|
||||
**Fields Implemented:**
|
||||
1. ✅ `entity_type` - CharField with choices (post, page, product, service, taxonomy_term)
|
||||
2. ✅ `cluster_role` - CharField with choices (hub, supporting, attribute)
|
||||
3. ✅ `cluster` - ForeignKey to `planner.Clusters`
|
||||
4. ✅ `keyword_objects` - ManyToManyField to `planner.Keywords`
|
||||
5. ✅ `taxonomy` - ForeignKey to `site_building.SiteBlueprintTaxonomy`
|
||||
|
||||
**Code Evidence:**
|
||||
```python
|
||||
# backend/igny8_core/business/content/models.py
|
||||
class Tasks(SiteSectorBaseModel):
|
||||
ENTITY_TYPE_CHOICES = [
|
||||
('post', 'Post'),
|
||||
('page', 'Page'),
|
||||
('product', 'Product'),
|
||||
('service', 'Service'),
|
||||
('taxonomy_term', 'Taxonomy Term'),
|
||||
]
|
||||
|
||||
CLUSTER_ROLE_CHOICES = [
|
||||
('hub', 'Hub'),
|
||||
('supporting', 'Supporting'),
|
||||
('attribute', 'Attribute'),
|
||||
]
|
||||
|
||||
entity_type = models.CharField(
|
||||
max_length=50,
|
||||
choices=ENTITY_TYPE_CHOICES,
|
||||
default='post',
|
||||
db_index=True,
|
||||
help_text="Type of content entity"
|
||||
)
|
||||
|
||||
cluster_role = models.CharField(
|
||||
max_length=50,
|
||||
choices=CLUSTER_ROLE_CHOICES,
|
||||
default='hub',
|
||||
help_text="Role within the cluster-driven sitemap"
|
||||
)
|
||||
|
||||
cluster = models.ForeignKey(
|
||||
'planner.Clusters',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='tasks'
|
||||
)
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
- ✅ `entity_type` field exists with correct choices
|
||||
- ✅ `cluster_role` field exists with correct choices
|
||||
- ✅ Database indexes on both fields
|
||||
- ✅ Proper foreign key relationships to Clusters
|
||||
|
||||
**Recommendation:** ✅ **No changes needed**
|
||||
|
||||
---
|
||||
|
||||
### 3. Content Model Fields (`entity_type`, `cluster_role`, `sync_status`)
|
||||
|
||||
**Status:** ✅ **FULLY IMPLEMENTED**
|
||||
|
||||
**Implementation Details:**
|
||||
- **File:** `backend/igny8_core/business/content/models.py`
|
||||
- **Model:** `Content`
|
||||
- **Lines:** 100-293
|
||||
|
||||
**Fields Implemented:**
|
||||
1. ✅ `entity_type` - CharField with choices (post, page, product, service, taxonomy_term, legacy types)
|
||||
2. ✅ `cluster_role` - CharField with choices (hub, supporting, attribute)
|
||||
3. ✅ `sync_status` - CharField with choices (native, imported, synced)
|
||||
4. ✅ `source` - CharField (igny8, wordpress, shopify, custom)
|
||||
5. ✅ `external_id`, `external_url`, `external_type` - for WP integration
|
||||
6. ✅ `structure_data` - JSONField for content metadata
|
||||
7. ✅ `json_blocks` - JSONField for structured content
|
||||
8. ✅ `cluster` - ForeignKey to `planner.Clusters`
|
||||
9. ✅ `taxonomies` - ManyToManyField to `ContentTaxonomy`
|
||||
|
||||
**Code Evidence:**
|
||||
```python
|
||||
# backend/igny8_core/business/content/models.py
|
||||
class Content(SiteSectorBaseModel):
|
||||
ENTITY_TYPE_CHOICES = [
|
||||
('post', 'Blog Post'),
|
||||
('page', 'Page'),
|
||||
('product', 'Product'),
|
||||
('service', 'Service Page'),
|
||||
('taxonomy_term', 'Taxonomy Term Page'),
|
||||
# Legacy choices for backward compatibility
|
||||
('blog_post', 'Blog Post (Legacy)'),
|
||||
('article', 'Article (Legacy)'),
|
||||
('taxonomy', 'Taxonomy Page (Legacy)'),
|
||||
]
|
||||
|
||||
SYNC_STATUS_CHOICES = [
|
||||
('native', 'Native IGNY8 Content'),
|
||||
('imported', 'Imported from External'),
|
||||
('synced', 'Synced from External'),
|
||||
]
|
||||
|
||||
CLUSTER_ROLE_CHOICES = [
|
||||
('hub', 'Hub Page'),
|
||||
('supporting', 'Supporting Content'),
|
||||
('attribute', 'Attribute Page'),
|
||||
]
|
||||
|
||||
entity_type = models.CharField(
|
||||
max_length=50,
|
||||
choices=ENTITY_TYPE_CHOICES,
|
||||
default='post',
|
||||
db_index=True
|
||||
)
|
||||
|
||||
sync_status = models.CharField(
|
||||
max_length=50,
|
||||
choices=SYNC_STATUS_CHOICES,
|
||||
default='native',
|
||||
db_index=True
|
||||
)
|
||||
|
||||
cluster_role = models.CharField(
|
||||
max_length=50,
|
||||
choices=CLUSTER_ROLE_CHOICES,
|
||||
default='supporting',
|
||||
blank=True,
|
||||
null=True,
|
||||
db_index=True
|
||||
)
|
||||
|
||||
structure_data = models.JSONField(
|
||||
default=dict,
|
||||
blank=True,
|
||||
help_text="Content structure data (metadata, schema, etc.)"
|
||||
)
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
- ✅ `entity_type` field with full choices (incl. legacy)
|
||||
- ✅ `sync_status` field for tracking import source
|
||||
- ✅ `cluster_role` field for cluster hierarchy
|
||||
- ✅ `structure_data` JSONField for flexible metadata
|
||||
- ✅ Proper indexing on all key fields
|
||||
|
||||
**Recommendation:** ✅ **No changes needed**
|
||||
|
||||
---
|
||||
|
||||
### 4. Cluster Detail Page (`/planner/clusters/:id`)
|
||||
|
||||
**Status:** ❌ **NOT IMPLEMENTED**
|
||||
|
||||
**Expected Implementation:**
|
||||
- Route: `/planner/clusters/:id`
|
||||
- Component: `frontend/src/pages/Planner/ClusterDetail.tsx` (doesn't exist)
|
||||
- Features: View cluster metadata, keywords, tasks, content ideas
|
||||
|
||||
**Current State:**
|
||||
- **Route:** ❌ NOT DEFINED in `App.tsx`
|
||||
- **Component:** ❌ DOES NOT EXIST
|
||||
- **Navigation:** ❌ Cluster names in table are NOT clickable
|
||||
- **Workaround:** PostEditor.tsx line 820 has navigate to `/planner/clusters/${cluster_id}` but route doesn't exist
|
||||
|
||||
**Code Evidence:**
|
||||
```tsx
|
||||
// frontend/src/App.tsx - Lines 200-221
|
||||
// Planner routes - NO cluster detail route exists
|
||||
<Route path="/planner" element={<Navigate to="/planner/keywords" replace />} />
|
||||
<Route path="/planner/keywords" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="planner">
|
||||
<Keywords />
|
||||
</ModuleGuard>
|
||||
</Suspense>
|
||||
} />
|
||||
<Route path="/planner/clusters" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="planner">
|
||||
<Clusters /> {/* This is the TABLE view only */}
|
||||
</ModuleGuard>
|
||||
</Suspense>
|
||||
} />
|
||||
<Route path="/planner/ideas" element={
|
||||
<Suspense fallback={null}>
|
||||
<ModuleGuard module="planner">
|
||||
<Ideas />
|
||||
</ModuleGuard>
|
||||
</Suspense>
|
||||
} />
|
||||
// ❌ NO <Route path="/planner/clusters/:id" element={...} />
|
||||
```
|
||||
|
||||
```tsx
|
||||
// frontend/src/pages/Planner/Clusters.tsx - Lines 1-450
|
||||
// Clusters page is TABLE-ONLY, no detail view
|
||||
export default function Clusters() {
|
||||
// ... table configuration only
|
||||
// ❌ NO cluster name click handler to navigate to detail page
|
||||
// ❌ NO detail view component
|
||||
}
|
||||
```
|
||||
|
||||
**Affected Files:**
|
||||
1. ❌ `App.tsx` - Missing route definition
|
||||
2. ❌ `pages/Planner/ClusterDetail.tsx` - Component doesn't exist
|
||||
3. 🟡 `pages/Planner/Clusters.tsx` - Table exists but no clickable names
|
||||
4. ⚠️ `pages/Sites/PostEditor.tsx:820` - Has broken link to cluster detail
|
||||
|
||||
**Missing Functionality:**
|
||||
- ❌ View cluster metadata (name, description, context_type, dimension_meta)
|
||||
- ❌ List all keywords in cluster with stats (volume, difficulty, status)
|
||||
- ❌ List all content ideas linked to cluster
|
||||
- ❌ List all tasks/content linked to cluster
|
||||
- ❌ Edit cluster details (name, description, context_type)
|
||||
- ❌ Add/remove keywords from cluster
|
||||
- ❌ Generate new ideas from cluster keywords
|
||||
|
||||
**Recommendation:** 🔴 **CRITICAL - CREATE CLUSTER DETAIL PAGE**
|
||||
|
||||
**Implementation Steps:**
|
||||
1. Create `frontend/src/pages/Planner/ClusterDetail.tsx`
|
||||
2. Add route in `App.tsx`: `<Route path="/planner/clusters/:id" element={<ClusterDetail />} />`
|
||||
3. Make cluster names clickable in `Clusters.tsx` table
|
||||
4. Create API endpoints: `GET /v1/planner/clusters/:id/keywords/`, `/ideas/`, `/tasks/`
|
||||
5. Add tabs: Overview, Keywords, Ideas, Tasks, Settings
|
||||
|
||||
---
|
||||
|
||||
### 5. Cluster Model Fields (`context_type`, `dimension_meta`)
|
||||
|
||||
**Status:** ✅ **FULLY IMPLEMENTED**
|
||||
|
||||
**Implementation Details:**
|
||||
- **File:** `backend/igny8_core/business/planning/models.py`
|
||||
- **Model:** `Clusters`
|
||||
- **Lines:** 5-52
|
||||
|
||||
**Fields Implemented:**
|
||||
1. ✅ `context_type` - CharField with choices (topic, attribute, service_line)
|
||||
2. ✅ `dimension_meta` - JSONField for extended metadata
|
||||
3. ✅ Proper indexes and database constraints
|
||||
|
||||
**Code Evidence:**
|
||||
```python
|
||||
# backend/igny8_core/business/planning/models.py
|
||||
class Clusters(SiteSectorBaseModel):
|
||||
CONTEXT_TYPE_CHOICES = [
|
||||
('topic', 'Topic Cluster'),
|
||||
('attribute', 'Attribute Cluster'),
|
||||
('service_line', 'Service Line'),
|
||||
]
|
||||
|
||||
name = models.CharField(max_length=255, unique=True, db_index=True)
|
||||
description = models.TextField(blank=True, null=True)
|
||||
keywords_count = models.IntegerField(default=0)
|
||||
volume = models.IntegerField(default=0)
|
||||
mapped_pages = models.IntegerField(default=0)
|
||||
status = models.CharField(max_length=50, default='active')
|
||||
|
||||
context_type = models.CharField(
|
||||
max_length=50,
|
||||
choices=CONTEXT_TYPE_CHOICES,
|
||||
default='topic',
|
||||
help_text="Primary dimension for this cluster"
|
||||
)
|
||||
|
||||
dimension_meta = models.JSONField(
|
||||
default=dict,
|
||||
blank=True,
|
||||
help_text="Extended metadata (taxonomy hints, attribute suggestions, coverage targets)"
|
||||
)
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
- ✅ `context_type` field with choices
|
||||
- ✅ `dimension_meta` JSONField for flexible metadata
|
||||
- ✅ Database index on `context_type`
|
||||
- ✅ Proper default values
|
||||
|
||||
**Recommendation:** ✅ **No changes needed**
|
||||
|
||||
---
|
||||
|
||||
### 6. Site Builder Hidden from Navigation
|
||||
|
||||
**Status:** ❌ **NOT IMPLEMENTED**
|
||||
|
||||
**Expected Implementation:**
|
||||
- Remove "Create with Builder" button from Sites page
|
||||
- Remove "Blueprints" navigation tab
|
||||
- Hide Blueprint-related functionality
|
||||
|
||||
**Current State:**
|
||||
- ❌ "Create with Builder" button STILL VISIBLE
|
||||
- ❌ "Blueprints" tab STILL in navigation
|
||||
- ❌ Blueprint routes still active
|
||||
|
||||
**Code Evidence:**
|
||||
```tsx
|
||||
// frontend/src/pages/Sites/List.tsx - Lines 688-689
|
||||
const tabItems = [
|
||||
{ label: 'Sites', path: '/sites', icon: <GridIcon className="w-4 h-4" /> },
|
||||
{ label: 'Create Site', path: '/sites/builder', icon: <PlusIcon className="w-4 h-4" /> }, // ❌ SHOULD BE REMOVED
|
||||
{ label: 'Blueprints', path: '/sites/blueprints', icon: <FileIcon className="w-4 h-4" /> }, // ❌ SHOULD BE REMOVED
|
||||
];
|
||||
|
||||
// Lines 717-721
|
||||
<div className="flex gap-2">
|
||||
<Button onClick={() => navigate('/sites/builder')} variant="outline"> {/* ❌ SHOULD BE REMOVED */}
|
||||
<PlusIcon className="w-5 h-5 mr-2" />
|
||||
Create with Builder
|
||||
</Button>
|
||||
<Button onClick={handleCreateSite} variant="primary">
|
||||
<PlusIcon className="w-5 h-5 mr-2" />
|
||||
Add Site
|
||||
</Button>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Affected Files:**
|
||||
1. `frontend/src/pages/Sites/List.tsx` - Lines 688-689, 717-721
|
||||
2. `frontend/src/pages/Sites/DeploymentPanel.tsx` - Still uses `fetchSiteBlueprints`
|
||||
3. `frontend/src/App.tsx` - May have `/sites/builder` and `/sites/blueprints` routes
|
||||
|
||||
**Recommendation:** 🔴 **CRITICAL - REMOVE BUILDER UI**
|
||||
|
||||
**Implementation Steps:**
|
||||
1. Remove "Create with Builder" button from Sites/List.tsx (line 717-720)
|
||||
2. Remove "Blueprints" tab from tabItems (line 689)
|
||||
3. Remove "Create Site" tab from tabItems (line 688) OR rename to "Add Site"
|
||||
4. Remove or disable `/sites/builder` and `/sites/blueprints` routes in App.tsx
|
||||
5. Update DeploymentPanel to not depend on Blueprints
|
||||
|
||||
---
|
||||
|
||||
### 7. Linker & Optimizer Hidden from Sidebar
|
||||
|
||||
**Status:** ❌ **NOT IMPLEMENTED**
|
||||
|
||||
**Expected Implementation:**
|
||||
- Hide Linker and Optimizer from main navigation
|
||||
- Make accessible only through admin/settings
|
||||
|
||||
**Current State:**
|
||||
- ❌ Linker IS VISIBLE in sidebar (WORKFLOW section)
|
||||
- ❌ Optimizer IS VISIBLE in sidebar (WORKFLOW section)
|
||||
- ✅ Gated by module enable settings (can be disabled)
|
||||
|
||||
**Code Evidence:**
|
||||
```tsx
|
||||
// frontend/src/layout/AppSidebar.tsx - Lines 136-153
|
||||
const workflowItems: NavItem[] = [];
|
||||
|
||||
if (moduleEnabled('planner')) {
|
||||
workflowItems.push({
|
||||
icon: <ListIcon />,
|
||||
name: "Planner",
|
||||
path: "/planner/keywords",
|
||||
});
|
||||
}
|
||||
|
||||
if (moduleEnabled('writer')) {
|
||||
workflowItems.push({
|
||||
icon: <TaskIcon />,
|
||||
name: "Writer",
|
||||
path: "/writer/content",
|
||||
});
|
||||
}
|
||||
|
||||
// ❌ LINKER STILL ADDED TO WORKFLOW
|
||||
if (moduleEnabled('linker')) {
|
||||
workflowItems.push({
|
||||
icon: <PlugInIcon />,
|
||||
name: "Linker",
|
||||
path: "/linker/content",
|
||||
});
|
||||
}
|
||||
|
||||
// ❌ OPTIMIZER STILL ADDED TO WORKFLOW
|
||||
if (moduleEnabled('optimizer')) {
|
||||
workflowItems.push({
|
||||
icon: <BoltIcon />,
|
||||
name: "Optimizer",
|
||||
path: "/optimizer/content",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Affected Files:**
|
||||
1. `frontend/src/layout/AppSidebar.tsx` - Lines 136-153
|
||||
2. Module enable settings control visibility (partial mitigation)
|
||||
|
||||
**Recommendation:** 🔴 **CRITICAL - REMOVE FROM SIDEBAR**
|
||||
|
||||
**Implementation Steps:**
|
||||
1. Remove Linker and Optimizer from `workflowItems` in AppSidebar.tsx
|
||||
2. Move to admin-only section or remove entirely
|
||||
3. OR update module enable settings to default Linker/Optimizer to disabled
|
||||
4. Update documentation to reflect Linker/Optimizer as deprecated/future features
|
||||
|
||||
---
|
||||
|
||||
### 8. Sites Page UX Cleanup
|
||||
|
||||
**Status:** ❌ **NOT IMPLEMENTED**
|
||||
|
||||
**Expected Implementation:**
|
||||
- Remove "Pages" button from site cards
|
||||
- Move "Sectors" button to Settings page
|
||||
- Clean up top banners
|
||||
- Simplify navigation
|
||||
|
||||
**Current State:**
|
||||
- ❌ Not verified (need to check grid card buttons)
|
||||
- ⚠️ Table/Grid toggle exists (line 726)
|
||||
- ✅ ModuleNavigationTabs exists (line 690)
|
||||
|
||||
**Files to Check:**
|
||||
1. `frontend/src/pages/Sites/List.tsx` - Grid card rendering section
|
||||
2. Need to search for "Pages" button and "Sectors" button in grid cards
|
||||
|
||||
**Recommendation:** 🟡 **NEEDS VERIFICATION**
|
||||
|
||||
**Next Steps:**
|
||||
1. Read `Sites/List.tsx` lines 800-1000 to check grid card rendering
|
||||
2. Search for "Pages" button in card actions
|
||||
3. Search for "Sectors" button/modal in card actions
|
||||
4. Verify top banner content
|
||||
|
||||
---
|
||||
|
||||
### 9. Site Settings 2x2 Grid Layout
|
||||
|
||||
**Status:** ❌ **NOT IMPLEMENTED**
|
||||
|
||||
**Expected Implementation:**
|
||||
- Merge SEO tabs (Meta Tags, Open Graph, Schema) into single page with 2x2 grid
|
||||
- Add Sectors tab to Settings
|
||||
- Simplify tab structure
|
||||
|
||||
**Current State:**
|
||||
- ❌ SEO tabs STILL SEPARATE (seo, og, schema)
|
||||
- ❌ NO Sectors tab in Settings
|
||||
- ✅ WordPress integration tab exists
|
||||
- ✅ Content Types tab exists
|
||||
|
||||
**Code Evidence:**
|
||||
```tsx
|
||||
// frontend/src/pages/Sites/Settings.tsx - Lines 43-44
|
||||
const initialTab = (searchParams.get('tab') as
|
||||
'general' | 'seo' | 'og' | 'schema' | 'integrations' | 'content-types') || 'general';
|
||||
|
||||
const [activeTab, setActiveTab] = useState<
|
||||
'general' | 'seo' | 'og' | 'schema' | 'integrations' | 'content-types'
|
||||
>(initialTab);
|
||||
|
||||
// ❌ STILL HAS SEPARATE TABS: 'seo', 'og', 'schema'
|
||||
// ❌ NO 'sectors' tab
|
||||
```
|
||||
|
||||
**Current Tab Structure:**
|
||||
1. ✅ `general` - Site name, domain, description
|
||||
2. ❌ `seo` - Meta tags (should be merged)
|
||||
3. ❌ `og` - Open Graph (should be merged)
|
||||
4. ❌ `schema` - Schema.org (should be merged)
|
||||
5. ✅ `integrations` - WordPress/Shopify integrations
|
||||
6. ✅ `content-types` - Content type management
|
||||
7. ❌ **MISSING:** `sectors` tab
|
||||
|
||||
**Expected Tab Structure:**
|
||||
1. `general` - Site name, domain, description
|
||||
2. `seo-metadata` - 2x2 grid: Meta Tags | Open Graph | Schema.org | Twitter Cards
|
||||
3. `sectors` - Manage active sectors for site
|
||||
4. `integrations` - WordPress/Shopify integrations
|
||||
5. `content-types` - Content type management
|
||||
|
||||
**Recommendation:** 🔴 **CRITICAL - REFACTOR SETTINGS TABS**
|
||||
|
||||
**Implementation Steps:**
|
||||
1. Merge `seo`, `og`, `schema` tabs into single `seo-metadata` tab
|
||||
2. Create 2x2 grid layout component for SEO metadata
|
||||
3. Add `sectors` tab with sector management UI
|
||||
4. Update tab type definitions
|
||||
5. Update URL param handling
|
||||
6. Test all form submissions
|
||||
|
||||
---
|
||||
|
||||
### 10. Site Card Button Rename
|
||||
|
||||
**Status:** 🟡 **NEEDS VERIFICATION**
|
||||
|
||||
**Expected Implementation:**
|
||||
- "Content" button renamed to "Content Manager"
|
||||
- "Pages" button removed
|
||||
- "Sectors" button moved to Settings
|
||||
|
||||
**Current State:**
|
||||
- ⚠️ Need to check grid card rendering in Sites/List.tsx
|
||||
- ⚠️ Need to verify button labels
|
||||
|
||||
**Recommendation:** 🟡 **VERIFY GRID CARD BUTTONS**
|
||||
|
||||
**Next Steps:**
|
||||
1. Read Sites/List.tsx grid rendering section
|
||||
2. Check for "Content" button label
|
||||
3. Check for "Pages" and "Sectors" buttons
|
||||
|
||||
---
|
||||
|
||||
### 11. Content Taxonomy Model
|
||||
|
||||
**Status:** ✅ **FULLY IMPLEMENTED**
|
||||
|
||||
**Implementation Details:**
|
||||
- **File:** `backend/igny8_core/business/content/models.py`
|
||||
- **Model:** `ContentTaxonomy`
|
||||
- **Lines:** 296-398
|
||||
|
||||
**Features Implemented:**
|
||||
1. ✅ `taxonomy_type` - category, tag, product_cat, product_tag, product_attr, service_cat
|
||||
2. ✅ `sync_status` - native, imported, synced
|
||||
3. ✅ `external_id`, `external_taxonomy` - WordPress integration fields
|
||||
4. ✅ Hierarchical support via `parent` ForeignKey
|
||||
5. ✅ Cluster mapping via `clusters` ManyToManyField
|
||||
6. ✅ Unique constraints on slug and external_id
|
||||
|
||||
**Code Evidence:**
|
||||
```python
|
||||
# backend/igny8_core/business/content/models.py
|
||||
class ContentTaxonomy(SiteSectorBaseModel):
|
||||
TAXONOMY_TYPE_CHOICES = [
|
||||
('category', 'Category'),
|
||||
('tag', 'Tag'),
|
||||
('product_cat', 'Product Category'),
|
||||
('product_tag', 'Product Tag'),
|
||||
('product_attr', 'Product Attribute'),
|
||||
('service_cat', 'Service Category'),
|
||||
]
|
||||
|
||||
SYNC_STATUS_CHOICES = [
|
||||
('native', 'Native IGNY8'),
|
||||
('imported', 'Imported from External'),
|
||||
('synced', 'Synced with External'),
|
||||
]
|
||||
|
||||
name = models.CharField(max_length=255, db_index=True)
|
||||
slug = models.SlugField(max_length=255, db_index=True)
|
||||
taxonomy_type = models.CharField(max_length=50, choices=TAXONOMY_TYPE_CHOICES, db_index=True)
|
||||
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE, related_name='children')
|
||||
|
||||
# WordPress/WooCommerce sync fields
|
||||
external_id = models.IntegerField(null=True, blank=True, db_index=True)
|
||||
external_taxonomy = models.CharField(max_length=100, blank=True)
|
||||
sync_status = models.CharField(max_length=50, choices=SYNC_STATUS_CHOICES, default='native', db_index=True)
|
||||
|
||||
# Cluster mapping
|
||||
clusters = models.ManyToManyField('planner.Clusters', blank=True, related_name='taxonomy_terms')
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
- ✅ Supports all required taxonomy types
|
||||
- ✅ WordPress integration fields present
|
||||
- ✅ Sync status tracking implemented
|
||||
- ✅ Hierarchical taxonomy support
|
||||
- ✅ Cluster mapping for semantic relationships
|
||||
|
||||
**Recommendation:** ✅ **No changes needed**
|
||||
|
||||
---
|
||||
|
||||
### 12. WordPress Content Import (Integration Only)
|
||||
|
||||
**Status:** ✅ **FULLY IMPLEMENTED** (in IGNY8 backend + WP plugin)
|
||||
|
||||
**Implementation Details:**
|
||||
|
||||
**IGNY8 Backend:**
|
||||
- **File:** `backend/igny8_core/modules/integration/` (various files)
|
||||
- **Endpoints:** REST API endpoints for receiving WordPress data
|
||||
- **Models:** `Content`, `ContentTaxonomy` with `sync_status` and `external_*` fields
|
||||
|
||||
**WordPress Plugin:**
|
||||
- **Files:** `igny8-wp-integration/includes/class-igny8-rest-api.php`
|
||||
- **Endpoints:** `/wp-json/igny8/v1/site-metadata/`, `/post-by-task-id/`, `/post-by-content-id/`
|
||||
- **Sync Logic:** `sync/igny8-to-wp.php`, `sync/post-sync.php`
|
||||
|
||||
**Data Flow:**
|
||||
1. ✅ WordPress → IGNY8: Site structure discovery via `/site-metadata/` endpoint
|
||||
2. ✅ WordPress → IGNY8: Content import with postmeta `_igny8_task_id`, `_igny8_content_id`
|
||||
3. ✅ IGNY8 → WordPress: Publish content back to WP via REST API
|
||||
4. ✅ Taxonomy sync: Categories, tags, product attributes
|
||||
|
||||
**Fields for Import:**
|
||||
- ✅ `sync_status` = 'imported' for WP content
|
||||
- ✅ `source` = 'wordpress'
|
||||
- ✅ `external_id` = WP post ID
|
||||
- ✅ `external_url` = WP post URL
|
||||
- ✅ `external_type` = WP post type (post, page, product)
|
||||
- ✅ `sync_metadata` JSONField for additional WP data
|
||||
|
||||
**Verification:**
|
||||
- ✅ Content model supports import tracking
|
||||
- ✅ WordPress plugin exposes required endpoints
|
||||
- ✅ Sync status properly tracked
|
||||
|
||||
**Recommendation:** ✅ **No changes needed** (WordPress integration working as designed)
|
||||
|
||||
---
|
||||
|
||||
### 13. Module Enable Settings (Linker/Optimizer Default OFF)
|
||||
|
||||
**Status:** 🟡 **PARTIALLY IMPLEMENTED**
|
||||
|
||||
**Implementation Details:**
|
||||
- **File:** `frontend/src/store/settingsStore.ts`
|
||||
- **Backend:** `backend/igny8_core/modules/system/models.py` (likely `ModuleEnableSettings` model)
|
||||
|
||||
**Current State:**
|
||||
- ✅ Module enable settings exist and are checked in AppSidebar
|
||||
- ✅ Linker and Optimizer are gated by `moduleEnabled()` checks
|
||||
- ⚠️ **DEFAULT VALUES NOT VERIFIED** - need to check backend defaults
|
||||
|
||||
**Code Evidence:**
|
||||
```tsx
|
||||
// frontend/src/layout/AppSidebar.tsx - Lines 136-153
|
||||
if (moduleEnabled('linker')) {
|
||||
workflowItems.push({
|
||||
icon: <PlugInIcon />,
|
||||
name: "Linker",
|
||||
path: "/linker/content",
|
||||
});
|
||||
}
|
||||
|
||||
if (moduleEnabled('optimizer')) {
|
||||
workflowItems.push({
|
||||
icon: <BoltIcon />,
|
||||
name: "Optimizer",
|
||||
path: "/optimizer/content",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Missing Verification:**
|
||||
- ⚠️ What are the DEFAULT values for `linker` and `optimizer` modules?
|
||||
- ⚠️ Are they enabled or disabled by default for new accounts?
|
||||
|
||||
**Recommendation:** 🟡 **VERIFY DEFAULT VALUES**
|
||||
|
||||
**Next Steps:**
|
||||
1. Check `backend/igny8_core/modules/system/models.py` for `ModuleEnableSettings`
|
||||
2. Verify default values in database migrations or model definitions
|
||||
3. Ensure Linker and Optimizer default to `enabled=False`
|
||||
4. Update settings UI to show Linker/Optimizer as "Future Feature" or hide entirely
|
||||
|
||||
---
|
||||
|
||||
### 14. Content Manager Page
|
||||
|
||||
**Status:** 🟡 **NEEDS VERIFICATION**
|
||||
|
||||
**Expected Implementation:**
|
||||
- `/sites/:id/content` route exists
|
||||
- Shows all content for a site (posts, pages, products, services)
|
||||
- Filters by entity_type, sync_status, cluster
|
||||
|
||||
**Current State:**
|
||||
- ✅ Route exists: `/sites/:id/content` (App.tsx line 462)
|
||||
- ✅ Component exists: `frontend/src/pages/Sites/Content.tsx`
|
||||
- ⚠️ **NEED TO VERIFY:** Does it filter by entity_type? Does it show sync_status?
|
||||
|
||||
**Code Evidence:**
|
||||
```tsx
|
||||
// frontend/src/App.tsx - Lines 462-466
|
||||
<Route path="/sites/:id/content" element={
|
||||
<Suspense fallback={null}>
|
||||
<SiteContent />
|
||||
</Suspense>
|
||||
} />
|
||||
```
|
||||
|
||||
**Recommendation:** 🟡 **VERIFY CONTENT MANAGER FEATURES**
|
||||
|
||||
**Next Steps:**
|
||||
1. Read `frontend/src/pages/Sites/Content.tsx`
|
||||
2. Verify it shows content from ALL entity types (posts, pages, products, services)
|
||||
3. Verify filters: entity_type, sync_status, cluster, status
|
||||
4. Verify it displays imported content (sync_status='imported')
|
||||
|
||||
---
|
||||
|
||||
### 15. Cluster-Driven Content Generation
|
||||
|
||||
**Status:** ✅ **FULLY IMPLEMENTED** (Backend Models + Relationships)
|
||||
|
||||
**Implementation Details:**
|
||||
|
||||
**Models:**
|
||||
1. ✅ `Clusters` model with `context_type` and `dimension_meta`
|
||||
2. ✅ `Keywords.cluster` ForeignKey
|
||||
3. ✅ `ContentIdeas.keyword_cluster` ForeignKey
|
||||
4. ✅ `ContentIdeas.cluster_role` field
|
||||
5. ✅ `Tasks.cluster` ForeignKey + `cluster_role` field
|
||||
6. ✅ `Content.cluster` ForeignKey + `cluster_role` field
|
||||
|
||||
**Relationships:**
|
||||
```
|
||||
Clusters → Keywords (1:M)
|
||||
Clusters → ContentIdeas (1:M)
|
||||
Clusters → Tasks (1:M)
|
||||
Clusters → Content (1:M)
|
||||
```
|
||||
|
||||
**Code Evidence:**
|
||||
```python
|
||||
# backend/igny8_core/business/planning/models.py
|
||||
class Keywords(SiteSectorBaseModel):
|
||||
cluster = models.ForeignKey(
|
||||
'Clusters',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='keywords'
|
||||
)
|
||||
|
||||
# backend/igny8_core/business/content/models.py
|
||||
class Tasks(SiteSectorBaseModel):
|
||||
cluster = models.ForeignKey(
|
||||
'planner.Clusters',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='tasks'
|
||||
)
|
||||
cluster_role = models.CharField(max_length=50, choices=CLUSTER_ROLE_CHOICES, default='hub')
|
||||
|
||||
class Content(SiteSectorBaseModel):
|
||||
cluster = models.ForeignKey(
|
||||
'planner.Clusters',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='contents'
|
||||
)
|
||||
cluster_role = models.CharField(max_length=50, choices=CLUSTER_ROLE_CHOICES, default='supporting')
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
- ✅ All models have cluster relationships
|
||||
- ✅ `cluster_role` field exists on Tasks and Content
|
||||
- ✅ Cluster detail page can list all related Keywords, Ideas, Tasks, Content
|
||||
- ⚠️ **MISSING UI:** No cluster detail page to visualize relationships
|
||||
|
||||
**Recommendation:** 🟡 **BACKEND COMPLETE, NEED FRONTEND UI**
|
||||
|
||||
**Next Steps:**
|
||||
1. Create Cluster Detail page (see Feature #4)
|
||||
2. Show cluster hierarchy visualization
|
||||
3. Show keyword→idea→task→content generation flow
|
||||
|
||||
---
|
||||
|
||||
## Summary Table
|
||||
|
||||
| # | Feature | Status | Files Affected | Priority |
|
||||
|---|---------|--------|----------------|----------|
|
||||
| 1 | Persistent Login | ✅ DONE | `authStore.ts` | ✅ Complete |
|
||||
| 2 | Writer Task Fields | ✅ DONE | `content/models.py` | ✅ Complete |
|
||||
| 3 | Content Model Fields | ✅ DONE | `content/models.py` | ✅ Complete |
|
||||
| 4 | Cluster Detail Page | ❌ MISSING | `App.tsx`, `ClusterDetail.tsx` (new) | 🔴 CRITICAL |
|
||||
| 5 | Cluster Model Fields | ✅ DONE | `planning/models.py` | ✅ Complete |
|
||||
| 6 | Site Builder Hidden | ❌ NOT DONE | `Sites/List.tsx` | 🔴 CRITICAL |
|
||||
| 7 | Linker/Optimizer Hidden | ❌ NOT DONE | `AppSidebar.tsx` | 🔴 CRITICAL |
|
||||
| 8 | Sites Page UX Cleanup | 🟡 VERIFY | `Sites/List.tsx` | 🟡 Medium |
|
||||
| 9 | Settings 2x2 Grid | ❌ NOT DONE | `Sites/Settings.tsx` | 🔴 CRITICAL |
|
||||
| 10 | Site Card Button Rename | 🟡 VERIFY | `Sites/List.tsx` | 🟡 Medium |
|
||||
| 11 | Content Taxonomy Model | ✅ DONE | `content/models.py` | ✅ Complete |
|
||||
| 12 | WordPress Import | ✅ DONE | Backend + WP Plugin | ✅ Complete |
|
||||
| 13 | Module Enable Defaults | 🟡 VERIFY | Backend models | 🟡 Medium |
|
||||
| 14 | Content Manager | 🟡 VERIFY | `Sites/Content.tsx` | 🟡 Medium |
|
||||
| 15 | Cluster-Driven Content | ✅ DONE | Backend models | ⚠️ Need UI |
|
||||
|
||||
---
|
||||
|
||||
## Priority Fixes
|
||||
|
||||
### 🔴 CRITICAL (Must Fix Before Launch)
|
||||
|
||||
1. **Create Cluster Detail Page** (Feature #4)
|
||||
- Route: `/planner/clusters/:id`
|
||||
- Component: `ClusterDetail.tsx`
|
||||
- API: `/v1/planner/clusters/:id/keywords/`, `/ideas/`, `/tasks/`
|
||||
|
||||
2. **Remove Site Builder UI** (Feature #6)
|
||||
- Remove "Create with Builder" button
|
||||
- Remove "Blueprints" navigation tab
|
||||
- Disable Builder routes
|
||||
|
||||
3. **Hide Linker & Optimizer** (Feature #7)
|
||||
- Remove from AppSidebar WORKFLOW section
|
||||
- OR set default enabled=False in module settings
|
||||
|
||||
4. **Refactor Settings Tabs** (Feature #9)
|
||||
- Merge SEO tabs into 2x2 grid
|
||||
- Add Sectors tab
|
||||
- Simplify navigation
|
||||
|
||||
### 🟡 MEDIUM (Verify & Fix)
|
||||
|
||||
5. **Verify Sites Page UX** (Feature #8)
|
||||
- Check grid card buttons
|
||||
- Confirm "Pages" and "Sectors" buttons removed
|
||||
|
||||
6. **Verify Content Manager** (Feature #14)
|
||||
- Check entity_type filters
|
||||
- Check sync_status display
|
||||
|
||||
7. **Verify Module Defaults** (Feature #13)
|
||||
- Check Linker/Optimizer default values
|
||||
|
||||
---
|
||||
|
||||
## WordPress Plugin Integration Notes
|
||||
|
||||
The WordPress plugin (`igny8-wp-integration/`) is **working correctly** for content import/sync and is **excluded from this audit** except where it integrates with IGNY8:
|
||||
|
||||
- ✅ REST endpoints for site metadata discovery
|
||||
- ✅ Postmeta storage (`_igny8_task_id`, `_igny8_content_id`)
|
||||
- ✅ Taxonomy sync (categories, tags, product attributes)
|
||||
- ✅ Two-way sync: WP ↔ IGNY8
|
||||
|
||||
**No changes needed** to WordPress plugin based on refactor plan.
|
||||
|
||||
---
|
||||
|
||||
## Recommendations
|
||||
|
||||
### Immediate Actions (Week 1)
|
||||
|
||||
1. **Create Cluster Detail Page**
|
||||
- Create component `ClusterDetail.tsx`
|
||||
- Add route `/planner/clusters/:id`
|
||||
- Make cluster names clickable in table
|
||||
- Show keywords, ideas, tasks, content
|
||||
|
||||
2. **Remove Builder UI**
|
||||
- Remove buttons from Sites/List.tsx
|
||||
- Remove Blueprints tab
|
||||
- Disable routes
|
||||
|
||||
3. **Hide Linker/Optimizer**
|
||||
- Remove from sidebar or set default disabled
|
||||
- Update documentation
|
||||
|
||||
### Short-term Actions (Week 2-3)
|
||||
|
||||
4. **Refactor Settings Tabs**
|
||||
- Create 2x2 grid component for SEO
|
||||
- Merge tabs
|
||||
- Add Sectors tab
|
||||
|
||||
5. **Verify & Fix Sites UX**
|
||||
- Check grid cards
|
||||
- Rename buttons
|
||||
- Test navigation
|
||||
|
||||
### Long-term Actions (Month 1-2)
|
||||
|
||||
6. **Complete Content Manager**
|
||||
- Add entity_type filters
|
||||
- Add sync_status badges
|
||||
- Add cluster filtering
|
||||
|
||||
7. **Update Documentation**
|
||||
- Update user guides for new UI
|
||||
- Document cluster workflow
|
||||
- Document content import flow
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
The IGNY8 app has **strong backend foundation** (✅ 5/15 features complete) but **critical UI/UX gaps** (❌ 4/15 features missing, 🟡 3/15 need verification).
|
||||
|
||||
**Top Priority:** Create Cluster Detail Page, remove Builder UI, hide Linker/Optimizer, refactor Settings tabs.
|
||||
|
||||
**Estimated Effort:** ~2-3 weeks for critical fixes, 1-2 weeks for verification/polish.
|
||||
|
||||
---
|
||||
|
||||
**Report Generated:** December 2024
|
||||
**Next Review:** After critical fixes implemented
|
||||
@@ -1,301 +0,0 @@
|
||||
# DDAY REFACTOR — Complete Documentation Index
|
||||
|
||||
**Date:** November 26, 2025
|
||||
**Status:** Stage 3 Complete ✅ | Stage 4 Ready for Implementation 📋
|
||||
|
||||
---
|
||||
|
||||
## 📚 DOCUMENTATION OVERVIEW
|
||||
|
||||
This folder contains the complete documentation for the DDAY refactor project, tracking progress from the original specifications through all implementation stages.
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ DOCUMENT STRUCTURE
|
||||
|
||||
### 1. **Original Specifications**
|
||||
**File:** `original-specs of-refactor`
|
||||
**Purpose:** The foundational DDAY refactor specifications
|
||||
**Key Content:**
|
||||
- End-to-end keyword → WordPress flow
|
||||
- Final data model specifications
|
||||
- WordPress integration requirements
|
||||
- Content Manager as single source of truth
|
||||
- Frontend module alignment requirements
|
||||
|
||||
**Status:** ✅ Original vision document (reference only)
|
||||
|
||||
---
|
||||
|
||||
### 2. **Gap Analysis**
|
||||
**File:** `REFACTOR_GAP_ANALYSIS.md`
|
||||
**Created:** November 26, 2025
|
||||
**Purpose:** Comprehensive comparison of original specs vs. current implementation
|
||||
|
||||
**Key Findings:**
|
||||
- **Overall Compliance:** 82% (33/40 major components)
|
||||
- **Total Gaps Identified:** 20
|
||||
- Critical: 2 (batch operations)
|
||||
- High: 6 (Sites UI, Content Manager features)
|
||||
- Medium: 5 (cleanup, validation)
|
||||
- Low: 7 (tests, polish)
|
||||
|
||||
**Critical Gaps:**
|
||||
1. No batch cluster assignment in Content Manager
|
||||
2. No batch taxonomy assignment in Content Manager
|
||||
3. Sites module builder buttons not removed
|
||||
4. Content Manager lacks unassigned filter/highlighting
|
||||
5. ContentTaxonomy.sync_status still in filters
|
||||
6. PostEditor SEO/Metadata tabs still exist
|
||||
|
||||
**Status:** ✅ Analysis complete — provides foundation for Stage 4
|
||||
|
||||
---
|
||||
|
||||
### 3. **Stage Implementation Docs**
|
||||
|
||||
#### Stage 1: Backend Refactor
|
||||
**File:** `STAGE_1_COMPLETE.md`
|
||||
**Completion:** November 25, 2025
|
||||
**Status:** ✅ 100% Complete
|
||||
|
||||
**Accomplished:**
|
||||
- Removed deprecated fields from all models
|
||||
- Updated serializers and ViewSets
|
||||
- Generated and applied migrations
|
||||
- Cleaned up admin interfaces
|
||||
|
||||
**Files Modified:** 8 backend files
|
||||
|
||||
---
|
||||
|
||||
#### Stage 2: Frontend Refactor
|
||||
**File:** `STAGE_2_REFACTOR_COMPLETE.md`
|
||||
**Completion:** November 25, 2025
|
||||
**Status:** ✅ 92% Complete (2 legacy components deferred)
|
||||
|
||||
**Accomplished:**
|
||||
- Updated 25 frontend files
|
||||
- Removed deprecated field references
|
||||
- Updated API types and interfaces
|
||||
- Created Cluster Detail page
|
||||
|
||||
**Remaining Work:** PostEditor tabs, ToggleTableRow cleanup
|
||||
|
||||
---
|
||||
|
||||
#### Stage 3: Pipeline Integration
|
||||
**File:** `STAGE_3_COMPLETE.md`
|
||||
**Completion:** November 26, 2025
|
||||
**Status:** ✅ 100% Complete (Core Features)
|
||||
|
||||
**Accomplished:**
|
||||
- Fixed Ideas → Tasks creation flow
|
||||
- Rewrote AI content generation function
|
||||
- Implemented publish/unpublish endpoints
|
||||
- Updated WordPress plugin for schema compatibility
|
||||
- Added conditional UI (publish buttons)
|
||||
- Fixed WordPress import flow
|
||||
|
||||
**Files Modified:** 13 files (5 backend + 5 frontend + 2 WP plugin + 3 docs)
|
||||
|
||||
---
|
||||
|
||||
#### Stage 4: DDAY Completion (PLANNED)
|
||||
**File:** `STAGE_4_PLAN.md`
|
||||
**Created:** November 26, 2025
|
||||
**Status:** 📋 Ready for Implementation
|
||||
|
||||
**Objectives:**
|
||||
- Close all 20 identified gaps
|
||||
- Implement batch operations
|
||||
- Clean up Sites module UI
|
||||
- Remove all deprecated field references
|
||||
- Achieve 100% spec compliance
|
||||
|
||||
**Estimated Effort:** 2-3 days
|
||||
**Files to Modify:** 17 files (6 backend + 10 frontend + 1 WP plugin + 4 docs)
|
||||
|
||||
---
|
||||
|
||||
### 4. **Supporting Documentation**
|
||||
|
||||
#### CHANGELOG.md
|
||||
**Purpose:** Version history and release notes
|
||||
**Status:** Updated through Stage 3
|
||||
**Next:** Stage 4 entry to be added upon completion
|
||||
|
||||
#### Progress & Summary Files
|
||||
- `STAGE_3_PROGRESS.md` — ❌ Removed (consolidated into STAGE_3_COMPLETE.md)
|
||||
- Various implementation audits and reports
|
||||
|
||||
---
|
||||
|
||||
## 🎯 CURRENT STATUS SUMMARY
|
||||
|
||||
### What's Complete (Stages 1-3)
|
||||
✅ **Backend Models:** 95% spec compliant
|
||||
✅ **Frontend Components:** 92% spec compliant
|
||||
✅ **WordPress Integration:** 100% functional
|
||||
✅ **Pipeline Flow:** 100% working (Keyword → Cluster → Idea → Task → Content → WordPress)
|
||||
✅ **State Machines:** 100% (queued/completed, draft/published)
|
||||
|
||||
### What Remains (Stage 4)
|
||||
📋 **Batch Operations:** Not implemented
|
||||
📋 **Sites UI Cleanup:** Builder buttons still present
|
||||
📋 **Content Manager Polish:** Missing unassigned filter, highlighting
|
||||
📋 **Deprecated Field Cleanup:** 12 remaining references
|
||||
📋 **PostEditor Refactor:** SEO/Metadata tabs need removal
|
||||
|
||||
---
|
||||
|
||||
## 📋 READING ORDER FOR NEW DEVELOPERS
|
||||
|
||||
### For Understanding the Vision:
|
||||
1. Read `original-specs of-refactor` (10 min)
|
||||
2. Review `REFACTOR_GAP_ANALYSIS.md` executive summary (5 min)
|
||||
|
||||
### For Understanding What Was Built:
|
||||
3. Read `STAGE_1_COMPLETE.md` — Backend changes (10 min)
|
||||
4. Read `STAGE_2_REFACTOR_COMPLETE.md` — Frontend changes (10 min)
|
||||
5. Read `STAGE_3_COMPLETE.md` — Pipeline integration (15 min)
|
||||
|
||||
### For Implementing Stage 4:
|
||||
6. Review `REFACTOR_GAP_ANALYSIS.md` — All gaps detailed (20 min)
|
||||
7. Study `STAGE_4_PLAN.md` — Implementation plan (30 min)
|
||||
8. Follow Phase 1-4 checklist in STAGE_4_PLAN.md
|
||||
|
||||
**Total Reading Time:** ~90 minutes to full context
|
||||
|
||||
---
|
||||
|
||||
## 🔍 QUICK REFERENCE
|
||||
|
||||
### Find Information About:
|
||||
|
||||
**"What fields were removed?"**
|
||||
→ `STAGE_1_COMPLETE.md` — Section 1: Model Simplification
|
||||
|
||||
**"What's the new Content model structure?"**
|
||||
→ `STAGE_3_COMPLETE.md` — Section: Schema Evolution
|
||||
|
||||
**"Why isn't batch assignment working?"**
|
||||
→ `REFACTOR_GAP_ANALYSIS.md` — GAP #2
|
||||
|
||||
**"How do I implement bulk cluster assignment?"**
|
||||
→ `STAGE_4_PLAN.md` — Phase 1.1 & 1.2
|
||||
|
||||
**"What Sites module changes are needed?"**
|
||||
→ `STAGE_4_PLAN.md` — Phase 1.3
|
||||
|
||||
**"Are there any WordPress plugin updates needed?"**
|
||||
→ `STAGE_3_COMPLETE.md` — Section 10 (already done!)
|
||||
→ `STAGE_4_PLAN.md` — Phase 4.2 (verify cluster_id)
|
||||
|
||||
**"What's the complete E2E flow?"**
|
||||
→ `original-specs of-refactor` — Section 1: End to End Flow
|
||||
→ `STAGE_4_PLAN.md` — Phase 4.1: Testing scenarios
|
||||
|
||||
---
|
||||
|
||||
## 📊 METRICS & PROGRESS
|
||||
|
||||
### Compliance Tracking
|
||||
|
||||
| Metric | Stage 1 | Stage 2 | Stage 3 | Stage 4 Target |
|
||||
|--------|---------|---------|---------|----------------|
|
||||
| Backend Models | 95% | — | — | 100% |
|
||||
| Backend Services | — | — | 100% | 100% |
|
||||
| Frontend Components | — | 92% | — | 100% |
|
||||
| WordPress Integration | — | — | 100% | 100% |
|
||||
| Content Manager Features | — | 70% | 75% | 100% |
|
||||
| Sites Module | — | 60% | — | 100% |
|
||||
| **OVERALL COMPLIANCE** | **95%** | **92%** | **95%** | **100%** |
|
||||
|
||||
### Files Modified Across All Stages
|
||||
|
||||
| Stage | Backend | Frontend | WP Plugin | Docs | Total |
|
||||
|-------|---------|----------|-----------|------|-------|
|
||||
| 1 | 8 | 0 | 0 | 2 | 10 |
|
||||
| 2 | 0 | 25 | 0 | 2 | 27 |
|
||||
| 3 | 5 | 5 | 2 | 3 | 15 |
|
||||
| 4 (planned) | 6 | 10 | 1 | 4 | 21 |
|
||||
| **TOTAL** | **19** | **40** | **3** | **11** | **73** |
|
||||
|
||||
### Code Cleanup Progress
|
||||
|
||||
| Deprecated Field | Stage 1 | Stage 2 | Stage 3 | Stage 4 |
|
||||
|------------------|---------|---------|---------|---------|
|
||||
| `cluster_role` | ✅ Model removed | ⚠️ 3 refs | ⚠️ 3 refs | ✅ 0 refs |
|
||||
| `sync_status` (Content) | ✅ Model removed | ✅ Removed | ✅ Removed | ✅ 0 refs |
|
||||
| `sync_status` (Taxonomy) | ⚠️ In filter | ⚠️ In filter | ⚠️ In filter | ✅ Removed |
|
||||
| `entity_type` | ✅ Model removed | ⚠️ 5 refs | ✅ Removed | ✅ 0 refs |
|
||||
| `context_type` | ✅ Model removed | ✅ Removed | ✅ Removed | ✅ 0 refs |
|
||||
| `meta_title` (Content) | ✅ Model removed | ⚠️ 4 refs | ⚠️ 2 refs | ✅ 0 refs |
|
||||
| `primary_keyword` | ✅ Model removed | ⚠️ 3 refs | ✅ Removed | ✅ 0 refs |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 NEXT ACTIONS
|
||||
|
||||
### For Project Managers:
|
||||
1. Review `REFACTOR_GAP_ANALYSIS.md` to understand remaining work
|
||||
2. Prioritize Stage 4 gaps (critical batch operations first)
|
||||
3. Allocate 2-3 days for Stage 4 completion
|
||||
|
||||
### For Developers:
|
||||
1. Read all stage completion docs to understand what's been built
|
||||
2. Review `STAGE_4_PLAN.md` implementation phases
|
||||
3. Start with Phase 1 (critical features)
|
||||
4. Follow file checklist systematically
|
||||
|
||||
### For QA/Testing:
|
||||
1. Review E2E testing scenarios in `STAGE_4_PLAN.md` — Phase 4.1
|
||||
2. Prepare test data (keywords, clusters, WordPress site)
|
||||
3. Verify all 4 test scenarios pass after Stage 4 completion
|
||||
|
||||
---
|
||||
|
||||
## ✅ COMPLETION CRITERIA
|
||||
|
||||
### Stage 4 is complete when:
|
||||
- [ ] All 20 gaps from REFACTOR_GAP_ANALYSIS.md resolved
|
||||
- [ ] Batch cluster assignment works (1-100 items)
|
||||
- [ ] Batch taxonomy assignment works (1-100 items)
|
||||
- [ ] Sites module shows only 3 buttons per card
|
||||
- [ ] PostEditor has only 2 tabs
|
||||
- [ ] 0 deprecated field references in active code
|
||||
- [ ] All E2E test scenarios pass
|
||||
- [ ] `STAGE_4_COMPLETE.md` created and reviewed
|
||||
- [ ] `CHANGELOG.md` updated with Stage 4 entry
|
||||
- [ ] Build passes with 0 TypeScript errors
|
||||
|
||||
### DDAY Refactor is 100% complete when:
|
||||
- [ ] Stage 4 completion criteria met
|
||||
- [ ] `original-specs of-refactor` marked as fully implemented
|
||||
- [ ] All documentation reviewed and finalized
|
||||
- [ ] Production deployment successful
|
||||
- [ ] User documentation updated
|
||||
|
||||
---
|
||||
|
||||
## 📞 DOCUMENT MAINTENANCE
|
||||
|
||||
### How to Update This Index:
|
||||
1. When creating new documentation, add entry to relevant section
|
||||
2. Update metrics tables after each stage completion
|
||||
3. Mark stages as complete when all criteria met
|
||||
4. Archive outdated progress tracking docs
|
||||
|
||||
### Document Owners:
|
||||
- **Original Specs:** Product/Architecture team (reference only)
|
||||
- **Gap Analysis:** Implementation team (update when gaps discovered)
|
||||
- **Stage Plans:** Development lead (create before each stage)
|
||||
- **Stage Complete:** Development team (create after verification)
|
||||
- **CHANGELOG:** Development lead (update after each release)
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** November 26, 2025
|
||||
**Next Review:** Upon Stage 4 completion
|
||||
**Status:** All documentation current and ready for Stage 4 implementation
|
||||
@@ -1,437 +0,0 @@
|
||||
# REFACTOR GAP ANALYSIS — Original Specs vs Current Implementation
|
||||
|
||||
**Analysis Date:** November 26, 2025
|
||||
**Analyst:** AI Agent (Claude Sonnet 4.5)
|
||||
**Scope:** Complete comparison of original DDAY refactor specifications against Stages 1-3 completed work
|
||||
|
||||
---
|
||||
|
||||
## 📋 EXECUTIVE SUMMARY
|
||||
|
||||
### Original Vision (DDAY Refactor Specs)
|
||||
Complete end-to-end SEO content pipeline:
|
||||
- **Input:** Keywords → Clusters → Ideas → Tasks → Content → Publish → WordPress
|
||||
- **Goal:** Single source of truth in Content Manager
|
||||
- **Scope:** Clean data models, bidirectional sync, simplified UX
|
||||
|
||||
### Current Status After Stages 1-3
|
||||
- ✅ **Stage 1:** Backend models refactored (95% spec compliance)
|
||||
- ✅ **Stage 2:** Frontend updated (92% spec compliance)
|
||||
- ✅ **Stage 3:** Pipeline integration (100% core features)
|
||||
- ⚠️ **Remaining Gaps:** 12 critical items, 18 minor items
|
||||
|
||||
---
|
||||
|
||||
## 🎯 COMPLIANCE MATRIX
|
||||
|
||||
| Original Spec Component | Stage 1 | Stage 2 | Stage 3 | Compliance | Gap Priority |
|
||||
|------------------------|---------|---------|---------|------------|--------------|
|
||||
| **1. Backend Models** | ✅ | N/A | N/A | 95% | LOW |
|
||||
| 1.1 Cluster Model | ✅ | N/A | N/A | 100% | ✅ NONE |
|
||||
| 1.2 Task Model | ⚠️ | N/A | N/A | 90% | MEDIUM |
|
||||
| 1.3 Content Model | ⚠️ | N/A | ⚠️ | 95% | LOW |
|
||||
| 1.4 ContentTaxonomy Model | ⚠️ | N/A | N/A | 85% | MEDIUM |
|
||||
| **2. WordPress Integration** | N/A | N/A | ✅ | 90% | MEDIUM |
|
||||
| 2.1 WP → IGNY8 Import | N/A | N/A | ✅ | 100% | ✅ NONE |
|
||||
| 2.2 IGNY8 → WP Publish | N/A | N/A | ✅ | 100% | ✅ NONE |
|
||||
| 2.3 WP Plugin Updates | N/A | N/A | ✅ | 100% | ✅ NONE |
|
||||
| **3. Content Manager** | N/A | ⚠️ | ✅ | 75% | **HIGH** |
|
||||
| 3.1 Single Source of Truth | N/A | ⚠️ | ⚠️ | 70% | **HIGH** |
|
||||
| 3.2 Batch Operations | N/A | ❌ | ❌ | 0% | **CRITICAL** |
|
||||
| 3.3 Cluster Assignment | N/A | ⚠️ | ⚠️ | 60% | **HIGH** |
|
||||
| **4. Planner Module** | N/A | ✅ | ✅ | 95% | LOW |
|
||||
| 4.1 Keyword Input | N/A | ✅ | ✅ | 100% | ✅ NONE |
|
||||
| 4.2 Cluster Generation | N/A | ✅ | ✅ | 100% | ✅ NONE |
|
||||
| 4.3 Idea → Task Creation | N/A | ✅ | ✅ | 100% | ✅ NONE |
|
||||
| **5. Writer Module** | N/A | ✅ | ✅ | 90% | MEDIUM |
|
||||
| 5.1 Task Listing | N/A | ✅ | ✅ | 100% | ✅ NONE |
|
||||
| 5.2 Content Generation | N/A | N/A | ✅ | 100% | ✅ NONE |
|
||||
| 5.3 Draft Viewing | N/A | ⚠️ | ⚠️ | 80% | MEDIUM |
|
||||
| **6. Sites Module** | N/A | ⚠️ | N/A | 60% | **HIGH** |
|
||||
| 6.1 Site Cards UI | N/A | ⚠️ | N/A | 80% | MEDIUM |
|
||||
| 6.2 Removed Builder Buttons | N/A | ❌ | ❌ | 0% | **HIGH** |
|
||||
| 6.3 Sectors in Settings | N/A | ❌ | ❌ | 0% | **HIGH** |
|
||||
| **7. Cluster Detail Page** | N/A | ✅ | ✅ | 100% | ✅ NONE |
|
||||
| **8. State Machines** | ✅ | ✅ | ✅ | 100% | ✅ NONE |
|
||||
| 8.1 Task Status (queued/completed) | ✅ | ✅ | ✅ | 100% | ✅ NONE |
|
||||
| 8.2 Content Status (draft/published) | ✅ | ✅ | ✅ | 100% | ✅ NONE |
|
||||
|
||||
**Overall Compliance:** 82% (33/40 major components complete)
|
||||
|
||||
---
|
||||
|
||||
## 🔴 CRITICAL GAPS (Blocking DDAY Refactor Completion)
|
||||
|
||||
### GAP #1: ContentTaxonomy.sync_status Still Exists
|
||||
**Spec Says:** Remove `sync_status` from ContentTaxonomy
|
||||
**Current State:** Field still exists in model and ViewSet filterset_fields
|
||||
**Impact:** HIGH - Violates spec requirement, creates confusion
|
||||
**Location:**
|
||||
```python
|
||||
# backend/igny8_core/modules/writer/views.py:1503
|
||||
filterset_fields = ['taxonomy_type', 'sync_status', 'parent', 'external_id', 'external_taxonomy']
|
||||
```
|
||||
**Root Cause:** Migration removed it but ViewSet filter wasn't updated
|
||||
**Fix Required:** Remove `sync_status` from `ContentTaxonomyViewSet.filterset_fields`
|
||||
|
||||
---
|
||||
|
||||
### GAP #2: Content Manager — No Batch Cluster Assignment
|
||||
**Spec Says:**
|
||||
```
|
||||
Content Manager must allow:
|
||||
- batch cluster assignment: select multiple rows, assign one cluster
|
||||
- batch taxonomy assignment: select rows, attach a taxonomy term
|
||||
```
|
||||
**Current State:** No batch operations implemented
|
||||
**Impact:** **CRITICAL** - Core spec requirement missing
|
||||
**Location:** Frontend Content Manager pages
|
||||
**Fix Required:**
|
||||
1. Add row selection checkboxes to Content table
|
||||
2. Add "Bulk Actions" dropdown with "Assign Cluster" and "Assign Taxonomies"
|
||||
3. Create modal for bulk cluster/taxonomy selection
|
||||
4. Backend endpoint: `PATCH /api/v1/writer/content/bulk-update/`
|
||||
|
||||
---
|
||||
|
||||
### GAP #3: Content Manager — Manual Cluster Assignment for Imported Content
|
||||
**Spec Says:**
|
||||
```
|
||||
Imported content from WordPress:
|
||||
- cluster must be manually assigned in Content Manager or via auto mapping later
|
||||
```
|
||||
**Current State:** UI exists but workflow unclear, no "unassigned" filter
|
||||
**Impact:** HIGH - Critical for imported content workflow
|
||||
**Location:** Content Manager listing and editor
|
||||
**Fix Required:**
|
||||
1. Add filter: `cluster: null` to show unassigned content
|
||||
2. Highlight rows with `cluster=null` in red/warning state
|
||||
3. Simplify cluster assignment in editor (dropdown with search)
|
||||
|
||||
---
|
||||
|
||||
### GAP #4: Sites Module — Builder Buttons Not Removed
|
||||
**Spec Says:**
|
||||
```
|
||||
Remove:
|
||||
- Pages button and its backend logic
|
||||
- Sectors button from card (moved into Site Settings tab)
|
||||
- Site builder and blueprints buttons (out of scope)
|
||||
```
|
||||
**Current State:** Sites module partially updated in Stage 2 but builder-related UI remains
|
||||
**Impact:** HIGH - Confuses users, violates spec
|
||||
**Location:** `frontend/src/pages/Sites/List.tsx`, `frontend/src/components/sites/*`
|
||||
**Fix Required:**
|
||||
1. Remove "Builder", "Blueprints", "Pages" buttons from site cards
|
||||
2. Move "Sectors" management into Site Settings page
|
||||
3. Clean up related routes and components
|
||||
|
||||
---
|
||||
|
||||
### GAP #5: Sites Module — Active/Inactive Toggle Location
|
||||
**Spec Says:**
|
||||
```
|
||||
Active/inactive toggle at the top
|
||||
```
|
||||
**Current State:** Toggle may be in wrong location or missing
|
||||
**Impact:** MEDIUM - UX issue
|
||||
**Location:** Sites grid view
|
||||
**Fix Required:** Add site-level active/inactive toggle above grid
|
||||
|
||||
---
|
||||
|
||||
### GAP #6: Deprecated Fields in Backend Management Commands
|
||||
**Spec Says:** Remove all `cluster_role`, `sync_status`, `context_type`, `dimension_meta` references
|
||||
**Current State:** Still referenced in audit command
|
||||
**Impact:** LOW - Non-critical but violates cleanup requirement
|
||||
**Location:**
|
||||
```python
|
||||
# backend/igny8_core/modules/writer/management/commands/audit_site_metadata.py:59
|
||||
tasks_with_cluster_role = tasks.filter(cluster_role__isnull=False).count()
|
||||
```
|
||||
**Fix Required:** Remove or update audit command logic
|
||||
|
||||
---
|
||||
|
||||
### GAP #7: Frontend — PostEditor SEO/Metadata Tabs
|
||||
**Spec Says:** Content Manager should allow editing title, content_html, cluster, taxonomy_terms
|
||||
**Current State:** PostEditor has deprecated SEO/Metadata tabs that reference removed fields
|
||||
**Impact:** MEDIUM - UI sections broken
|
||||
**Location:** `frontend/src/pages/Sites/PostEditor.tsx` (lines 450-600)
|
||||
**Fix Required:**
|
||||
1. Remove SEO tab (meta_title, meta_description, primary_keyword, secondary_keywords)
|
||||
2. Remove Metadata tab (tags, categories - replaced by taxonomy_terms)
|
||||
3. Keep only: Content, Taxonomy & Cluster tabs
|
||||
|
||||
---
|
||||
|
||||
### GAP #8: Frontend — ToggleTableRow Deprecated Field Fallbacks
|
||||
**Spec Says:** Use only new schema fields
|
||||
**Current State:** Extensive fallback logic for removed fields
|
||||
**Impact:** LOW - Works but needs cleanup
|
||||
**Location:** `frontend/src/components/common/ToggleTableRow.tsx`
|
||||
**Fix Required:** Remove fallbacks for `primary_keyword`, `meta_description`, `tags`, `categories`
|
||||
|
||||
---
|
||||
|
||||
### GAP #9: ContentTaxonomy.parent Field
|
||||
**Spec Says:** Remove `parent` field
|
||||
**Current State:** Migration marked as removed but still in ViewSet filters
|
||||
**Impact:** MEDIUM - Creates confusion
|
||||
**Location:**
|
||||
```python
|
||||
# backend/igny8_core/modules/writer/views.py:1503
|
||||
filterset_fields = ['taxonomy_type', 'sync_status', 'parent', ...]
|
||||
```
|
||||
**Fix Required:** Remove `parent` from filterset_fields
|
||||
|
||||
---
|
||||
|
||||
### GAP #10: Cluster Detail Page — Content Type Tabs Incomplete
|
||||
**Spec Says:**
|
||||
```
|
||||
Show tabs or sections per content_type:
|
||||
- Articles
|
||||
- Pages
|
||||
- Products
|
||||
- Taxonomy Pages
|
||||
```
|
||||
**Current State:** Tabs exist but may not include "Taxonomy" type
|
||||
**Impact:** LOW - Minor omission
|
||||
**Location:** `frontend/src/pages/Planner/ClusterDetail.tsx`
|
||||
**Fix Required:** Verify all 4 content_type tabs present
|
||||
|
||||
---
|
||||
|
||||
### GAP #11: No Content Validation Before Publish
|
||||
**Spec Says:**
|
||||
```
|
||||
Load Content:
|
||||
ensure status = draft
|
||||
ensure site record configured with WP endpoint and auth
|
||||
```
|
||||
**Current State:** Backend publish service exists but validation unclear
|
||||
**Impact:** MEDIUM - Could allow invalid publish operations
|
||||
**Location:** `backend/igny8_core/modules/writer/views.py` (publish endpoint)
|
||||
**Fix Required:** Add validation checks before publish
|
||||
|
||||
---
|
||||
|
||||
### GAP #12: WordPress Plugin — No Metadata Storage Confirmation
|
||||
**Spec Says:**
|
||||
```
|
||||
Plugin may optionally store meta keys like _igny8_content_id or _igny8_cluster_id, but this is optional.
|
||||
```
|
||||
**Current State:** Stage 3 added `_igny8_content_id` but cluster_id not confirmed
|
||||
**Impact:** LOW - Nice to have
|
||||
**Location:** WordPress plugin meta field storage
|
||||
**Fix Required:** Verify `_igny8_cluster_id` is saved in WP post meta
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ MEDIUM PRIORITY GAPS
|
||||
|
||||
### GAP #13: Writer Module — Link to Content Manager
|
||||
**Spec Says:**
|
||||
```
|
||||
Link to open draft in Content Manager (optional but helpful)
|
||||
```
|
||||
**Current State:** Not implemented
|
||||
**Impact:** MEDIUM - UX improvement
|
||||
**Fix Required:** Add "View in Content Manager" button on Writer task rows
|
||||
|
||||
---
|
||||
|
||||
### GAP #14: Content Manager — Missing Filters
|
||||
**Spec Says:**
|
||||
```
|
||||
Filters:
|
||||
- content_type
|
||||
- status
|
||||
- cluster
|
||||
- taxonomy_type (optional)
|
||||
- source
|
||||
```
|
||||
**Current State:** Basic filters exist, taxonomy_type filter unclear
|
||||
**Impact:** LOW - Optional filter
|
||||
**Fix Required:** Add taxonomy_type filter to Content Manager
|
||||
|
||||
---
|
||||
|
||||
### GAP #15: Frontend Test Files — Deprecated Field References
|
||||
**Spec Says:** Remove all deprecated field references
|
||||
**Current State:** Test files still use `sync_status`, `entity_type`, `cluster_role`
|
||||
**Impact:** LOW - Tests may fail or be misleading
|
||||
**Location:**
|
||||
```typescript
|
||||
// frontend/src/pages/Optimizer/__tests__/ContentSelector.test.tsx
|
||||
{ id: 1, title: 'Test Content', source: 'igny8', sync_status: 'native', ... }
|
||||
```
|
||||
**Fix Required:** Update test mocks to use new schema
|
||||
|
||||
---
|
||||
|
||||
### GAP #16: DeploymentPanel — sync_status References
|
||||
**Spec Says:** sync_status only for Integration model, not Content
|
||||
**Current State:** DeploymentPanel shows `readiness.checks.sync_status`
|
||||
**Impact:** LOW - Deployment feature out of DDAY scope
|
||||
**Location:** `frontend/src/pages/Sites/DeploymentPanel.tsx`
|
||||
**Fix Required:** Verify this refers to Integration.sync_status not Content.sync_status
|
||||
|
||||
---
|
||||
|
||||
### GAP #17: Integration Model — Correct Usage
|
||||
**Spec Says:** sync_status removed from Content/ContentTaxonomy
|
||||
**Current State:** Integration model correctly has sync_status
|
||||
**Impact:** ✅ NONE - This is correct usage
|
||||
**Location:** `backend/igny8_core/business/integration/models.py`
|
||||
**Verification:** ✅ Integration.sync_status is valid and should remain
|
||||
|
||||
---
|
||||
|
||||
### GAP #18: Frontend — Optimizer Module Refactoring
|
||||
**Spec Says:** Out of DDAY scope but should align with new schema
|
||||
**Current State:** Stage 2 partially updated, still shows deprecated badges
|
||||
**Impact:** LOW - Module out of immediate scope
|
||||
**Fix Required:** Full Optimizer module refactor (defer to post-DDAY)
|
||||
|
||||
---
|
||||
|
||||
## ✅ CORRECTLY IMPLEMENTED (No Gaps)
|
||||
|
||||
### 1. Backend Models (Stage 1)
|
||||
- ✅ Cluster: `context_type`, `dimension_meta` removed
|
||||
- ✅ Task: `cluster_role`, `entity_type`, `idea`, `taxonomy`, `keywords` CharField removed
|
||||
- ✅ Task: Added `content_type`, `content_structure`, `taxonomy_term`, `keywords` M2M
|
||||
- ✅ Content: Removed 25+ deprecated fields
|
||||
- ✅ Content: Added `title`, `content_html`, `content_type`, `content_structure`, `taxonomy_terms` M2M
|
||||
- ✅ Content: Removed OneToOne relationship with Task
|
||||
- ✅ ContentTaxonomy: Removed `description`, `parent`, `count`, `metadata`, `clusters` (mostly)
|
||||
|
||||
### 2. State Machines (Stages 1-3)
|
||||
- ✅ Task: `queued` → `completed` only
|
||||
- ✅ Content: `draft` → `published` only
|
||||
- ✅ No sync tracking in Planner or Writer
|
||||
|
||||
### 3. WordPress Integration (Stage 3)
|
||||
- ✅ WP → IGNY8 import creates Content with `source=wordpress`
|
||||
- ✅ Content.external_id = WP post_id
|
||||
- ✅ Content.external_url = WP permalink
|
||||
- ✅ Taxonomy mapping via ContentTaxonomy.external_id
|
||||
- ✅ IGNY8 → WP publish updates external_id/url
|
||||
- ✅ WordPress plugin updated for content_html schema
|
||||
|
||||
### 4. Planner Module (Stages 2-3)
|
||||
- ✅ Keyword input functional
|
||||
- ✅ Cluster generation working
|
||||
- ✅ Idea → Task creation with correct field mapping
|
||||
|
||||
### 5. Writer Module (Stages 2-3)
|
||||
- ✅ Task listing with queued/completed status
|
||||
- ✅ Content generation creates independent Content records
|
||||
- ✅ Task status updates to completed after content creation
|
||||
|
||||
### 6. Cluster Detail Page (Stages 2-3)
|
||||
- ✅ Page created with cluster name and description
|
||||
- ✅ Tabs for content_type filtering
|
||||
- ✅ Proper data fetching
|
||||
|
||||
### 7. Backend Publishing Service (Stage 3)
|
||||
- ✅ Publish endpoint prevents duplicate publishing
|
||||
- ✅ Sets external_id, external_url on success
|
||||
- ✅ Updates Content.status to published
|
||||
- ✅ Does not touch Task (correct behavior)
|
||||
|
||||
---
|
||||
|
||||
## 📊 GAP SUMMARY BY CATEGORY
|
||||
|
||||
| Category | Critical | High | Medium | Low | Total Gaps |
|
||||
|----------|----------|------|--------|-----|------------|
|
||||
| Backend Models | 1 | 0 | 2 | 1 | 4 |
|
||||
| Backend Services | 0 | 0 | 1 | 0 | 1 |
|
||||
| Frontend Content Manager | 1 | 2 | 1 | 2 | 6 |
|
||||
| Frontend Sites Module | 0 | 3 | 1 | 0 | 4 |
|
||||
| Frontend Components | 0 | 1 | 0 | 2 | 3 |
|
||||
| WordPress Plugin | 0 | 0 | 0 | 1 | 1 |
|
||||
| Tests/Docs | 0 | 0 | 0 | 1 | 1 |
|
||||
| **TOTAL** | **2** | **6** | **5** | **7** | **20** |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 WHAT STAGE 4 MUST ACCOMPLISH
|
||||
|
||||
### Critical (Blocking Completion)
|
||||
1. ✅ Implement batch cluster assignment in Content Manager
|
||||
2. ✅ Implement batch taxonomy assignment in Content Manager
|
||||
|
||||
### High Priority (Core Spec Requirements)
|
||||
3. ✅ Remove builder/blueprints/pages buttons from Sites module
|
||||
4. ✅ Move Sectors management into Site Settings
|
||||
5. ✅ Add cluster=null filter and warning highlights for imported content
|
||||
6. ✅ Remove sync_status and parent from ContentTaxonomy filters
|
||||
|
||||
### Medium Priority (Polish & Cleanup)
|
||||
7. ✅ Refactor PostEditor to remove SEO/Metadata tabs
|
||||
8. ✅ Add publish validation checks (status=draft, site configured)
|
||||
9. ✅ Add "View in Content Manager" link from Writer tasks
|
||||
10. ✅ Clean up ToggleTableRow deprecated field fallbacks
|
||||
|
||||
### Low Priority (Cleanup & Tests)
|
||||
11. ✅ Remove cluster_role references from audit commands
|
||||
12. ✅ Update frontend test mocks to new schema
|
||||
13. ✅ Verify cluster_id saved in WordPress post meta
|
||||
14. ✅ Verify all 4 content_type tabs in Cluster Detail
|
||||
|
||||
---
|
||||
|
||||
## 📁 FILES REQUIRING UPDATES (Stage 4)
|
||||
|
||||
### Backend (6 files)
|
||||
1. `backend/igny8_core/modules/writer/views.py` - Remove sync_status/parent from filters, add bulk-update endpoint
|
||||
2. `backend/igny8_core/modules/writer/serializers.py` - Add BulkUpdateContentSerializer
|
||||
3. `backend/igny8_core/modules/writer/management/commands/audit_site_metadata.py` - Remove cluster_role references
|
||||
4. `backend/igny8_core/modules/writer/admin.py` - Verify filters clean
|
||||
5. `backend/igny8_core/modules/writer/urls.py` - Add bulk-update route
|
||||
6. `backend/igny8_core/business/publishing/services/adapters/wordpress_adapter.py` - Add validation
|
||||
|
||||
### Frontend (10 files)
|
||||
1. `frontend/src/pages/Writer/Content.tsx` - Add bulk operations UI
|
||||
2. `frontend/src/pages/Sites/Content.tsx` - Add bulk operations UI
|
||||
3. `frontend/src/config/pages/content.config.tsx` - Add bulk action column config
|
||||
4. `frontend/src/pages/Sites/List.tsx` - Remove builder buttons, add active toggle
|
||||
5. `frontend/src/pages/Sites/Settings.tsx` - Add Sectors management tab
|
||||
6. `frontend/src/pages/Sites/PostEditor.tsx` - Remove SEO/Metadata tabs
|
||||
7. `frontend/src/components/common/ToggleTableRow.tsx` - Remove deprecated fallbacks
|
||||
8. `frontend/src/pages/Writer/Tasks.tsx` - Add "View in Content Manager" link
|
||||
9. `frontend/src/pages/Planner/ClusterDetail.tsx` - Verify Taxonomy tab
|
||||
10. `frontend/src/pages/Optimizer/__tests__/ContentSelector.test.tsx` - Update test mocks
|
||||
|
||||
### WordPress Plugin (1 file)
|
||||
1. `sync/igny8-to-wp.php` - Verify `_igny8_cluster_id` meta field storage
|
||||
|
||||
---
|
||||
|
||||
## 💡 RECOMMENDATIONS
|
||||
|
||||
### Immediate Actions (Stage 4 Phase 1)
|
||||
- Implement batch operations (highest user value)
|
||||
- Clean up Sites module UI (high visibility)
|
||||
- Remove deprecated field filters (technical debt)
|
||||
|
||||
### Follow-up Actions (Stage 4 Phase 2)
|
||||
- Refactor PostEditor tabs (lower urgency)
|
||||
- Update test files (maintenance)
|
||||
- Polish Content Manager UX
|
||||
|
||||
### Post-Stage 4 (Future)
|
||||
- Full Optimizer module refactor
|
||||
- Linker module alignment
|
||||
- Advanced analytics for Content Manager
|
||||
|
||||
---
|
||||
|
||||
**Analysis Complete:** November 26, 2025
|
||||
**Total Gaps Identified:** 20
|
||||
**Estimated Stage 4 Effort:** 2-3 days (critical path: batch operations)
|
||||
**Stage 4 Completion Target:** 100% DDAY refactor spec compliance
|
||||
@@ -1,225 +0,0 @@
|
||||
# Sites List Refactor - Complete Summary (Updated)
|
||||
|
||||
## Overview
|
||||
Successfully completed comprehensive refactor of the Sites List page to remove all builder/blueprint functionality and improve user experience with better defaults and simplified UI.
|
||||
|
||||
## Latest Updates (November 26, 2025)
|
||||
|
||||
### ✅ Additional UI/UX Improvements
|
||||
|
||||
#### 1. Replaced "Show Welcome Guide" Button
|
||||
- **Before:** Outline button with chevron icons saying "Show/Hide Welcome Guide"
|
||||
- **After:** Success variant, medium-sized button with + icon saying "Add Site"
|
||||
- **Impact:** More prominent call-to-action, clearer purpose
|
||||
|
||||
#### 2. Removed FormModal and Old Add Site Button
|
||||
- **Removed Components:**
|
||||
- FormModal component and import
|
||||
- Old "Add Site" button from right side
|
||||
- `handleCreateSite()`, `handleEdit()`, `handleSaveSite()` functions
|
||||
- `getSiteFormFields()` function
|
||||
- `showSiteModal`, `isSaving`, `formData` state variables
|
||||
- `onEdit` prop from TablePageTemplate in table view
|
||||
- **Impact:** Simplified codebase, single consistent site creation flow via WorkflowGuide
|
||||
|
||||
#### 3. Standard Filter Bar for Grid View
|
||||
- **Before:** Custom filter inputs with Card wrapper
|
||||
- **After:** Styled filter bar matching table view design
|
||||
- **Features:**
|
||||
- Responsive flex layout with proper wrapping
|
||||
- Consistent styling with gray background
|
||||
- Proper focus states and dark mode support
|
||||
- Clear button appears only when filters are active
|
||||
- Labels above each filter input
|
||||
- **Impact:** Consistent UX between grid and table views
|
||||
|
||||
#### 4. Removed Sectors Label from Site Cards
|
||||
- **Removed:** `{site.active_sectors_count || 0} / 5 Sectors` badge
|
||||
- **Kept:** Site type badge, industry badge, integrations badge
|
||||
- **Impact:** Cleaner card design, focuses on essential information
|
||||
|
||||
#### 5. Reduced Card Padding
|
||||
- **Card Body Padding:** Changed from `p-5 pb-9` to `p-4 pb-6`
|
||||
- **Card Actions Padding:** Changed from `p-5` to `p-3`
|
||||
- **Toggle/Badge Position:** Changed from `top-5 right-5` to `top-4 right-4`
|
||||
- **Impact:** More compact cards, better space utilization, fits more cards on screen
|
||||
|
||||
## Complete Changes Summary
|
||||
|
||||
### ✅ 1. Removed Builder Routes and Pages
|
||||
- **File:** `frontend/src/App.tsx`
|
||||
- **Action:** Removed `/sites/builder` route and `SiteEditor` component import
|
||||
- **Impact:** Builder page no longer accessible from routing
|
||||
|
||||
### ✅ 2. Removed Blueprints Routes and Pages
|
||||
- **File:** `frontend/src/App.tsx`
|
||||
- **Action:** Removed `/sites/blueprints` route
|
||||
- **Impact:** Blueprint functionality completely removed from application
|
||||
|
||||
### ✅ 3. Removed Create Site and Blueprints Buttons from Menu
|
||||
- **File:** `frontend/src/pages/Sites/List.tsx`
|
||||
- **Action:** Navigation tabs reduced from 3 tabs to 1 tab ("All Sites")
|
||||
- **Before:** `["All Sites", "Create Site", "Blueprints"]`
|
||||
- **After:** `["All Sites"]`
|
||||
- **Impact:** Simplified navigation, removed unused menu items
|
||||
|
||||
### ✅ 4. Removed "Create with Builder" Button
|
||||
- **File:** `frontend/src/pages/Sites/List.tsx`
|
||||
- **Action:** Removed "Create with Builder" button from header actions
|
||||
- **Impact:** Only "Add Site" button remains for creating sites
|
||||
|
||||
### ✅ 5. Integrated Welcome Screen Site Creation
|
||||
- **Files Modified:**
|
||||
- `frontend/src/pages/Sites/List.tsx`
|
||||
- **Imports Added:**
|
||||
- `WorkflowGuide` component from `components/onboarding/WorkflowGuide`
|
||||
- `ChevronDownIcon`, `ChevronUpIcon` icons
|
||||
- **State Added:**
|
||||
- `showWelcomeGuide` state for toggling guide visibility
|
||||
- **UI Changes:**
|
||||
- Added "Show/Hide Welcome Guide" button in header (left side)
|
||||
- Welcome guide integrates site creation with industry and sector selection
|
||||
- Auto-closes after site is created
|
||||
- Reloads sites list after creation
|
||||
- **Impact:** Users can now create sites with full industry/sector configuration directly from Sites List page
|
||||
|
||||
### ✅ 6. Changed Default View to Grid
|
||||
- **File:** `frontend/src/pages/Sites/List.tsx`
|
||||
- **Action:** Changed `viewType` default state from `'table'` to `'grid'`
|
||||
- **Line:** `const [viewType, setViewType] = useState<ViewType>('grid');`
|
||||
- **Impact:** Users see grid view by default (better visual representation of sites)
|
||||
|
||||
### ✅ 7. Updated Filters Bar for Grid View
|
||||
- **File:** `frontend/src/pages/Sites/List.tsx`
|
||||
- **Action:** Filter bar already properly styled for grid view with responsive columns
|
||||
- **Features:**
|
||||
- 5-column grid layout on large screens
|
||||
- 2-column on medium screens
|
||||
- Single column on mobile
|
||||
- Clear Filters button when active filters present
|
||||
- Results count display
|
||||
- **Impact:** Better filter UX in grid view
|
||||
|
||||
### ✅ 8. Removed Site Configuration Notification
|
||||
- **File:** `frontend/src/pages/Sites/List.tsx`
|
||||
- **Action:** Removed Alert component displaying "Sites Configuration" message
|
||||
- **Impact:** Cleaner UI without unnecessary notifications
|
||||
|
||||
### ✅ 9. Removed Pages Button from Site Cards
|
||||
- **File:** `frontend/src/pages/Sites/List.tsx`
|
||||
- **Before:** 3-column button grid with Dashboard, Content, Pages + Settings row with toggle
|
||||
- **After:** 2-column button grid with Dashboard, Content, and full-width Settings button
|
||||
- **Removed:**
|
||||
- Pages button (line 590-596)
|
||||
- `PageIcon` usage in grid cards
|
||||
- **Layout:**
|
||||
```tsx
|
||||
Dashboard | Content
|
||||
----Settings----
|
||||
```
|
||||
- **Impact:** Simplified card actions, removed builder-related navigation
|
||||
|
||||
### ✅ 10. Moved Toggle Switch to Top Right
|
||||
- **File:** `frontend/src/pages/Sites/List.tsx`
|
||||
- **Before:** Toggle switch at bottom of card in flex container with Settings button
|
||||
- **After:** Toggle switch at top right of card, positioned absolutely next to status badge
|
||||
- **Position:**
|
||||
- `absolute top-5 right-5`
|
||||
- Grouped with status badge in flex container
|
||||
- Switch appears before badge
|
||||
- **Impact:** Better visual hierarchy, status controls at top where status badge is located
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Files Modified
|
||||
1. `/data/app/igny8/frontend/src/App.tsx` - Route cleanup
|
||||
2. `/data/app/igny8/frontend/src/pages/Sites/List.tsx` - Main refactor file
|
||||
|
||||
### Components Removed
|
||||
- SiteEditor component (lazy import)
|
||||
|
||||
### Routes Removed
|
||||
- `/sites/:id/editor` (builder route)
|
||||
- `/sites/blueprints` (blueprints route)
|
||||
|
||||
### New Dependencies Added
|
||||
- `WorkflowGuide` component integration
|
||||
- `ChevronDownIcon`, `ChevronUpIcon` icons
|
||||
|
||||
### State Changes
|
||||
- Added `showWelcomeGuide` boolean state
|
||||
- Changed `viewType` default from `'table'` to `'grid'`
|
||||
|
||||
### UI Improvements
|
||||
1. **Simplified Navigation:** Only "All Sites" tab remains
|
||||
2. **Better Defaults:** Grid view as default provides better visual overview
|
||||
3. **Welcome Guide Integration:** Full site creation workflow with industry/sectors
|
||||
4. **Cleaner Cards:**
|
||||
- Removed Pages button
|
||||
- Moved toggle to top-right with status badge
|
||||
- 2-column button layout
|
||||
5. **Collapsible Guide:** Welcome guide can be shown/hidden on demand
|
||||
|
||||
## Build Status
|
||||
✅ **Build Successful**
|
||||
- Compile time: ~10.4 seconds
|
||||
- No TypeScript errors
|
||||
- All imports resolved correctly
|
||||
- Bundle sizes optimized
|
||||
|
||||
## Testing Recommendations
|
||||
1. **Navigation Testing:**
|
||||
- Verify `/sites/builder` returns 404
|
||||
- Verify `/sites/blueprints` returns 404
|
||||
- Verify `/sites` shows grid view by default
|
||||
|
||||
2. **Welcome Guide Testing:**
|
||||
- Click "Show Welcome Guide" button
|
||||
- Create site with industry and sectors
|
||||
- Verify guide closes after creation
|
||||
- Verify sites list refreshes
|
||||
|
||||
3. **Grid View Testing:**
|
||||
- Verify toggle switch is at top-right near badge
|
||||
- Verify Pages button is removed
|
||||
- Verify Dashboard, Content, Settings buttons work
|
||||
- Test site activation toggle
|
||||
|
||||
4. **Filter Testing:**
|
||||
- Test search filter
|
||||
- Test site type, hosting type, status filters
|
||||
- Verify Clear Filters button works
|
||||
|
||||
## Migration Notes
|
||||
- **No Database Changes:** All changes are frontend-only
|
||||
- **No Breaking Changes:** Existing API endpoints unchanged
|
||||
- **Backward Compatible:** Old routes return 404, no errors
|
||||
|
||||
## Known Limitations
|
||||
- Welcome guide uses same component as dashboard home page
|
||||
- Toggle switch might need accessibility improvements (aria-labels)
|
||||
- Grid view doesn't support sorting (table view does)
|
||||
|
||||
## Future Enhancements
|
||||
1. Add sorting to grid view
|
||||
2. Add bulk actions for multiple site selection
|
||||
3. Improve mobile responsiveness of site cards
|
||||
4. Add site preview/thumbnail images
|
||||
5. Implement site templates (different from removed blueprints)
|
||||
|
||||
## Documentation Updates Needed
|
||||
- Update user guide to reflect new Sites page layout
|
||||
- Update screenshots in help documentation
|
||||
- Remove builder/blueprint references from all docs
|
||||
|
||||
## Conclusion
|
||||
All 10 tasks completed successfully in a single comprehensive refactor. The Sites List page now:
|
||||
- Has no builder/blueprint functionality
|
||||
- Shows grid view by default
|
||||
- Integrates welcome guide for site creation
|
||||
- Displays cleaner site cards with reorganized controls
|
||||
- Provides better user experience with simplified navigation
|
||||
|
||||
**Status:** ✅ **COMPLETE**
|
||||
**Build:** ✅ **PASSING**
|
||||
**Total Tasks:** 10/10
|
||||
@@ -1,662 +0,0 @@
|
||||
# Sites Module UI Standardization Plan
|
||||
**Goal:** Make Sites module UI consistent with IGNY8 global design standard (Planner/Writer/Dashboard)
|
||||
|
||||
**Reference:** See `IGNY8_DESIGN_STANDARD.md` for complete design patterns
|
||||
|
||||
**Status:** Ready for implementation
|
||||
**Priority:** Medium-High (improves UX, accessibility, maintainability)
|
||||
**Estimated Effort:** 4-6 hours
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
### Current State
|
||||
The Sites module uses inconsistent UI patterns:
|
||||
- ✅ Uses Button component correctly in many places
|
||||
- ✅ Uses EnhancedMetricCard for metrics
|
||||
- ❌ Uses `<button onClick={() => navigate(...)}` instead of `<Link to={...}>` in Quick Actions
|
||||
- ❌ Missing ComponentCard wrapper for section organization
|
||||
- ⚠️ Heavy inline Tailwind styling instead of reusable components
|
||||
- ⚠️ Manual section headings instead of ComponentCard title prop
|
||||
|
||||
### Target State
|
||||
Standardize Sites module to match Planner/Writer patterns:
|
||||
- ✅ All navigation uses `<Link>` component (accessibility + keyboard nav)
|
||||
- ✅ All sections wrapped in ComponentCard
|
||||
- ✅ Quick Actions follow standard gradient icon pattern
|
||||
- ✅ Consistent Button component usage (no raw buttons)
|
||||
- ✅ Reduced inline Tailwind duplication
|
||||
|
||||
### Impact
|
||||
- **Accessibility:** Better keyboard navigation and screen reader support
|
||||
- **Maintainability:** Easier to update shared styles globally
|
||||
- **Consistency:** Users see familiar patterns across all modules
|
||||
- **Code Quality:** Less duplication, clearer component boundaries
|
||||
|
||||
---
|
||||
|
||||
## Audit Results
|
||||
|
||||
### Files Analyzed
|
||||
- ✅ `Dashboard.tsx` (357 lines) - Main site dashboard
|
||||
- ✅ `List.tsx` (sites list and creation)
|
||||
- ✅ `Settings.tsx` (site settings tabs)
|
||||
- ✅ `Content.tsx` (content management)
|
||||
- ✅ `PageManager.tsx` (page management)
|
||||
- ✅ `PostEditor.tsx` (post editing)
|
||||
- ✅ `SyncDashboard.tsx` (sync status)
|
||||
- ✅ `DeploymentPanel.tsx` (deployment)
|
||||
- ✅ `Preview.tsx` (site preview)
|
||||
- ✅ `Manage.tsx` (site management)
|
||||
- ✅ `Editor.tsx` (site editor)
|
||||
|
||||
### Issues Found
|
||||
|
||||
#### 1. Navigation Pattern Inconsistency
|
||||
**Files affected:** Dashboard.tsx (5 instances)
|
||||
|
||||
**Current (❌):**
|
||||
```tsx
|
||||
<button
|
||||
onClick={() => navigate(`/sites/${siteId}/pages`)}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
{/* ... */}
|
||||
</button>
|
||||
```
|
||||
|
||||
**Target (✅):**
|
||||
```tsx
|
||||
<Link
|
||||
to={`/sites/${siteId}/pages`}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
{/* ... */}
|
||||
</Link>
|
||||
```
|
||||
|
||||
**Reason:** Link provides better accessibility, keyboard navigation, and browser features (right-click open in new tab)
|
||||
|
||||
---
|
||||
|
||||
#### 2. Missing ComponentCard Wrapper
|
||||
**Files affected:** Dashboard.tsx, List.tsx, Settings.tsx
|
||||
|
||||
**Current (❌):**
|
||||
```tsx
|
||||
<div className="mb-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Quick Actions
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{/* actions */}
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Target (✅):**
|
||||
```tsx
|
||||
<ComponentCard title="Quick Actions" desc="Common site management tasks">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{/* actions */}
|
||||
</div>
|
||||
</ComponentCard>
|
||||
```
|
||||
|
||||
**Reason:** Consistent section styling, automatic dark mode support, less boilerplate
|
||||
|
||||
---
|
||||
|
||||
#### 3. Button + Navigate Anti-Pattern
|
||||
**Files affected:** Content.tsx (2 instances), Editor.tsx (2 instances), List.tsx (1 instance)
|
||||
|
||||
**Current (❌):**
|
||||
```tsx
|
||||
<Button onClick={() => navigate('/sites/builder')} variant="primary">
|
||||
Create New Site
|
||||
</Button>
|
||||
```
|
||||
|
||||
**Target (✅):**
|
||||
```tsx
|
||||
<Button as={Link} to="/sites/builder" variant="primary">
|
||||
Create New Site
|
||||
</Button>
|
||||
```
|
||||
|
||||
**Reason:** Button component supports `as` prop for Link rendering while maintaining Button styles
|
||||
|
||||
---
|
||||
|
||||
#### 4. Inline Tailwind Duplication
|
||||
**Files affected:** Settings.tsx (30+ instances), Content.tsx, List.tsx
|
||||
|
||||
**Example:**
|
||||
```tsx
|
||||
className="mt-1 w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-md dark:bg-gray-800 dark:text-white"
|
||||
```
|
||||
|
||||
**Solution:** Extract to reusable Input component or use existing form components
|
||||
|
||||
---
|
||||
|
||||
#### 5. Raw Button Elements
|
||||
**Files affected:** Multiple (50+ instances found)
|
||||
|
||||
**Pattern:** Some `<button>` tags for non-navigation actions (modals, toggles) - these are acceptable if not for navigation
|
||||
|
||||
---
|
||||
|
||||
## Refactoring Plan by File
|
||||
|
||||
### Phase 1: Dashboard.tsx (High Priority)
|
||||
**Lines affected:** 254-324 (Quick Actions section)
|
||||
|
||||
**Changes:**
|
||||
1. Import Link from react-router-dom
|
||||
2. Replace 5 `<button onClick={navigate}>` with `<Link to>`
|
||||
3. Wrap Quick Actions section in ComponentCard
|
||||
4. Remove manual heading, use ComponentCard title prop
|
||||
|
||||
**Before:**
|
||||
```tsx
|
||||
<div className="mb-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Quick Actions
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<button onClick={() => navigate(`/sites/${siteId}/pages`)} className="...">
|
||||
{/* ... */}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```tsx
|
||||
<ComponentCard title="Quick Actions" desc="Common site management tasks">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<Link to={`/sites/${siteId}/pages`} className="...">
|
||||
{/* ... */}
|
||||
</Link>
|
||||
</div>
|
||||
</ComponentCard>
|
||||
```
|
||||
|
||||
**Estimated Time:** 30 minutes
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Content.tsx (Medium Priority)
|
||||
**Lines affected:** 133, 214 (Button with navigate)
|
||||
|
||||
**Changes:**
|
||||
1. Replace `<Button onClick={() => navigate(...)}>` with `<Button as={Link} to={...}>`
|
||||
2. Verify variant prop consistency
|
||||
3. Test navigation after change
|
||||
|
||||
**Before:**
|
||||
```tsx
|
||||
<Button onClick={() => navigate(`/sites/${siteId}/posts/new`)} variant="primary">
|
||||
Create New Post
|
||||
</Button>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```tsx
|
||||
<Button as={Link} to={`/sites/${siteId}/posts/new`} variant="primary">
|
||||
Create New Post
|
||||
</Button>
|
||||
```
|
||||
|
||||
**Estimated Time:** 15 minutes
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: List.tsx (Medium Priority)
|
||||
**Lines affected:** 670 (Button with navigate), filter/tab sections
|
||||
|
||||
**Changes:**
|
||||
1. Replace `<Button onClick={() => navigate('/sites/builder')}` with `<Button as={Link} to="/sites/builder">`
|
||||
2. Consider extracting filter section to reusable component
|
||||
3. Review tab navigation pattern for consistency
|
||||
|
||||
**Estimated Time:** 30 minutes
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Settings.tsx (Low Priority - Large File)
|
||||
**Lines affected:** 30+ inline Tailwind instances
|
||||
|
||||
**Changes:**
|
||||
1. Extract repeated input styling to shared component
|
||||
2. Consider creating SettingsSection component (like ComponentCard but for tabs)
|
||||
3. Review tab navigation pattern
|
||||
4. Consolidate status badge styling
|
||||
|
||||
**Deferred:** This file needs deeper refactor - consider separate task
|
||||
|
||||
**Estimated Time:** 2 hours (deferred)
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: Editor.tsx (Low Priority)
|
||||
**Lines affected:** 117, 147 (Button with navigate)
|
||||
|
||||
**Changes:**
|
||||
1. Replace Button + navigate with Button as Link
|
||||
2. Verify navigation flow
|
||||
|
||||
**Estimated Time:** 10 minutes
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: Other Files
|
||||
**Files:** SyncDashboard.tsx, PageManager.tsx, DeploymentPanel.tsx, Preview.tsx, Manage.tsx
|
||||
|
||||
**Review:** Most raw buttons here are for actions (not navigation) - acceptable usage
|
||||
**Action:** Verify each instance is truly an action (modal, toggle) and not navigation
|
||||
|
||||
**Estimated Time:** 30 minutes audit
|
||||
|
||||
---
|
||||
|
||||
## Implementation Steps (Detailed)
|
||||
|
||||
### Step 1: Create Quick Action Component (Optional - DRY)
|
||||
**File:** `frontend/src/components/sites/SiteActionCard.tsx`
|
||||
|
||||
**Purpose:** Extract repeated Quick Action card pattern
|
||||
|
||||
**Props:**
|
||||
```tsx
|
||||
interface SiteActionCardProps {
|
||||
to: string;
|
||||
icon: ReactNode;
|
||||
title: string;
|
||||
description: string;
|
||||
gradientColor: 'primary' | 'success' | 'warning' | 'purple';
|
||||
hoverColor: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Reduce duplication (5 cards in Dashboard become 5 component calls)
|
||||
- Consistent styling automatically
|
||||
- Easier to update globally
|
||||
|
||||
**Decision:** Create if time permits - not critical path
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Dashboard.tsx Refactor
|
||||
**File:** `frontend/src/pages/Sites/Dashboard.tsx`
|
||||
|
||||
**Line-by-line changes:**
|
||||
|
||||
1. **Add import:**
|
||||
```tsx
|
||||
import { Link } from 'react-router-dom';
|
||||
import ComponentCard from '../../components/common/ComponentCard';
|
||||
```
|
||||
|
||||
2. **Replace Quick Actions section (lines 245-324):**
|
||||
|
||||
**OLD:**
|
||||
```tsx
|
||||
{/* Quick Actions - Matching Planner Dashboard pattern */}
|
||||
<div className="mb-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Quick Actions
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<button
|
||||
onClick={() => navigate(`/sites/${siteId}/pages`)}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
```
|
||||
|
||||
**NEW:**
|
||||
```tsx
|
||||
{/* Quick Actions */}
|
||||
<ComponentCard title="Quick Actions" desc="Common site management tasks">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<Link
|
||||
to={`/sites/${siteId}/pages`}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
```
|
||||
|
||||
3. **Repeat for all 5 action cards:**
|
||||
- Manage Pages → `/sites/${siteId}/pages`
|
||||
- Manage Content → `/sites/${siteId}/content`
|
||||
- Integrations → `/sites/${siteId}/settings?tab=integrations`
|
||||
- Sync Dashboard → `/sites/${siteId}/sync`
|
||||
- Deploy Site → `/sites/${siteId}/deploy`
|
||||
|
||||
4. **Update Recent Activity section (optional):**
|
||||
```tsx
|
||||
<ComponentCard title="Recent Activity">
|
||||
{/* content */}
|
||||
</ComponentCard>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Content.tsx Refactor
|
||||
**File:** `frontend/src/pages/Sites/Content.tsx`
|
||||
|
||||
**Changes:**
|
||||
|
||||
1. **Import Link:**
|
||||
```tsx
|
||||
import { Link } from 'react-router-dom';
|
||||
```
|
||||
|
||||
2. **Replace line 133:**
|
||||
```tsx
|
||||
// OLD
|
||||
<Button onClick={() => navigate(`/sites/${siteId}/posts/new`)} variant="primary">
|
||||
|
||||
// NEW
|
||||
<Button as={Link} to={`/sites/${siteId}/posts/new`} variant="primary">
|
||||
```
|
||||
|
||||
3. **Replace line 214 (duplicate):**
|
||||
```tsx
|
||||
// OLD
|
||||
<Button onClick={() => navigate(`/sites/${siteId}/posts/new`)} variant="primary">
|
||||
|
||||
// NEW
|
||||
<Button as={Link} to={`/sites/${siteId}/posts/new`} variant="primary">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: List.tsx Refactor
|
||||
**File:** `frontend/src/pages/Sites/List.tsx`
|
||||
|
||||
**Changes:**
|
||||
|
||||
1. **Import Link:**
|
||||
```tsx
|
||||
import { Link } from 'react-router-dom';
|
||||
```
|
||||
|
||||
2. **Replace line 670:**
|
||||
```tsx
|
||||
// OLD
|
||||
<Button onClick={() => navigate('/sites/builder')} variant="outline">
|
||||
|
||||
// NEW
|
||||
<Button as={Link} to="/sites/builder" variant="outline">
|
||||
```
|
||||
|
||||
3. **Review filter buttons (lines 681-693):**
|
||||
These appear to be actual buttons (state toggles), not navigation - keep as-is
|
||||
|
||||
---
|
||||
|
||||
### Step 5: Editor.tsx Refactor
|
||||
**File:** `frontend/src/pages/Sites/Editor.tsx`
|
||||
|
||||
**Changes:**
|
||||
|
||||
1. **Import Link:**
|
||||
```tsx
|
||||
import { Link } from 'react-router-dom';
|
||||
```
|
||||
|
||||
2. **Replace line 117:**
|
||||
```tsx
|
||||
// OLD
|
||||
<Button onClick={() => navigate('/sites/builder')} variant="primary">
|
||||
|
||||
// NEW
|
||||
<Button as={Link} to="/sites/builder" variant="primary">
|
||||
```
|
||||
|
||||
3. **Replace line 147:**
|
||||
```tsx
|
||||
// OLD
|
||||
<Button onClick={() => navigate('/sites/builder')} variant="primary">
|
||||
|
||||
// NEW
|
||||
<Button as={Link} to="/sites/builder" variant="primary">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 6: Final Audit
|
||||
**Files:** All Sites pages
|
||||
|
||||
**Checklist:**
|
||||
- [ ] No `<button onClick={() => navigate(...)}` for navigation
|
||||
- [ ] All navigation uses Link or Button with `as={Link}`
|
||||
- [ ] Section headers use ComponentCard where appropriate
|
||||
- [ ] EnhancedMetricCard used for all metrics
|
||||
- [ ] Button component variants consistent
|
||||
- [ ] No raw `<button>` for navigation (actions are OK)
|
||||
|
||||
---
|
||||
|
||||
## Testing Plan
|
||||
|
||||
### Visual Regression Testing
|
||||
1. Compare Sites Dashboard to Planner Dashboard side-by-side
|
||||
2. Verify Quick Actions grid layout matches (1/2/3 columns)
|
||||
3. Verify gradient icon boxes match size and colors
|
||||
4. Verify hover states match (border color, shadow)
|
||||
5. Verify ComponentCard styling matches other modules
|
||||
|
||||
### Functional Testing
|
||||
1. Test keyboard navigation (Tab through actions, Enter to navigate)
|
||||
2. Test right-click "Open in new tab" on all action cards
|
||||
3. Test screen reader labels (use browser inspector)
|
||||
4. Test all navigation paths work correctly
|
||||
5. Test dark mode consistency
|
||||
|
||||
### Accessibility Testing
|
||||
1. Run Lighthouse accessibility audit before/after
|
||||
2. Verify all Links have proper href attributes (not onClick)
|
||||
3. Verify focus indicators visible on keyboard nav
|
||||
4. Verify semantic HTML (Links vs buttons)
|
||||
|
||||
### Code Quality Testing
|
||||
1. Run TypeScript compiler - 0 errors
|
||||
2. Run ESLint - 0 warnings on changed files
|
||||
3. Verify no console errors in browser
|
||||
4. Verify no duplicate imports
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
### Pre-Implementation
|
||||
1. Create feature branch: `feature/sites-ui-standardization`
|
||||
2. Commit each file change separately for easy rollback
|
||||
3. Test each file after change before moving to next
|
||||
|
||||
### If Issues Found
|
||||
1. Revert specific file commit
|
||||
2. Investigate issue in isolation
|
||||
3. Re-apply fix and test
|
||||
|
||||
### Full Rollback
|
||||
```bash
|
||||
git checkout main -- frontend/src/pages/Sites/Dashboard.tsx
|
||||
git checkout main -- frontend/src/pages/Sites/Content.tsx
|
||||
# etc.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Must Have (P0)
|
||||
- ✅ All Quick Actions use Link component (not button + navigate)
|
||||
- ✅ Dashboard Quick Actions wrapped in ComponentCard
|
||||
- ✅ Zero TypeScript errors
|
||||
- ✅ All navigation paths work correctly
|
||||
|
||||
### Should Have (P1)
|
||||
- ✅ Button + navigate replaced with Button as Link
|
||||
- ✅ Keyboard navigation works on all action cards
|
||||
- ✅ Visual consistency with Planner/Writer modules
|
||||
|
||||
### Nice to Have (P2)
|
||||
- ⏸ SiteActionCard component extracted (DRY improvement)
|
||||
- ⏸ Settings.tsx input styling standardized (larger refactor)
|
||||
- ⏸ Status badge component extracted
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
### Low Risk
|
||||
- Dashboard.tsx Quick Actions refactor (structural change, low impact)
|
||||
- Content/List/Editor Button changes (simple prop change)
|
||||
|
||||
### Medium Risk
|
||||
- ComponentCard integration (may affect spacing/layout)
|
||||
- Link component behavior differences (unlikely but possible)
|
||||
|
||||
### High Risk
|
||||
- None identified
|
||||
|
||||
### Mitigation
|
||||
- Test in dev environment before production
|
||||
- Create feature flag if needed (not expected)
|
||||
- Monitor error logs after deployment
|
||||
|
||||
---
|
||||
|
||||
## Timeline
|
||||
|
||||
### Immediate (Today)
|
||||
- ✅ Design standard documentation (DONE)
|
||||
- ✅ Audit sites pages (DONE)
|
||||
- ✅ Create refactoring plan (DONE)
|
||||
|
||||
### Phase 1 (1-2 hours)
|
||||
- Dashboard.tsx Quick Actions refactor
|
||||
- Test visual consistency
|
||||
- Test navigation functionality
|
||||
|
||||
### Phase 2 (30 minutes)
|
||||
- Content.tsx, List.tsx, Editor.tsx refactors
|
||||
- Test all navigation paths
|
||||
|
||||
### Phase 3 (30 minutes)
|
||||
- Final audit and QA
|
||||
- Accessibility testing
|
||||
- Documentation update
|
||||
|
||||
### Total Estimated Time: 2-3 hours active work
|
||||
|
||||
---
|
||||
|
||||
## Documentation Updates
|
||||
|
||||
### After Implementation
|
||||
1. Update DESIGN_SYSTEM.md with Sites module compliance
|
||||
2. Add Sites Dashboard to reference implementations list
|
||||
3. Document SiteActionCard component (if created)
|
||||
4. Update CHANGELOG.md with UI standardization entry
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Beyond This Refactor
|
||||
1. Extract Settings.tsx tab pattern to reusable component
|
||||
2. Create FormInput component for repeated input styling
|
||||
3. Standardize status badge patterns across all modules
|
||||
4. Add loading states to all navigation actions
|
||||
5. Add transition animations to match Planner/Writer
|
||||
|
||||
### Technical Debt Reduction
|
||||
1. Audit all inline Tailwind usage across Sites module
|
||||
2. Create Sites-specific component library (like dashboard components)
|
||||
3. Consolidate color usage (ensure CSS variables used consistently)
|
||||
|
||||
---
|
||||
|
||||
## Questions & Decisions
|
||||
|
||||
### Open Questions
|
||||
- [ ] Should we create SiteActionCard component now or later?
|
||||
- **Decision:** Later - keep first pass simple, extract after pattern proven
|
||||
|
||||
- [ ] Should Settings.tsx be included in this refactor?
|
||||
- **Decision:** No - defer to separate task due to complexity
|
||||
|
||||
- [ ] Should we add analytics tracking to navigation events?
|
||||
- **Decision:** Out of scope - separate feature request
|
||||
|
||||
### Decisions Made
|
||||
- ✅ Use Link component (not button + navigate) for all navigation
|
||||
- ✅ Use ComponentCard for section organization
|
||||
- ✅ Use Button `as={Link}` pattern for button-styled navigation
|
||||
- ✅ Defer Settings.tsx refactor to separate task
|
||||
- ✅ Keep PageManager.tsx raw buttons (mostly actions, not navigation)
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Code Snippets
|
||||
|
||||
### Standard Quick Action Card Pattern
|
||||
```tsx
|
||||
<Link
|
||||
to={path}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<Icon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Action Title</h4>
|
||||
<p className="text-sm text-slate-600">Action description</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-primary)] transition" />
|
||||
</Link>
|
||||
```
|
||||
|
||||
### Gradient Color Reference
|
||||
```tsx
|
||||
// Primary (Blue)
|
||||
from-[var(--color-primary)] to-[var(--color-primary-dark)]
|
||||
hover:border-[var(--color-primary)]
|
||||
group-hover:text-[var(--color-primary)]
|
||||
|
||||
// Success (Green)
|
||||
from-[var(--color-success)] to-[var(--color-success-dark)]
|
||||
hover:border-[var(--color-success)]
|
||||
group-hover:text-[var(--color-success)]
|
||||
|
||||
// Warning (Orange)
|
||||
from-[var(--color-warning)] to-[var(--color-warning-dark)]
|
||||
hover:border-[var(--color-warning)]
|
||||
group-hover:text-[var(--color-warning)]
|
||||
|
||||
// Purple
|
||||
from-[var(--color-purple)] to-[var(--color-purple-dark)]
|
||||
hover:border-[var(--color-purple)]
|
||||
group-hover:text-[var(--color-purple)]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**End of Plan**
|
||||
|
||||
**Next Steps:**
|
||||
1. Review this plan with team (if applicable)
|
||||
2. Create feature branch
|
||||
3. Start Phase 1 implementation (Dashboard.tsx)
|
||||
4. Test and iterate
|
||||
5. Deploy and monitor
|
||||
|
||||
**Prepared By:** GitHub Copilot
|
||||
**Date:** 2025-01-21
|
||||
**Version:** 1.0
|
||||
@@ -1,496 +0,0 @@
|
||||
# STAGE 2 FRONTEND REFACTOR - DETAILED EXECUTION PLAN
|
||||
|
||||
**Created:** November 25, 2025
|
||||
**Status:** Planning Complete - Ready for Execution
|
||||
|
||||
---
|
||||
|
||||
## 📊 SCOPE ANALYSIS
|
||||
|
||||
**Total Frontend Files:** 413 TypeScript files
|
||||
**Files with Deprecated Fields:** 17 identified
|
||||
**Major Modules to Update:** 6 (Planner, Writer, Sites, Settings, Linker, Optimizer)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 EXECUTION SEQUENCE
|
||||
|
||||
### PHASE 1: Core API & Type Definitions (Foundation)
|
||||
**Priority:** CRITICAL - Must be done first
|
||||
**Estimated Changes:** ~500 lines across 2-3 files
|
||||
|
||||
#### 1.1 Update API Service (`src/services/api.ts`)
|
||||
**File:** `frontend/src/services/api.ts` (2482 lines)
|
||||
|
||||
**Changes Required:**
|
||||
|
||||
**ContentIdea Interface (Lines 770-783):**
|
||||
- ❌ Remove: `site_entity_type`, `cluster_role`
|
||||
- ✅ Keep: `content_structure`, `content_type`
|
||||
|
||||
**Task Interface (Lines 930-940):**
|
||||
- ❌ Remove: `entity_type`, `cluster_role`
|
||||
- ✅ Add: `content_type` (if missing)
|
||||
- ✅ Add: `content_structure` (if missing)
|
||||
- ✅ Add: `taxonomy_term_id` (optional)
|
||||
|
||||
**Content Interface (Lines 2010-2030):**
|
||||
- ❌ Remove: `entity_type`, `cluster_role`, `sync_status`
|
||||
- ✅ Add: `content_type` (required)
|
||||
- ✅ Add: `content_structure` (required)
|
||||
- ✅ Add: `taxonomy_terms` (array of taxonomy objects)
|
||||
- ✅ Add: `source` ('igny8' | 'wordpress')
|
||||
- ✅ Update `status` type to ('draft' | 'published')
|
||||
|
||||
**ContentValidation Interface (Lines 2095-2110):**
|
||||
- ❌ Remove: `has_entity_type`, `entity_type`
|
||||
- ✅ Add: `has_content_type`, `content_type`
|
||||
|
||||
**Filter Interfaces:**
|
||||
- Update `TasksFilters` to include `content_type`, `content_structure`
|
||||
- Update `ContentFilters` to include `content_type`, `content_structure`, `source`
|
||||
|
||||
#### 1.2 Update Integration API (`src/services/integration.api.ts`)
|
||||
**Changes Required:**
|
||||
- Update `sync_status` type from custom to match backend
|
||||
- Remove any deprecated field references
|
||||
|
||||
---
|
||||
|
||||
### PHASE 2: Configuration Files (Data Layer)
|
||||
**Priority:** HIGH - Defines table columns and filters
|
||||
**Estimated Changes:** ~300 lines across 3 files
|
||||
|
||||
#### 2.1 Tasks Config (`src/config/pages/tasks.config.tsx`)
|
||||
**Changes:**
|
||||
- Update column definitions: remove `entity_type`, `cluster_role`
|
||||
- Add columns: `content_type`, `content_structure`
|
||||
- Update filters: replace deprecated with new fields
|
||||
- Update status options to: `queued`, `completed`
|
||||
|
||||
#### 2.2 Content Config (`src/config/pages/content.config.tsx`)
|
||||
**Changes:**
|
||||
- Update column definitions: remove `entity_type`, `cluster_role`, `sync_status`
|
||||
- Add columns: `content_type`, `content_structure`, `source`, `taxonomy_terms`
|
||||
- Update filters: add `content_type`, `content_structure`, `source`
|
||||
- Update status options to: `draft`, `published`
|
||||
|
||||
#### 2.3 Ideas Config (`src/config/pages/ideas.config.tsx`)
|
||||
**Changes:**
|
||||
- Remove: `site_entity_type`, `cluster_role`
|
||||
- Ensure: `content_type`, `content_structure` are present
|
||||
|
||||
---
|
||||
|
||||
### PHASE 3: Zustand Stores (State Management)
|
||||
**Priority:** HIGH - Core state management
|
||||
**Estimated Changes:** ~100 lines across 2 files
|
||||
|
||||
#### 3.1 Planner Store (`src/store/plannerStore.ts`)
|
||||
**Changes:**
|
||||
- Update task state interface to match new API types
|
||||
- Remove deprecated field handling
|
||||
|
||||
#### 3.2 Create/Update Content Store
|
||||
**Action:** Check if exists, if not create new store for content state management
|
||||
- Handle `status`: draft, published
|
||||
- Handle `source`: igny8, wordpress
|
||||
- Handle `taxonomy_terms` array
|
||||
|
||||
---
|
||||
|
||||
### PHASE 4: Planner Module (Part B)
|
||||
**Priority:** MEDIUM
|
||||
**Estimated Changes:** ~400 lines across 3 files
|
||||
|
||||
#### 4.1 Clusters Page (`src/pages/Planner/Clusters.tsx`)
|
||||
**Changes:**
|
||||
- Make cluster names clickable → navigate to `/clusters/:id`
|
||||
- Remove any `context_type` or `dimension_meta` displays
|
||||
- Clean up cluster card UI
|
||||
|
||||
#### 4.2 Ideas Page (`src/pages/Planner/Ideas.tsx`)
|
||||
**Current:** 15,062 lines
|
||||
**Changes:**
|
||||
- Update idea cards to show: cluster, `content_type`, `content_structure`
|
||||
- Remove: `site_entity_type`, `cluster_role`
|
||||
- Update create/edit forms
|
||||
|
||||
#### 4.3 Planner Dashboard (`src/pages/Planner/Dashboard.tsx`)
|
||||
**Changes:**
|
||||
- Remove deprecated field displays
|
||||
- Update task creation form:
|
||||
- Required: cluster, content_type, content_structure
|
||||
- Optional: taxonomy_term (only when content_type = 'taxonomy')
|
||||
- Remove: cluster_role, entity_type
|
||||
|
||||
---
|
||||
|
||||
### PHASE 5: Writer Module (Part C)
|
||||
**Priority:** HIGH
|
||||
**Estimated Changes:** ~600 lines across 3 files
|
||||
|
||||
#### 5.1 Tasks Page (`src/pages/Writer/Tasks.tsx`)
|
||||
**Current:** 28,013 lines
|
||||
**Changes:**
|
||||
- Update table columns:
|
||||
- Replace `entity_type` → `content_type`
|
||||
- Replace `cluster_role` → `content_structure`
|
||||
- Update status display (queued/completed)
|
||||
- Update filters:
|
||||
- Add: content_type, content_structure
|
||||
- Remove: entity_type, cluster_role, sync_status
|
||||
- Task detail: redirect to Content Manager after generation
|
||||
|
||||
#### 5.2 Content Page (`src/pages/Writer/Content.tsx`)
|
||||
**Current:** 11,037 lines
|
||||
**Changes:**
|
||||
- Update table columns:
|
||||
- Add: content_type, content_structure, source, taxonomy_terms
|
||||
- Remove: entity_type, cluster_role, sync_status
|
||||
- Update filters:
|
||||
- Add: content_type, content_structure, source
|
||||
- Remove: deprecated filters
|
||||
- Update status display (draft/published)
|
||||
|
||||
#### 5.3 Writer Dashboard (`src/pages/Writer/Dashboard.tsx`)
|
||||
**Changes:**
|
||||
- Update metrics to use new field names
|
||||
- Remove deprecated status counts
|
||||
|
||||
---
|
||||
|
||||
### PHASE 6: Sites Module - Major Restructure (Part D)
|
||||
**Priority:** CRITICAL - 18-point specification
|
||||
**Estimated Changes:** ~1000 lines across 4 files
|
||||
|
||||
#### 6.1 Sites List Page (`src/pages/Sites/List.tsx`)
|
||||
**Current:** 36,680 lines
|
||||
**18-Point Changes:**
|
||||
1. ✅ Grid view ONLY - remove table toggle
|
||||
2. ✅ Collapsible Add Site form
|
||||
3. ✅ Remove: Pages button, Sectors button, Blueprints button
|
||||
4. ✅ Remove: Create Site (builder) button
|
||||
5. ✅ Remove: Banner notifications
|
||||
6. ✅ Remove: "Sites Configuration" text
|
||||
7. ✅ Site card top-right: Active/inactive switch
|
||||
8. ✅ Site card bottom: ONLY Dashboard, Content, Settings buttons
|
||||
|
||||
#### 6.2 Site Settings Page (`src/pages/Sites/Settings.tsx`)
|
||||
**Current:** 37,735 lines
|
||||
**Changes:**
|
||||
- Two-row grid of 4 cards: General, SEO Meta, Open Graph, Schema
|
||||
- Move Sector/Industry selector below cards
|
||||
- Remove deprecated integration fields
|
||||
|
||||
#### 6.3 Content Page (`src/pages/Sites/Content.tsx`)
|
||||
**Current:** 11,424 lines
|
||||
**Changes:**
|
||||
- Update to use new content fields
|
||||
- Remove deprecated filters
|
||||
- This becomes primary content management interface
|
||||
|
||||
#### 6.4 Remove/Clean Builder Folder
|
||||
**Action:** Review `src/pages/Sites/Builder/` - may need deprecation
|
||||
|
||||
---
|
||||
|
||||
### PHASE 7: Create Cluster Detail Page (Part E - NEW)
|
||||
**Priority:** MEDIUM
|
||||
**Estimated Changes:** ~500 lines - NEW FILE
|
||||
|
||||
#### 7.1 Create New Page (`src/pages/Planner/ClusterDetail.tsx`)
|
||||
**Structure:**
|
||||
- Route: `/clusters/:id`
|
||||
- Tabs: Articles, Pages, Products, Taxonomy Pages
|
||||
- Each tab shows:
|
||||
- Title (clickable → Content Manager)
|
||||
- content_type
|
||||
- content_structure
|
||||
- taxonomy_terms (as chips/tags)
|
||||
- status (draft/published)
|
||||
- source (IGNY8/WP)
|
||||
- Actions (edit, view)
|
||||
|
||||
#### 7.2 Update Routing
|
||||
**File:** `src/App.tsx` or routing config
|
||||
- Add route: `/clusters/:id` → ClusterDetail component
|
||||
|
||||
---
|
||||
|
||||
### PHASE 8: Content Manager Refactor (Part F - MOST CRITICAL)
|
||||
**Priority:** CRITICAL
|
||||
**Estimated Changes:** ~800 lines across 2-3 files
|
||||
|
||||
#### 8.1 Content Table Component
|
||||
**Location:** TBD (may be in Sites/Content.tsx or separate)
|
||||
|
||||
**Table Columns (New Structure):**
|
||||
1. Title (clickable → edit)
|
||||
2. Content Type
|
||||
3. Content Structure
|
||||
4. Cluster
|
||||
5. Taxonomy Terms (chips)
|
||||
6. Status (draft/published badge)
|
||||
7. Source (IGNY8/WP badge)
|
||||
8. URL (if published)
|
||||
9. Word Count (computed frontend from content_html)
|
||||
10. Images (if API supports)
|
||||
11. Actions dropdown
|
||||
|
||||
**Filters:**
|
||||
- cluster (dropdown)
|
||||
- content_type (dropdown)
|
||||
- content_structure (dropdown)
|
||||
- taxonomy (multi-select)
|
||||
- status (draft/published)
|
||||
- source (igny8/wordpress)
|
||||
|
||||
**Row Actions:**
|
||||
- Edit Content
|
||||
- Publish to WordPress
|
||||
- View in WordPress (if external_url)
|
||||
- Assign taxonomy
|
||||
- Assign cluster
|
||||
- View images
|
||||
|
||||
**Bulk Actions:**
|
||||
- Bulk publish
|
||||
- Bulk assign cluster
|
||||
- Bulk assign taxonomy
|
||||
|
||||
#### 8.2 Content Editor Page
|
||||
**Create:** `src/pages/Sites/ContentEditor.tsx` or update existing
|
||||
|
||||
**Editable Fields:**
|
||||
- title (text input)
|
||||
- content_html (rich editor)
|
||||
- cluster (dropdown)
|
||||
- taxonomy_terms (multi-select)
|
||||
- content_type (dropdown - backend choices)
|
||||
- content_structure (dropdown - backend choices)
|
||||
|
||||
**Remove:**
|
||||
- sync_status
|
||||
- cluster_role
|
||||
- All WP meta fields (handled by backend)
|
||||
|
||||
#### 8.3 Publish to WordPress
|
||||
**Function:** Update publish handler
|
||||
- Call: `POST /v1/writer/content/{id}/publish/`
|
||||
- On success:
|
||||
- Update local state: status = 'published'
|
||||
- Update: external_id, external_url
|
||||
- Show success toast with WP URL
|
||||
|
||||
---
|
||||
|
||||
### PHASE 9: Integration Components
|
||||
**Priority:** LOW-MEDIUM
|
||||
**Estimated Changes:** ~200 lines across 2 files
|
||||
|
||||
#### 9.1 WordPress Integration Card (`src/components/sites/WordPressIntegrationCard.tsx`)
|
||||
**Changes:**
|
||||
- Remove deprecated sync_status displays
|
||||
- Update to show source tracking
|
||||
|
||||
#### 9.2 Site Integrations Section (`src/components/integration/SiteIntegrationsSection.tsx`)
|
||||
**Changes:**
|
||||
- Update integration status displays
|
||||
- Remove deprecated fields
|
||||
|
||||
---
|
||||
|
||||
### PHASE 10: Linker & Optimizer Modules
|
||||
**Priority:** LOW - Can be done separately
|
||||
**Estimated Changes:** ~400 lines across 4 files
|
||||
|
||||
#### 10.1 Linker Content List (`src/pages/Linker/ContentList.tsx`)
|
||||
**Changes:**
|
||||
- Update content display to use new fields
|
||||
- Remove deprecated filters
|
||||
|
||||
#### 10.2 Optimizer Content Selector (`src/pages/Optimizer/ContentSelector.tsx`)
|
||||
**Changes:**
|
||||
- Update content query fields
|
||||
- Remove entity_type references
|
||||
|
||||
#### 10.3 Optimizer Analysis Preview (`src/pages/Optimizer/AnalysisPreview.tsx`)
|
||||
**Changes:**
|
||||
- Update content field displays
|
||||
|
||||
---
|
||||
|
||||
### PHASE 11: Global UI Cleanup (Part G)
|
||||
**Priority:** MEDIUM
|
||||
**Estimated Changes:** Distributed across all files
|
||||
|
||||
**Actions:**
|
||||
- Search and remove all "Blog post" references
|
||||
- Remove all sync_status badges/labels
|
||||
- Remove cluster_role displays
|
||||
- Remove context_type displays
|
||||
- Ensure button consistency
|
||||
- Ensure icon consistency
|
||||
- Fix spacing and padding issues
|
||||
|
||||
---
|
||||
|
||||
### PHASE 12: Documentation (Part H)
|
||||
**Priority:** LOW
|
||||
**Estimated Changes:** 1 new doc file
|
||||
|
||||
**Create:** `frontend/ARCHITECTURE.md`
|
||||
**Content:**
|
||||
- Final UI structure
|
||||
- Page responsibilities
|
||||
- New pipeline: Planner → Writer → Content Manager → WP Publish
|
||||
- Dropdown field mappings
|
||||
- Component flow diagrams
|
||||
|
||||
---
|
||||
|
||||
### PHASE 13: Changelog (Part I)
|
||||
**Priority:** LOW
|
||||
**Estimated Changes:** 1 file update
|
||||
|
||||
**Update:** `CHANGELOG.md`
|
||||
**Add Entry:**
|
||||
```markdown
|
||||
## [v1.0.0] - Stage 2 Frontend Refactor - 2025-11-25
|
||||
|
||||
### Changed
|
||||
- Planner, Writer, Sites, Clusters, Content Manager UI fully updated
|
||||
- Deprecated UI elements removed (cluster_role, sync_status, entity_type)
|
||||
- Full alignment with Stage 1 backend
|
||||
- Unified statuses: queued→completed (tasks), draft→published (content)
|
||||
- Content types and structures from backend choices
|
||||
- WordPress publish and import integration updated
|
||||
|
||||
### Added
|
||||
- Cluster detail page (/clusters/:id)
|
||||
- Enhanced Content Manager with full taxonomy support
|
||||
- Source tracking (IGNY8/WordPress)
|
||||
- Direct taxonomy term assignment
|
||||
|
||||
### Removed
|
||||
- All deprecated field UI elements
|
||||
- Legacy sync status displays
|
||||
- Blog post type references
|
||||
- Cluster role displays
|
||||
|
||||
### Documentation
|
||||
- Frontend architecture documented
|
||||
- Component flow diagrams added
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 FILE CHANGE SUMMARY
|
||||
|
||||
### Files to Modify (17 core files)
|
||||
1. ✏️ `src/services/api.ts` - API interfaces
|
||||
2. ✏️ `src/services/integration.api.ts` - Integration types
|
||||
3. ✏️ `src/config/pages/tasks.config.tsx` - Task table config
|
||||
4. ✏️ `src/config/pages/content.config.tsx` - Content table config
|
||||
5. ✏️ `src/config/pages/ideas.config.tsx` - Ideas config
|
||||
6. ✏️ `src/store/plannerStore.ts` - Planner state
|
||||
7. ✏️ `src/pages/Planner/Clusters.tsx` - Cluster list
|
||||
8. ✏️ `src/pages/Planner/Ideas.tsx` - Ideas page
|
||||
9. ✏️ `src/pages/Planner/Dashboard.tsx` - Planner dashboard
|
||||
10. ✏️ `src/pages/Writer/Tasks.tsx` - Tasks page
|
||||
11. ✏️ `src/pages/Writer/Content.tsx` - Writer content
|
||||
12. ✏️ `src/pages/Writer/Dashboard.tsx` - Writer dashboard
|
||||
13. ✏️ `src/pages/Sites/List.tsx` - Sites list (18-point fix)
|
||||
14. ✏️ `src/pages/Sites/Settings.tsx` - Site settings
|
||||
15. ✏️ `src/pages/Sites/Content.tsx` - Site content
|
||||
16. ✏️ `src/components/sites/WordPressIntegrationCard.tsx`
|
||||
17. ✏️ `src/components/integration/SiteIntegrationsSection.tsx`
|
||||
|
||||
### Files to Create (2 new files)
|
||||
1. ✨ `src/pages/Planner/ClusterDetail.tsx` - New cluster detail page
|
||||
2. ✨ `frontend/ARCHITECTURE.md` - Frontend documentation
|
||||
|
||||
### Files to Review for Cleanup (6 files)
|
||||
1. 🔍 `src/pages/Linker/ContentList.tsx`
|
||||
2. 🔍 `src/pages/Optimizer/ContentSelector.tsx`
|
||||
3. 🔍 `src/pages/Optimizer/AnalysisPreview.tsx`
|
||||
4. 🔍 `src/pages/Sites/DeploymentPanel.tsx`
|
||||
5. 🔍 `src/pages/Sites/PostEditor.tsx`
|
||||
6. 🔍 `src/pages/Sites/Builder/*` - Consider deprecation
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ CRITICAL DEPENDENCIES
|
||||
|
||||
**Must Complete in Order:**
|
||||
1. PHASE 1 (API) → All other phases depend on this
|
||||
2. PHASE 2 (Config) → Required before UI updates
|
||||
3. PHASE 3 (Stores) → Required before component updates
|
||||
4. PHASES 4-10 → Can be done in parallel after 1-3
|
||||
5. PHASES 11-13 → Final cleanup
|
||||
|
||||
**Backend Contract (DO NOT MODIFY):**
|
||||
- Use EXACT backend field names in API calls
|
||||
- Frontend labels can be prettier, but API payloads must match backend
|
||||
- No new backend fields can be requested
|
||||
|
||||
---
|
||||
|
||||
## 🧪 TESTING STRATEGY
|
||||
|
||||
### After Each Phase:
|
||||
1. **Type Check:** `npm run type-check` (if available)
|
||||
2. **Build Check:** `npm run build`
|
||||
3. **Manual Test:** Verify UI renders without errors
|
||||
|
||||
### Critical Test Paths:
|
||||
1. **Task Creation Flow:** Planner → Create Task → View in Writer
|
||||
2. **Content Generation:** Task → Generate → View in Content Manager
|
||||
3. **WP Publish:** Content Manager → Publish → Verify external_url
|
||||
4. **Cluster Detail:** Clusters → Click Name → View Detail Page
|
||||
5. **Content Filter:** Content Manager → Filter by type, structure, source
|
||||
6. **Bulk Actions:** Select Multiple → Bulk Publish
|
||||
|
||||
---
|
||||
|
||||
## 📊 ESTIMATED EFFORT
|
||||
|
||||
| Phase | Priority | Estimated Time | Lines Changed |
|
||||
|-------|----------|----------------|---------------|
|
||||
| 1. API & Types | CRITICAL | 2-3 hours | ~500 |
|
||||
| 2. Config Files | HIGH | 1-2 hours | ~300 |
|
||||
| 3. Stores | HIGH | 1 hour | ~100 |
|
||||
| 4. Planner | MEDIUM | 2-3 hours | ~400 |
|
||||
| 5. Writer | HIGH | 3-4 hours | ~600 |
|
||||
| 6. Sites (18-pt) | CRITICAL | 4-5 hours | ~1000 |
|
||||
| 7. Cluster Detail | MEDIUM | 2-3 hours | ~500 |
|
||||
| 8. Content Manager | CRITICAL | 4-5 hours | ~800 |
|
||||
| 9. Integration | LOW-MED | 1-2 hours | ~200 |
|
||||
| 10. Linker/Optimizer | LOW | 2 hours | ~400 |
|
||||
| 11. UI Cleanup | MEDIUM | 2-3 hours | distributed |
|
||||
| 12-13. Docs | LOW | 1 hour | minimal |
|
||||
|
||||
**Total Estimated: 25-35 hours of focused development**
|
||||
|
||||
---
|
||||
|
||||
## 🚀 EXECUTION READINESS
|
||||
|
||||
**Status:** ✅ PLAN COMPLETE
|
||||
**Next Step:** Begin Phase 1 - API & Type Definitions
|
||||
**Blocker Check:** ✅ None - Backend is stable and deployed
|
||||
|
||||
**Ready to Execute:** YES
|
||||
|
||||
---
|
||||
|
||||
## 📝 NOTES
|
||||
|
||||
- All changes are frontend-only
|
||||
- Backend is locked and stable
|
||||
- Type safety will catch most issues early
|
||||
- Test incrementally to avoid compound errors
|
||||
- Keep git commits granular per phase
|
||||
|
||||
**End of Execution Plan**
|
||||
505
STAGE_3_PLAN.md
505
STAGE_3_PLAN.md
@@ -1,505 +0,0 @@
|
||||
expects the agent to read:
|
||||
|
||||
STAGE_1_COMPLETE.md
|
||||
|
||||
STAGE_2_REFACTOR_COMPLETE.md
|
||||
|
||||
---
|
||||
|
||||
# 🎉 STAGE 3 — COMPLETED ✅
|
||||
|
||||
**Completion Date:** November 26, 2025
|
||||
**Status:** 100% Complete - All Requirements Met
|
||||
**Documentation:** See `STAGE_3_COMPLETE.md` for detailed implementation summary
|
||||
|
||||
All Stage 3 requirements have been successfully implemented:
|
||||
- ✅ Parts A-F: Core pipeline, Content Manager, WordPress integration, Cluster Detail, Sites module, Status system
|
||||
- ✅ Parts G-I: Performance basics, Documentation, Changelog
|
||||
- ✅ 9 files modified (5 backend + 5 frontend)
|
||||
- ✅ Production-ready with full end-to-end pipeline functionality
|
||||
|
||||
**Next:** Deploy to production and implement deferred performance optimizations.
|
||||
|
||||
---
|
||||
|
||||
## ORIGINAL STAGE 3 PLAN (FOR REFERENCE)
|
||||
|
||||
✅ STAGE 3 — FINAL PIPELINE COMPLETION PROMPT
|
||||
IGNY8 Unified Workflow, WordPress Sync, Publishing, and Final System Stabilization
|
||||
|
||||
## 🎯 STAGE 3 PROGRESS TRACKER
|
||||
|
||||
**Last Updated:** November 25, 2025
|
||||
**Status:** 🟡 In Progress
|
||||
**Completion:** 15% (2/13 major sections)
|
||||
|
||||
### ✅ Completed Sections
|
||||
|
||||
#### ✅ A.1 Planner → Task Flow Verification (Stage 1 & 2)
|
||||
- Keywords → Clusters mapping correct ✅
|
||||
- Ideas → Tasks creation uses final fields ✅
|
||||
- Clusters appear correctly in Writer & Content Manager ✅
|
||||
- No legacy fields flow into tasks ✅
|
||||
- Task statuses correctly set to queued → completed ✅
|
||||
|
||||
#### ✅ Part F.1-F.3 Status System Cleanup (Stage 2)
|
||||
- Content Status: draft/published ✅
|
||||
- Task Status: queued/completed ✅
|
||||
- Source: igny8/wordpress ✅
|
||||
- No legacy statuses in frontend ✅
|
||||
|
||||
#### ✅ NEW: Content Taxonomy API Integration (Stage 3 Partial)
|
||||
**Date Completed:** November 25, 2025
|
||||
|
||||
**Backend:**
|
||||
- ✅ ContentTaxonomyViewSet exists at `/v1/writer/taxonomies/`
|
||||
- ✅ Supports filtering by taxonomy_type, site, sector
|
||||
- ✅ Full CRUD operations available
|
||||
- ✅ Serializer complete with all fields
|
||||
|
||||
**Frontend:**
|
||||
- ✅ Added `fetchTaxonomies()` API function in `services/api.ts`
|
||||
- ✅ Added `ContentTaxonomy` interface matching backend schema
|
||||
- ✅ Added `ContentTaxonomyFilters` interface
|
||||
- ✅ Added `ContentTaxonomyResponse` interface
|
||||
- ✅ Updated Writer Dashboard to fetch real taxonomy data
|
||||
- ✅ Removed all TODO comments for Stage 3/4 taxonomy endpoints
|
||||
- ✅ Taxonomy counts now display real data
|
||||
- ✅ Attribute counts calculated (product_attribute taxonomy type)
|
||||
- ✅ Build passes with zero errors
|
||||
|
||||
**Files Modified:**
|
||||
1. `frontend/src/services/api.ts` (+103 lines)
|
||||
- Added ContentTaxonomy interface
|
||||
- Added fetchTaxonomies() function
|
||||
- Added CRUD operations for taxonomies
|
||||
|
||||
2. `frontend/src/pages/Writer/Dashboard.tsx` (3 changes)
|
||||
- Added fetchTaxonomies import
|
||||
- Updated Promise.all to include taxonomy fetch
|
||||
- Replaced hardcoded 0 values with real taxonomy counts
|
||||
|
||||
**Testing Status:**
|
||||
- ✅ TypeScript compilation passes
|
||||
- ✅ Build completes successfully
|
||||
- ⚠️ Runtime testing pending (requires backend running)
|
||||
|
||||
---
|
||||
|
||||
## 🔴 MANDATORY HEADER — DO NOT SKIP
|
||||
|
||||
The backend is fully finalized per STAGE_1_COMPLETE.md.
|
||||
The frontend architecture and UI structure are defined in STAGE_2_EXECUTION_PLAN.md.
|
||||
|
||||
You MUST NOT:
|
||||
|
||||
modify backend models
|
||||
|
||||
modify backend serializers
|
||||
|
||||
modify backend fields
|
||||
|
||||
change content_type or content_structure choices
|
||||
|
||||
modify WordPress plugin structure
|
||||
|
||||
create new database fields
|
||||
|
||||
change Stage 1 or 2 logic
|
||||
|
||||
Stage 3 is pipeline-level integration, end-to-end fixing, and system stabilization, NOT architecture change.
|
||||
|
||||
🎯 STAGE 3 GOAL
|
||||
|
||||
Make IGNY8 fully functional, with a working, reliable end-to-end pipeline:
|
||||
|
||||
Planner → Writer → Content Manager → Publish → WordPress → Sync → Cluster & Taxonomy Updates → Final Status
|
||||
|
||||
This stage ensures:
|
||||
|
||||
everything connects
|
||||
|
||||
everything updates correctly
|
||||
|
||||
statuses reflect reality
|
||||
|
||||
cluster mapping works
|
||||
|
||||
taxonomy assignments work
|
||||
|
||||
WordPress sync is stable
|
||||
|
||||
publish flow is consistent
|
||||
|
||||
Writer → Content → WP loop is clean
|
||||
|
||||
system supports full-scale SEO workflows
|
||||
|
||||
🔷 PART A — END-TO-END PIPELINE FLOW FIXES
|
||||
A.1 Planner → Task Flow Verification
|
||||
|
||||
Ensure:
|
||||
|
||||
Keywords → Clusters mapping correct
|
||||
|
||||
Ideas → Tasks creation uses final fields
|
||||
|
||||
Clusters created appear correctly in Writer & Content Manager
|
||||
|
||||
No legacy fields flow into tasks
|
||||
|
||||
Task statuses correctly set to queued → completed
|
||||
|
||||
Fix any broken points.
|
||||
|
||||
A.2 Writer → Content Flow
|
||||
|
||||
Ensure:
|
||||
|
||||
Writer generates correct content_html
|
||||
|
||||
Writer stores data using final fields from Stage 1
|
||||
|
||||
Writer tasks insert content into Content table
|
||||
|
||||
Correct mapping:
|
||||
|
||||
cluster
|
||||
|
||||
content_type
|
||||
|
||||
content_structure
|
||||
|
||||
taxonomy_term (optional)
|
||||
|
||||
Content created through Writer must appear immediately in Content Manager
|
||||
|
||||
Task status must update to “completed” after generation
|
||||
|
||||
Fix any inconsistencies.
|
||||
|
||||
🔷 PART B — CONTENT MANAGER FINALIZATION
|
||||
|
||||
The Content Manager becomes the “📌 Single Source of Truth” in IGNY8.
|
||||
|
||||
You must ensure:
|
||||
B.1 Content Manager loads all content types
|
||||
|
||||
From both:
|
||||
|
||||
IGNY8 generated content
|
||||
|
||||
WordPress-synced content
|
||||
|
||||
B.2 Editing is stable
|
||||
|
||||
Editor page must:
|
||||
|
||||
load existing content correctly
|
||||
|
||||
allow editing title and content_html
|
||||
|
||||
allow cluster assignment
|
||||
|
||||
allow taxonomy assignment
|
||||
|
||||
save updates reliably
|
||||
|
||||
show backend validation errors clearly
|
||||
|
||||
B.3 Taxonomy assignment works
|
||||
|
||||
Assigning categories/tags/attributes must update ContentTaxonomy M2M
|
||||
|
||||
No old taxonomy structures referenced
|
||||
|
||||
B.4 Cluster assignment works
|
||||
|
||||
Content cluster updated reliably
|
||||
|
||||
Appears correctly in Cluster Detail page
|
||||
|
||||
B.5 Filters fully functional
|
||||
|
||||
Remove dead filters.
|
||||
Ensure all filters are aligned with backend schema.
|
||||
|
||||
🔷 PART C — WORDPRESS INTEGRATION (IMPORT + PUBLISH)
|
||||
|
||||
This part ensures the SEO cycle is complete.
|
||||
|
||||
C.1 WordPress Sync (WP → IGNY8)
|
||||
|
||||
Verify and fix:
|
||||
|
||||
Import posts/pages/products → Creates Content rows
|
||||
|
||||
Import categories/tags/product_attrs → Creates ContentTaxonomy rows
|
||||
|
||||
Imported content:
|
||||
|
||||
source = wordpress
|
||||
|
||||
status = draft
|
||||
|
||||
correct mapping of external_id & external_url
|
||||
|
||||
Verify:
|
||||
|
||||
site connections
|
||||
|
||||
WP credentials
|
||||
|
||||
CORS rules
|
||||
|
||||
error handling
|
||||
|
||||
Fix anything missing.
|
||||
|
||||
C.2 WordPress Publish (IGNY8 → WP)
|
||||
When user clicks “Publish” in Content Manager:
|
||||
|
||||
Pipeline MUST:
|
||||
|
||||
Build WP payload
|
||||
|
||||
Include:
|
||||
|
||||
title (post_title)
|
||||
|
||||
content_html (post_content)
|
||||
|
||||
taxonomy mappings via external_id
|
||||
|
||||
content_type → correct WP post_type
|
||||
|
||||
Send POST request to WP REST API
|
||||
|
||||
On success:
|
||||
|
||||
Update external_id
|
||||
|
||||
Update external_url
|
||||
|
||||
status → published
|
||||
|
||||
source → igny8
|
||||
|
||||
Fix all missing or unstable behavior.
|
||||
|
||||
C.3 Prevent Duplicate Publishing
|
||||
|
||||
Ensure:
|
||||
|
||||
Content with external_id cannot publish again
|
||||
|
||||
Instead, show “View on WordPress” action
|
||||
|
||||
Add frontend guard to disable publish
|
||||
|
||||
Add backend guard to return 400 “Already published”
|
||||
|
||||
🔷 PART D — CLUSTER DETAIL PAGE INTEGRATION
|
||||
|
||||
Ensure cluster detail page:
|
||||
|
||||
fetches content by cluster
|
||||
|
||||
supports:
|
||||
|
||||
articles
|
||||
|
||||
pages
|
||||
|
||||
products
|
||||
|
||||
taxonomy archive items
|
||||
|
||||
uses final backend fields
|
||||
|
||||
links items to Content Manager
|
||||
|
||||
supports filters
|
||||
|
||||
Fix any broken integration.
|
||||
|
||||
🔷 PART E — SITES MODULE PIPELINE
|
||||
|
||||
Stage 3 ensures the Sites module is fully integrated:
|
||||
|
||||
E.1 Site → Planner Link
|
||||
|
||||
Clusters display only for selected site.
|
||||
Ideas feed into tasks for the active site.
|
||||
|
||||
E.2 Site → Writer Link
|
||||
|
||||
Writer tasks must be per-site
|
||||
(Different sites should not mix content.)
|
||||
|
||||
E.3 Site → Content Manager Link
|
||||
|
||||
Content Manager must only load content for selected site.
|
||||
|
||||
E.4 Site → WordPress Credentials
|
||||
|
||||
Ensure publish + sync functions use the active site’s credentials.
|
||||
|
||||
🔷 PART F — STATUS SYSTEM (FINAL CLEANUP)
|
||||
|
||||
The final statuses MUST be:
|
||||
|
||||
F.1 Content Status
|
||||
|
||||
draft
|
||||
|
||||
published
|
||||
|
||||
F.2 Task Status
|
||||
|
||||
queued
|
||||
|
||||
completed
|
||||
|
||||
F.3 Source
|
||||
|
||||
igny8
|
||||
|
||||
wordpress
|
||||
|
||||
Ensure:
|
||||
|
||||
No legacy statuses appear anywhere in the frontend or backend.
|
||||
|
||||
🔷 PART G — PERFORMANCE & RELIABILITY CHECKS
|
||||
|
||||
Implement:
|
||||
|
||||
Pagination improvements
|
||||
|
||||
Loading states
|
||||
|
||||
Error messages
|
||||
|
||||
Retry messages
|
||||
|
||||
Graceful handling of WP network issues
|
||||
|
||||
Handling slow Writer/AI operations
|
||||
|
||||
Prevent double actions (double publish, double sync)
|
||||
|
||||
Full test run across pipeline
|
||||
|
||||
🔷 PART H — STAGE 3 DOCUMENTATION UPDATE
|
||||
|
||||
Update main docs:
|
||||
|
||||
Full pipeline workflow
|
||||
|
||||
Sequence diagrams
|
||||
|
||||
Final UI screenshots
|
||||
|
||||
API interaction diagrams
|
||||
|
||||
All user flows (Planner → Writer → Content → Publish)
|
||||
|
||||
🔷 PART I — CHANGELOG UPDATE
|
||||
|
||||
Append:
|
||||
|
||||
[2025-11-XX] IGNY8 Stage 3 — Full System Pipeline Complete
|
||||
- Completed end-to-end workflow integration
|
||||
- Fully functional Content Manager with editing, cluster/taxonomy assignment, publishing
|
||||
- Verified WordPress import + publish flows
|
||||
- Added frontend guards against double publish
|
||||
- Unified content source + status logic
|
||||
- Cleaned all final inconsistencies across Planner → Writer → Content Manager
|
||||
- IGNY8 is production-ready with complete pipeline
|
||||
|
||||
🔥 FINAL EXECUTION INSTRUCTIONS (AGENT)
|
||||
|
||||
You MUST:
|
||||
|
||||
Fix all pipeline gaps
|
||||
|
||||
Update all frontend integration points
|
||||
|
||||
Refine all WordPress flows
|
||||
|
||||
Verify all status transitions
|
||||
|
||||
Confirm API compatibility with Stage 1 backend
|
||||
|
||||
Produce all updated code files
|
||||
|
||||
Update documentation
|
||||
|
||||
Update changelog
|
||||
|
||||
Provide a final summary of:
|
||||
|
||||
All updated pages
|
||||
|
||||
All updated components
|
||||
|
||||
All updated stores
|
||||
|
||||
All updated hooks
|
||||
|
||||
Pipeline fixes
|
||||
|
||||
WordPress integration fixes
|
||||
|
||||
Begin Stage 3 execution now.
|
||||
|
||||
---
|
||||
|
||||
## 📋 REMAINING WORK CHECKLIST
|
||||
|
||||
### 🟡 In Progress
|
||||
- [ ] **A.2 Writer → Content Flow** - Verify content generation and storage
|
||||
- [ ] **B.1-B.5 Content Manager Finalization** - Make it single source of truth
|
||||
- [ ] **C.1 WordPress Sync (WP → IGNY8)** - Import flow verification
|
||||
- [ ] **C.2 WordPress Publish (IGNY8 → WP)** - Publish flow implementation
|
||||
- [ ] **C.3 Prevent Duplicate Publishing** - Frontend and backend guards
|
||||
- [ ] **D Cluster Detail Page Integration** - Content filtering and display
|
||||
- [ ] **E.1-E.4 Sites Module Pipeline** - Per-site content isolation
|
||||
- [ ] **G Performance & Reliability** - Loading states, error handling, pagination
|
||||
- [ ] **H Documentation Update** - Workflow diagrams, API interactions
|
||||
- [ ] **I Changelog Update** - Stage 3 completion entry
|
||||
|
||||
### ✅ Completed
|
||||
- [x] **A.1 Planner → Task Flow** - Verified in Stage 1 & 2
|
||||
- [x] **F Status System** - Cleaned in Stage 2
|
||||
- [x] **Content Taxonomy API** - fetchTaxonomies() implemented (Nov 25, 2025)
|
||||
- [x] **Writer Dashboard Taxonomy Integration** - Real data displayed (Nov 25, 2025)
|
||||
|
||||
### 🔧 Next Priority Items
|
||||
1. **Writer → Content Flow (A.2)** - Verify AI generation creates proper Content rows
|
||||
2. **Content Manager (B.1-B.5)** - Implement editing, cluster/taxonomy assignment
|
||||
3. **WordPress Publish (C.2)** - Implement publish button and API integration
|
||||
4. **Cluster Detail Integration (D)** - Connect to real content data
|
||||
|
||||
---
|
||||
|
||||
## 📝 Implementation Notes
|
||||
|
||||
### Taxonomy Integration Details
|
||||
- Backend endpoint: `/v1/writer/taxonomies/`
|
||||
- Supports types: category, tag, product_category, product_tag, product_attribute, cluster
|
||||
- Auto-filters by active site and sector
|
||||
- Pagination supported (default: 10, max: 100)
|
||||
- Search by name, slug, description
|
||||
- Ordering by name, taxonomy_type, count, created_at
|
||||
|
||||
### Known Limitations
|
||||
- Attribute management UI not yet implemented (can use taxonomy UI with type filter)
|
||||
- No bulk taxonomy operations yet
|
||||
- WordPress taxonomy sync not yet tested
|
||||
- Taxonomy assignment UI in Content Editor pending
|
||||
|
||||
---
|
||||
|
||||
**Next Session: Focus on A.2 (Writer → Content Flow) and B.1 (Content Manager loads all content types)**
|
||||
@@ -1,510 +0,0 @@
|
||||
# STAGE 3 IMPLEMENTATION — COMPLETE
|
||||
|
||||
**Date:** November 26, 2025
|
||||
**Developer:** AI Agent (Claude Sonnet 4.5)
|
||||
**Status:** ✅ **100% COMPLETE** (All Core Pipeline Features Functional)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 OBJECTIVE — ACHIEVED ✅
|
||||
|
||||
Stage 3 successfully completed all requirements from `STAGE_3_PLAN.md`:
|
||||
- ✅ Complete end-to-end workflow: Planner → Writer → Content Manager → Publish → WordPress
|
||||
- ✅ All components use final Stage 1 schema
|
||||
- ✅ Status transitions verified and working correctly
|
||||
- ✅ Full-scale SEO workflows enabled
|
||||
- ✅ Bidirectional WordPress sync functional
|
||||
- ✅ Sites module auto-filtering implemented
|
||||
- ✅ Cluster Detail page integrated
|
||||
|
||||
-----
|
||||
|
||||
## ✅ COMPLETED WORK — ALL STAGE 3 PARTS (100%)
|
||||
|
||||
### Overview: 9 Files Modified (5 Backend + 5 Frontend)
|
||||
|
||||
| Part | Description | Status |
|
||||
|------|-------------|--------|
|
||||
| A | Planner → Task Flow | ✅ 100% |
|
||||
| B | Content Manager Finalization | ✅ 100% |
|
||||
| C | WordPress Integration | ✅ 100% |
|
||||
| D | Cluster Detail Page | ✅ 100% |
|
||||
| E | Sites Module Pipeline | ✅ 100% |
|
||||
| F | Status System Cleanup | ✅ 100% |
|
||||
| G | Performance & Reliability | ✅ Basic (Advanced deferred) |
|
||||
| H | Documentation | ✅ 100% |
|
||||
| I | Changelog | ✅ 100% |
|
||||
|
||||
### 1. **Ideas → Tasks Creation Flow** ✅
|
||||
**File:** `backend/igny8_core/modules/planner/views.py`
|
||||
|
||||
Fixed the `bulk_queue_to_writer` action to properly map ContentIdea fields to the final Task schema:
|
||||
|
||||
**Before (Broken):**
|
||||
```python
|
||||
task = Tasks.objects.create(
|
||||
keywords=idea.target_keywords, # CharField - DEPRECATED
|
||||
entity_type=idea.site_entity_type, # REMOVED FIELD
|
||||
cluster_role=idea.cluster_role, # REMOVED FIELD
|
||||
taxonomy=idea.taxonomy, # Wrong FK name
|
||||
idea=idea, # OneToOne removed
|
||||
)
|
||||
```
|
||||
|
||||
**After (Fixed):**
|
||||
```python
|
||||
# Map fields correctly
|
||||
content_type = idea.site_entity_type or 'post'
|
||||
role_to_structure = {'hub': 'article', 'supporting': 'guide', 'attribute': 'comparison'}
|
||||
content_structure = role_to_structure.get(idea.cluster_role, 'article')
|
||||
|
||||
task = Tasks.objects.create(
|
||||
title=idea.idea_title,
|
||||
description=idea.description,
|
||||
cluster=idea.keyword_cluster,
|
||||
content_type=content_type,
|
||||
content_structure=content_structure,
|
||||
taxonomy_term=None,
|
||||
status='queued',
|
||||
)
|
||||
task.keywords.set(idea.keyword_objects.all()) # M2M relationship
|
||||
```
|
||||
|
||||
**Impact:** Ideas can now be properly promoted to Writer tasks without errors.
|
||||
|
||||
---
|
||||
|
||||
### 2. **AI Content Generation** ✅
|
||||
**File:** `backend/igny8_core/ai/functions/generate_content.py`
|
||||
|
||||
**CRITICAL FIX:** Completely rewrote the content creation logic to use the Stage 1 final schema.
|
||||
|
||||
**Before (Broken):**
|
||||
- Created `TaskContent` (deprecated OneToOne model)
|
||||
- Used `html_content` field (wrong name)
|
||||
- Referenced `task.idea`, `task.taxonomy`, `task.keyword_objects` (removed/renamed)
|
||||
- Saved SEO fields like `meta_title`, `primary_keyword` (removed fields)
|
||||
- Updated Task but kept status as-is
|
||||
|
||||
**After (Fixed):**
|
||||
```python
|
||||
def save_output(...):
|
||||
# Create independent Content record
|
||||
content_record = Content.objects.create(
|
||||
title=title,
|
||||
content_html=content_html, # Correct field name
|
||||
cluster=task.cluster,
|
||||
content_type=task.content_type,
|
||||
content_structure=task.content_structure,
|
||||
source='igny8',
|
||||
status='draft',
|
||||
account=task.account,
|
||||
site=task.site,
|
||||
sector=task.sector,
|
||||
)
|
||||
|
||||
# Link taxonomy if available
|
||||
if task.taxonomy_term:
|
||||
content_record.taxonomy_terms.add(task.taxonomy_term)
|
||||
|
||||
# Update task status to completed
|
||||
task.status = 'completed'
|
||||
task.save()
|
||||
```
|
||||
|
||||
**Key Changes:**
|
||||
- ✅ Creates independent Content (no OneToOne FK to Task)
|
||||
- ✅ Uses correct field names (`content_html`, `content_type`, `content_structure`)
|
||||
- ✅ Sets `source='igny8'` automatically
|
||||
- ✅ Sets `status='draft'` for new content
|
||||
- ✅ Updates Task status to `completed`
|
||||
- ✅ Removed all deprecated field references
|
||||
|
||||
**Impact:** Writer AI function now correctly creates Content records and updates Task status per Stage 3 requirements.
|
||||
|
||||
---
|
||||
|
||||
### 3. **WordPress Integration** ✅
|
||||
|
||||
#### 3a. WordPress Publishing
|
||||
**File:** `backend/igny8_core/modules/writer/views.py` - `ContentViewSet.publish()`
|
||||
|
||||
Implemented proper WordPress publishing with duplicate prevention and status updates.
|
||||
|
||||
**Before (Broken):**
|
||||
- Placeholder implementation
|
||||
- No duplicate check
|
||||
- Hardcoded fake external_id
|
||||
- No integration with WordPress adapter
|
||||
|
||||
**After (Fixed):**
|
||||
```python
|
||||
@action(detail=True, methods=['post'], url_path='publish')
|
||||
def publish(self, request, pk=None):
|
||||
content = self.get_object()
|
||||
|
||||
# Prevent duplicate publishing
|
||||
if content.external_id:
|
||||
return error_response('Content already published...', 400)
|
||||
|
||||
# Get WP credentials from site metadata
|
||||
site = Site.objects.get(id=site_id)
|
||||
wp_credentials = site.metadata.get('wordpress', {})
|
||||
|
||||
# Use WordPress adapter
|
||||
adapter = WordPressAdapter()
|
||||
result = adapter.publish(content, {
|
||||
'site_url': wp_url,
|
||||
'username': wp_username,
|
||||
'app_password': wp_app_password,
|
||||
'status': 'publish',
|
||||
})
|
||||
|
||||
if result['success']:
|
||||
# Update content with external references
|
||||
content.external_id = result['external_id']
|
||||
content.external_url = result['url']
|
||||
content.status = 'published'
|
||||
content.save()
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- ✅ Duplicate publishing prevention (checks `external_id`)
|
||||
- ✅ Proper error handling with structured responses
|
||||
- ✅ Integration with `WordPressAdapter` service
|
||||
- ✅ Updates `external_id`, `external_url`, `status` on success
|
||||
- ✅ Uses site's WordPress credentials from metadata
|
||||
|
||||
**Impact:** Content can now be published to WordPress without duplicates.
|
||||
|
||||
#### 3b. WordPress Unpublish ✅
|
||||
**File:** `backend/igny8_core/modules/writer/views.py` - `ContentViewSet.unpublish()`
|
||||
|
||||
**Implementation:**
|
||||
```python
|
||||
@action(detail=True, methods=['post'], url_path='unpublish')
|
||||
def unpublish(self, request, pk=None):
|
||||
content = self.get_object()
|
||||
if not content.external_id:
|
||||
return error_response('Content is not published', 400)
|
||||
|
||||
content.external_id = None
|
||||
content.external_url = None
|
||||
content.status = 'draft'
|
||||
content.save()
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- ✅ Validates content is currently published
|
||||
- ✅ Clears external references
|
||||
- ✅ Reverts status to 'draft'
|
||||
|
||||
#### 3c. WordPress Import (WP → IGNY8) ✅
|
||||
**File:** `backend/igny8_core/business/integration/services/content_sync_service.py`
|
||||
|
||||
**Fixed Implementation:**
|
||||
```python
|
||||
content = Content.objects.create(
|
||||
content_html=post.get('content'), # ✅ Correct field name
|
||||
content_type=self._map_wp_post_type(post.get('type')),
|
||||
content_structure='article',
|
||||
source='wordpress', # ✅ Set source correctly
|
||||
status='published' if post['status'] == 'publish' else 'draft',
|
||||
external_id=str(post['id']),
|
||||
external_url=post['link'],
|
||||
)
|
||||
# ✅ Map taxonomies to ContentTaxonomy M2M
|
||||
```
|
||||
|
||||
**Impact:** WordPress posts now import correctly with proper schema compliance.
|
||||
|
||||
#### 3d. WordPress Adapter Update ✅
|
||||
**File:** `backend/igny8_core/business/publishing/services/adapters/wordpress_adapter.py`
|
||||
|
||||
**Change:** Prioritizes `content_html` over deprecated fields:
|
||||
```python
|
||||
content_html = getattr(content, 'content_html', '') or \
|
||||
getattr(content, 'html_content', '') or \
|
||||
getattr(content, 'content', '')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. **PostEditor Refactor** ✅
|
||||
**File:** `frontend/src/pages/Sites/PostEditor.tsx`
|
||||
|
||||
**Changes:**
|
||||
- ✅ Removed deprecated SEO fields (meta_title, meta_description, primary_keyword, secondary_keywords)
|
||||
- ✅ Replaced SEO/Metadata tabs with single "Taxonomy & Cluster" tab
|
||||
- ✅ Shows read-only taxonomy_terms and cluster assignments
|
||||
- ✅ Uses `content_html` consistently (no html_content fallback)
|
||||
- ✅ Updated Content interface to match Stage 1 schema
|
||||
|
||||
**Impact:** Clean, simplified interface focused on core content editing.
|
||||
|
||||
---
|
||||
|
||||
### 5. **Frontend Publish Guards** ✅
|
||||
**Files:** Multiple frontend files
|
||||
|
||||
**Implementation:**
|
||||
- ✅ `api.ts`: Added `publishContent()` and `unpublishContent()` functions
|
||||
- ✅ `table-actions.config.tsx`: Added conditional row actions with `shouldShow` callback
|
||||
- ✅ `TablePageTemplate.tsx`: Filters actions based on `shouldShow(row)`
|
||||
- ✅ `Content.tsx`: Handlers for publish/unpublish/view_on_wordpress actions
|
||||
|
||||
**Features:**
|
||||
- "Publish to WordPress" - only shows when `external_id` is null
|
||||
- "View on WordPress" - only shows when `external_id` exists (opens in new tab)
|
||||
- "Unpublish" - only shows when `external_id` exists
|
||||
- Proper error handling and success messages
|
||||
|
||||
---
|
||||
|
||||
### 6. **Cluster Detail & Sites Module** ✅
|
||||
|
||||
**Cluster Detail Page:**
|
||||
- ✅ Uses Stage 1 schema (content_type, content_structure)
|
||||
- ✅ Links to Content Manager via `/writer/content/{id}`
|
||||
- ✅ Filters content by cluster_id
|
||||
- ✅ Supports tabs for different content types
|
||||
|
||||
**Sites Module Integration:**
|
||||
- ✅ ContentViewSet extends SiteSectorModelViewSet (auto-filters by site)
|
||||
- ✅ Frontend listens to 'siteChanged' events
|
||||
- ✅ WordPress credentials from `site.metadata['wordpress']`
|
||||
|
||||
---
|
||||
|
||||
## ✅ ALL REQUIREMENTS MET (No Remaining Work)
|
||||
|
||||
All Stage 3 requirements have been successfully completed. No remaining work items.
|
||||
|
||||
### Future Enhancements (Deferred to Post-Stage 3)
|
||||
|
||||
**Performance Optimizations (Part G - Advanced):**
|
||||
- Optimistic UI updates
|
||||
- Advanced retry logic for network failures
|
||||
- Request deduplication
|
||||
- Performance monitoring dashboard
|
||||
- Enhanced error recovery
|
||||
|
||||
**Advanced Features:**
|
||||
- Bulk publish operations
|
||||
- Scheduled publishing
|
||||
- Content versioning
|
||||
- A/B testing for content
|
||||
|
||||
**Analytics & Reporting:**
|
||||
- Content performance tracking
|
||||
- WordPress sync status dashboard
|
||||
- Pipeline metrics and insights
|
||||
|
||||
---
|
||||
|
||||
## 📊 TEST SCENARIOS
|
||||
|
||||
### Scenario 1: Full Pipeline Test
|
||||
```
|
||||
1. Planner → Create Idea
|
||||
2. Planner → Queue to Writer (bulk_queue_to_writer)
|
||||
3. Writer → Tasks → Select task
|
||||
4. Writer → Generate Content (calls generate_content AI function)
|
||||
5. Writer → Content Manager → Verify content created (status=draft)
|
||||
6. Writer → Content Manager → Verify task status=completed
|
||||
7. Writer → Content Manager → Publish to WordPress
|
||||
8. Writer → Content Manager → Verify external_id set, status=published
|
||||
9. Try publishing again → Should get error "already published"
|
||||
```
|
||||
|
||||
**Expected Result:** ✅ All steps work correctly
|
||||
|
||||
---
|
||||
|
||||
### Scenario 2: WordPress Import Test
|
||||
```
|
||||
1. WordPress site has existing posts
|
||||
2. IGNY8 → Integration → Sync from WordPress
|
||||
3. Content Manager → Verify imported content
|
||||
- source='wordpress'
|
||||
- external_id set
|
||||
- taxonomy_terms mapped correctly
|
||||
```
|
||||
|
||||
**Expected Result:** ✅ Imports correctly with proper schema
|
||||
|
||||
---
|
||||
|
||||
### Scenario 3: Unpublish Test
|
||||
```
|
||||
1. Select published content (external_id exists)
|
||||
2. Click "Unpublish" action
|
||||
3. Verify external_id and external_url cleared
|
||||
4. Verify status reverted to 'draft'
|
||||
5. Verify "Publish" button reappears
|
||||
```
|
||||
|
||||
**Expected Result:** ✅ Unpublish works correctly
|
||||
|
||||
---
|
||||
|
||||
### Scenario 4: Conditional UI Test
|
||||
```
|
||||
1. View content list with mixed published/draft items
|
||||
2. Draft items show "Publish to WordPress" button
|
||||
3. Published items show "View on WordPress" and "Unpublish" buttons
|
||||
4. Click "View on WordPress" opens in new tab
|
||||
```
|
||||
|
||||
**Expected Result:** ✅ Conditional actions display correctly
|
||||
|
||||
---
|
||||
|
||||
## 🔧 TECHNICAL NOTES
|
||||
|
||||
### Schema Recap (Stage 1 Final)
|
||||
```python
|
||||
# Task Model
|
||||
class Tasks:
|
||||
title: str
|
||||
description: str
|
||||
cluster: FK(Clusters, required)
|
||||
content_type: str # post, page, product, service, category, tag
|
||||
content_structure: str # article, listicle, guide, comparison, product_page
|
||||
taxonomy_term: FK(ContentTaxonomy, optional)
|
||||
keywords: M2M(Keywords)
|
||||
status: str # queued, completed
|
||||
|
||||
# Content Model (Independent)
|
||||
class Content:
|
||||
title: str
|
||||
content_html: str
|
||||
cluster: FK(Clusters, required)
|
||||
content_type: str
|
||||
content_structure: str
|
||||
taxonomy_terms: M2M(ContentTaxonomy)
|
||||
external_id: str (optional)
|
||||
external_url: str (optional)
|
||||
source: str # igny8, wordpress
|
||||
status: str # draft, published
|
||||
|
||||
# NO OneToOne relationship between Task and Content!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 FILES MODIFIED
|
||||
|
||||
### Backend (5 files)
|
||||
1. `backend/igny8_core/modules/planner/views.py` - Ideas → Tasks creation
|
||||
2. `backend/igny8_core/ai/functions/generate_content.py` - Content generation (complete rewrite)
|
||||
3. `backend/igny8_core/modules/writer/views.py` - Publish/unpublish endpoints
|
||||
4. `backend/igny8_core/business/integration/services/content_sync_service.py` - WordPress import
|
||||
5. `backend/igny8_core/business/publishing/services/adapters/wordpress_adapter.py` - Schema compliance
|
||||
|
||||
### Frontend (5 files)
|
||||
1. `frontend/src/services/api.ts` - Publish/unpublish API functions
|
||||
2. `frontend/src/config/pages/table-actions.config.tsx` - Conditional row actions
|
||||
3. `frontend/src/templates/TablePageTemplate.tsx` - shouldShow filter logic
|
||||
4. `frontend/src/pages/Writer/Content.tsx` - Action handlers
|
||||
5. `frontend/src/pages/Sites/PostEditor.tsx` - Simplified interface
|
||||
|
||||
### Documentation (3 files)
|
||||
1. `STAGE_3_PROGRESS.md` - Comprehensive progress tracking (to be removed)
|
||||
2. `CHANGELOG.md` - Stage 3 completion summary
|
||||
3. `STAGE_3_SUMMARY.md` - This file (to be renamed to STAGE_3_COMPLETE.md)
|
||||
|
||||
**Total:** 13 files modified/created
|
||||
|
||||
---
|
||||
|
||||
## 🚀 PRODUCTION DEPLOYMENT STEPS
|
||||
|
||||
### Immediate Next Steps
|
||||
1. **Deploy to Staging Environment**
|
||||
- Set up staging server with WordPress test site
|
||||
- Configure environment variables
|
||||
- Run database migrations
|
||||
- Test all endpoints
|
||||
|
||||
2. **End-to-End Testing**
|
||||
- Test full pipeline: Idea → Task → Content → Publish
|
||||
- Test WordPress import from real WP site
|
||||
- Test publish/unpublish cycles
|
||||
- Verify all status transitions
|
||||
|
||||
3. **User Documentation**
|
||||
- Create user guides for each module
|
||||
- Record video tutorials for key workflows
|
||||
- Document API endpoints
|
||||
- Create troubleshooting guide
|
||||
|
||||
### Future Enhancements (Post-Production)
|
||||
4. **Performance Optimization**
|
||||
- Implement optimistic UI updates
|
||||
- Add advanced retry logic
|
||||
- Set up monitoring dashboard
|
||||
- Performance profiling
|
||||
|
||||
5. **Advanced Features**
|
||||
- Bulk operations
|
||||
- Scheduled publishing
|
||||
- Content versioning
|
||||
- Analytics and reporting
|
||||
|
||||
---
|
||||
|
||||
## 💡 KEY INSIGHTS
|
||||
|
||||
### What Worked Well
|
||||
- Stage 1 migrations were solid - no schema changes needed
|
||||
- Clear separation between Task and Content models
|
||||
- WordPress adapter pattern is clean and extensible
|
||||
|
||||
### Challenges Encountered
|
||||
- Many deprecated field references scattered across codebase
|
||||
- AI function had deeply embedded old schema assumptions
|
||||
- Integration service was written before Stage 1 refactor
|
||||
|
||||
### Lessons Learned
|
||||
- Always search codebase for field references before "finalizing" schema
|
||||
- AI functions need careful review after model changes
|
||||
- Test E2E pipeline early to catch integration issues
|
||||
|
||||
---
|
||||
|
||||
**Completion Date:** November 26, 2025
|
||||
**Status:** ✅ **100% COMPLETE** - All core pipeline features functional and production-ready
|
||||
**Next Milestone:** Production deployment and monitoring
|
||||
|
||||
---
|
||||
|
||||
## 🎉 STAGE 3 ACHIEVEMENTS
|
||||
|
||||
### Core Pipeline ✅
|
||||
- End-to-end workflow fully functional
|
||||
- Bidirectional WordPress sync working
|
||||
- Complete Stage 1 schema compliance
|
||||
- All status transitions verified
|
||||
|
||||
### Integration ✅
|
||||
- WordPress publish/unpublish working
|
||||
- Duplicate prevention implemented
|
||||
- Conditional UI based on publish state
|
||||
- Sites module auto-filtering functional
|
||||
|
||||
### User Experience ✅
|
||||
- Simplified PostEditor interface
|
||||
- Smart action button visibility
|
||||
- Proper error handling and messaging
|
||||
- Loading states implemented
|
||||
|
||||
### Code Quality ✅
|
||||
- All deprecated fields removed
|
||||
- Clean separation of concerns
|
||||
- Comprehensive documentation
|
||||
- Production-ready codebase
|
||||
|
||||
---
|
||||
|
||||
See `CHANGELOG.md` for detailed release notes and `STAGE_3_PLAN.md` for original requirements.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,406 +0,0 @@
|
||||
# Admin & Views Update Summary
|
||||
|
||||
**Date**: November 21, 2025
|
||||
**Status**: ✅ **COMPLETED**
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Updated all Django admin interfaces, API views, filters, and serializers to use the new unified content architecture.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Writer Module Updates
|
||||
|
||||
### Admin (`igny8_core/modules/writer/admin.py`)
|
||||
|
||||
#### 1. **TasksAdmin** - Simplified & Deprecated Fields Marked
|
||||
```python
|
||||
list_display = ['title', 'site', 'sector', 'status', 'cluster', 'created_at']
|
||||
list_filter = ['status', 'site', 'sector', 'cluster']
|
||||
readonly_fields = ['content_type', 'content_structure', 'entity_type', 'cluster_role', 'assigned_post_id', 'post_url']
|
||||
```
|
||||
|
||||
**Changes:**
|
||||
- Removed `content_type` and `word_count` from list display
|
||||
- Added fieldsets with "Deprecated Fields" section (collapsed)
|
||||
- Marked 6 deprecated fields as read-only
|
||||
|
||||
#### 2. **ContentAdmin** - Enhanced with New Structure
|
||||
```python
|
||||
list_display = ['title', 'entity_type', 'content_format', 'cluster_role', 'site', 'sector', 'source', 'sync_status', 'word_count', 'generated_at']
|
||||
list_filter = ['entity_type', 'content_format', 'cluster_role', 'source', 'sync_status', 'status', 'site', 'sector', 'generated_at']
|
||||
filter_horizontal = ['taxonomies']
|
||||
readonly_fields = ['categories', 'tags']
|
||||
```
|
||||
|
||||
**Changes:**
|
||||
- Added `entity_type`, `content_format`, `cluster_role` to list display
|
||||
- Added `source`, `sync_status` filters
|
||||
- Added `taxonomies` M2M widget (filter_horizontal)
|
||||
- Organized into 7 fieldsets:
|
||||
- Basic Info
|
||||
- Content Classification
|
||||
- Content
|
||||
- SEO
|
||||
- Taxonomies & Attributes
|
||||
- WordPress Sync
|
||||
- Optimization
|
||||
- Deprecated Fields (collapsed)
|
||||
|
||||
#### 3. **ContentTaxonomyAdmin** - NEW
|
||||
```python
|
||||
list_display = ['name', 'taxonomy_type', 'slug', 'parent', 'external_id', 'external_taxonomy', 'sync_status', 'count', 'site', 'sector']
|
||||
list_filter = ['taxonomy_type', 'sync_status', 'site', 'sector', 'parent']
|
||||
filter_horizontal = ['clusters']
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Full CRUD for categories, tags, product attributes
|
||||
- WordPress sync fields visible
|
||||
- Semantic cluster mapping via M2M widget
|
||||
- Hierarchical taxonomy support (parent field)
|
||||
|
||||
#### 4. **ContentAttributeAdmin** - NEW
|
||||
```python
|
||||
list_display = ['name', 'value', 'attribute_type', 'content', 'cluster', 'external_id', 'source', 'site', 'sector']
|
||||
list_filter = ['attribute_type', 'source', 'site', 'sector']
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Product specs, service modifiers, semantic facets
|
||||
- WordPress/WooCommerce sync fields
|
||||
- Link to content or cluster
|
||||
|
||||
---
|
||||
|
||||
### Views (`igny8_core/modules/writer/views.py`)
|
||||
|
||||
#### 1. **TasksViewSet** - Simplified Filters
|
||||
```python
|
||||
filterset_fields = ['status', 'cluster_id'] # Removed deprecated fields
|
||||
```
|
||||
|
||||
#### 2. **ContentViewSet** - Enhanced Filters
|
||||
```python
|
||||
queryset = Content.objects.select_related('task', 'site', 'sector', 'cluster').prefetch_related('taxonomies', 'attributes')
|
||||
filterset_fields = [
|
||||
'task_id',
|
||||
'status',
|
||||
'entity_type', # NEW
|
||||
'content_format', # NEW
|
||||
'cluster_role', # NEW
|
||||
'source', # NEW
|
||||
'sync_status', # NEW
|
||||
'cluster',
|
||||
'external_type', # NEW
|
||||
]
|
||||
search_fields = ['title', 'meta_title', 'primary_keyword', 'external_url'] # Added external_url
|
||||
ordering_fields = ['generated_at', 'updated_at', 'word_count', 'status', 'entity_type', 'content_format']
|
||||
```
|
||||
|
||||
**Changes:**
|
||||
- Added 5 new filter fields for unified structure
|
||||
- Optimized queryset with select_related & prefetch_related
|
||||
- Added external_url to search fields
|
||||
|
||||
#### 3. **ContentTaxonomyViewSet** - NEW
|
||||
```python
|
||||
Endpoint: /api/v1/writer/taxonomies/
|
||||
Methods: GET, POST, PUT, PATCH, DELETE
|
||||
|
||||
filterset_fields = ['taxonomy_type', 'sync_status', 'parent', 'external_id', 'external_taxonomy']
|
||||
search_fields = ['name', 'slug', 'description', 'external_taxonomy']
|
||||
ordering = ['taxonomy_type', 'name']
|
||||
```
|
||||
|
||||
**Custom Actions:**
|
||||
- `POST /api/v1/writer/taxonomies/{id}/map_to_cluster/` - Map taxonomy to semantic cluster
|
||||
- `GET /api/v1/writer/taxonomies/{id}/contents/` - Get all content for taxonomy
|
||||
|
||||
#### 4. **ContentAttributeViewSet** - NEW
|
||||
```python
|
||||
Endpoint: /api/v1/writer/attributes/
|
||||
Methods: GET, POST, PUT, PATCH, DELETE
|
||||
|
||||
filterset_fields = ['attribute_type', 'source', 'content', 'cluster', 'external_id']
|
||||
search_fields = ['name', 'value', 'external_attribute_name', 'content__title']
|
||||
ordering = ['attribute_type', 'name']
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### URLs (`igny8_core/modules/writer/urls.py`)
|
||||
|
||||
**New Routes Added:**
|
||||
```python
|
||||
router.register(r'taxonomies', ContentTaxonomyViewSet, basename='taxonomy')
|
||||
router.register(r'attributes', ContentAttributeViewSet, basename='attribute')
|
||||
```
|
||||
|
||||
**Available Endpoints:**
|
||||
- `/api/v1/writer/tasks/`
|
||||
- `/api/v1/writer/images/`
|
||||
- `/api/v1/writer/content/`
|
||||
- `/api/v1/writer/taxonomies/` ✨ NEW
|
||||
- `/api/v1/writer/attributes/` ✨ NEW
|
||||
|
||||
---
|
||||
|
||||
## ✅ Planner Module Updates
|
||||
|
||||
### Admin (`igny8_core/modules/planner/admin.py`)
|
||||
|
||||
#### **ContentIdeasAdmin** - Updated for New Structure
|
||||
```python
|
||||
list_display = ['idea_title', 'site', 'sector', 'description_preview', 'site_entity_type', 'cluster_role', 'status', 'keyword_cluster', 'estimated_word_count', 'created_at']
|
||||
list_filter = ['status', 'site_entity_type', 'cluster_role', 'site', 'sector']
|
||||
readonly_fields = ['content_structure', 'content_type']
|
||||
```
|
||||
|
||||
**Changes:**
|
||||
- Replaced `content_structure`, `content_type` with `site_entity_type`, `cluster_role` in display
|
||||
- Marked old fields as read-only in collapsed fieldset
|
||||
- Updated filters to use new fields
|
||||
|
||||
**Fieldsets:**
|
||||
- Basic Info
|
||||
- Content Planning (site_entity_type, cluster_role)
|
||||
- Keywords & Clustering
|
||||
- Deprecated Fields (collapsed)
|
||||
|
||||
---
|
||||
|
||||
### Views (`igny8_core/modules/planner/views.py`)
|
||||
|
||||
#### **ContentIdeasViewSet** - Updated Filters
|
||||
```python
|
||||
filterset_fields = ['status', 'keyword_cluster_id', 'site_entity_type', 'cluster_role'] # Updated
|
||||
```
|
||||
|
||||
**Changes:**
|
||||
- Replaced `content_structure`, `content_type` with `site_entity_type`, `cluster_role`
|
||||
|
||||
---
|
||||
|
||||
## 📊 New API Endpoints Summary
|
||||
|
||||
### Writer Taxonomies
|
||||
```bash
|
||||
GET /api/v1/writer/taxonomies/ # List all taxonomies
|
||||
POST /api/v1/writer/taxonomies/ # Create taxonomy
|
||||
GET /api/v1/writer/taxonomies/{id}/ # Get taxonomy
|
||||
PUT /api/v1/writer/taxonomies/{id}/ # Update taxonomy
|
||||
DELETE /api/v1/writer/taxonomies/{id}/ # Delete taxonomy
|
||||
POST /api/v1/writer/taxonomies/{id}/map_to_cluster/ # Map to cluster
|
||||
GET /api/v1/writer/taxonomies/{id}/contents/ # Get taxonomy contents
|
||||
```
|
||||
|
||||
**Filters:**
|
||||
- `?taxonomy_type=category` (category, tag, product_cat, product_tag, product_attr, service_cat)
|
||||
- `?sync_status=imported` (native, imported, synced)
|
||||
- `?parent=5` (hierarchical filtering)
|
||||
- `?external_id=12` (WP term ID)
|
||||
- `?external_taxonomy=category` (WP taxonomy name)
|
||||
|
||||
**Search:**
|
||||
- `?search=SEO` (searches name, slug, description)
|
||||
|
||||
---
|
||||
|
||||
### Writer Attributes
|
||||
```bash
|
||||
GET /api/v1/writer/attributes/ # List all attributes
|
||||
POST /api/v1/writer/attributes/ # Create attribute
|
||||
GET /api/v1/writer/attributes/{id}/ # Get attribute
|
||||
PUT /api/v1/writer/attributes/{id}/ # Update attribute
|
||||
DELETE /api/v1/writer/attributes/{id}/ # Delete attribute
|
||||
```
|
||||
|
||||
**Filters:**
|
||||
- `?attribute_type=product_spec` (product_spec, service_modifier, semantic_facet)
|
||||
- `?source=wordpress` (blueprint, manual, import, wordpress)
|
||||
- `?content=42` (filter by content ID)
|
||||
- `?cluster=8` (filter by cluster ID)
|
||||
- `?external_id=101` (WP attribute term ID)
|
||||
|
||||
**Search:**
|
||||
- `?search=Color` (searches name, value, external_attribute_name, content title)
|
||||
|
||||
---
|
||||
|
||||
### Enhanced Content Filters
|
||||
```bash
|
||||
GET /api/v1/writer/content/?entity_type=post
|
||||
GET /api/v1/writer/content/?content_format=listicle
|
||||
GET /api/v1/writer/content/?cluster_role=hub
|
||||
GET /api/v1/writer/content/?source=wordpress
|
||||
GET /api/v1/writer/content/?sync_status=imported
|
||||
GET /api/v1/writer/content/?external_type=product
|
||||
GET /api/v1/writer/content/?search=seo+tools
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Backward Compatibility
|
||||
|
||||
### Deprecated Fields Still Work
|
||||
|
||||
**Tasks:**
|
||||
- `content_type`, `content_structure` → Read-only in admin
|
||||
- Still in database, marked with help text
|
||||
|
||||
**Content:**
|
||||
- `categories`, `tags` (JSON) → Read-only in admin
|
||||
- Data migrated to `taxonomies` M2M
|
||||
- Old fields preserved for transition period
|
||||
|
||||
**ContentIdeas:**
|
||||
- `content_structure`, `content_type` → Read-only in admin
|
||||
- Replaced by `site_entity_type`, `cluster_role`
|
||||
|
||||
---
|
||||
|
||||
## 📝 Django Admin Features
|
||||
|
||||
### New Admin Capabilities
|
||||
|
||||
1. **Content Taxonomy Management**
|
||||
- Create/edit categories, tags, product attributes
|
||||
- Map to semantic clusters (M2M widget)
|
||||
- View WordPress sync status
|
||||
- Hierarchical taxonomy support
|
||||
|
||||
2. **Content Attribute Management**
|
||||
- Create product specs (Color: Blue, Size: Large)
|
||||
- Create service modifiers (Location: NYC)
|
||||
- Create semantic facets (Target Audience: Enterprise)
|
||||
- Link to content or clusters
|
||||
|
||||
3. **Enhanced Content Admin**
|
||||
- Filter by entity_type, content_format, cluster_role
|
||||
- Filter by source (igny8, wordpress, shopify)
|
||||
- Filter by sync_status (native, imported, synced)
|
||||
- Assign taxonomies via M2M widget
|
||||
- View WordPress sync metadata
|
||||
|
||||
4. **Simplified Task Admin**
|
||||
- Deprecated fields hidden in collapsed section
|
||||
- Focus on core planning fields
|
||||
- Read-only access to legacy data
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing Checklist
|
||||
|
||||
### Admin Interface
|
||||
- ✅ Tasks admin loads without errors
|
||||
- ✅ Content admin shows new fields
|
||||
- ✅ ContentTaxonomy admin registered
|
||||
- ✅ ContentAttribute admin registered
|
||||
- ✅ ContentIdeas admin updated
|
||||
- ✅ All deprecated fields marked read-only
|
||||
- ✅ Fieldsets organized properly
|
||||
|
||||
### API Endpoints
|
||||
- ✅ `/api/v1/writer/taxonomies/` accessible
|
||||
- ✅ `/api/v1/writer/attributes/` accessible
|
||||
- ✅ Content filters work with new fields
|
||||
- ✅ ContentIdeas filters updated
|
||||
- ✅ No 500 errors on backend restart
|
||||
|
||||
### Database
|
||||
- ✅ All migrations applied
|
||||
- ✅ New tables exist
|
||||
- ✅ New fields in Content table
|
||||
- ✅ M2M relationships functional
|
||||
|
||||
---
|
||||
|
||||
## 📚 Usage Examples
|
||||
|
||||
### Create Taxonomy via API
|
||||
```bash
|
||||
POST /api/v1/writer/taxonomies/
|
||||
{
|
||||
"name": "SEO",
|
||||
"slug": "seo",
|
||||
"taxonomy_type": "category",
|
||||
"description": "All about SEO",
|
||||
"site_id": 5,
|
||||
"sector_id": 3
|
||||
}
|
||||
```
|
||||
|
||||
### Create Product Attribute via API
|
||||
```bash
|
||||
POST /api/v1/writer/attributes/
|
||||
{
|
||||
"name": "Color",
|
||||
"value": "Blue",
|
||||
"attribute_type": "product_spec",
|
||||
"content": 42,
|
||||
"external_id": 101,
|
||||
"external_attribute_name": "pa_color",
|
||||
"source": "wordpress",
|
||||
"site_id": 5,
|
||||
"sector_id": 3
|
||||
}
|
||||
```
|
||||
|
||||
### Filter Content by New Fields
|
||||
```bash
|
||||
GET /api/v1/writer/content/?entity_type=post&content_format=listicle&cluster_role=hub
|
||||
GET /api/v1/writer/content/?source=wordpress&sync_status=imported
|
||||
GET /api/v1/writer/taxonomies/?taxonomy_type=category&sync_status=imported
|
||||
GET /api/v1/writer/attributes/?attribute_type=product_spec&source=wordpress
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Next Steps
|
||||
|
||||
### Ready for Frontend Integration
|
||||
|
||||
1. **Site Settings → Content Types Tab**
|
||||
- Display taxonomies from `/api/v1/writer/taxonomies/`
|
||||
- Show attributes from `/api/v1/writer/attributes/`
|
||||
- Enable/disable sync per type
|
||||
- Set fetch limits
|
||||
|
||||
2. **Content Management**
|
||||
- Filter content by `entity_type`, `content_format`, `cluster_role`
|
||||
- Display WordPress sync status
|
||||
- Show assigned taxonomies
|
||||
- View product attributes
|
||||
|
||||
3. **WordPress Import UI**
|
||||
- Fetch structure from plugin
|
||||
- Create ContentTaxonomy records
|
||||
- Import content titles
|
||||
- Map to clusters
|
||||
|
||||
---
|
||||
|
||||
## ✅ Summary
|
||||
|
||||
**All admin interfaces and API views updated to use unified content architecture.**
|
||||
|
||||
**Changes:**
|
||||
- ✅ 3 new admin classes registered
|
||||
- ✅ 2 new ViewSets added
|
||||
- ✅ 7 new filter fields in Content
|
||||
- ✅ 5 new filter fields in Taxonomies
|
||||
- ✅ 5 new filter fields in Attributes
|
||||
- ✅ All deprecated fields marked read-only
|
||||
- ✅ Backward compatibility maintained
|
||||
- ✅ Backend restart successful
|
||||
- ✅ No linter errors
|
||||
|
||||
**New Endpoints:**
|
||||
- `/api/v1/writer/taxonomies/` (full CRUD + custom actions)
|
||||
- `/api/v1/writer/attributes/` (full CRUD)
|
||||
|
||||
**Status:** Production-ready, fully functional, WordPress integration prepared.
|
||||
|
||||
@@ -1,482 +0,0 @@
|
||||
# ✅ Cleanup Complete - Unified Content Architecture
|
||||
|
||||
**Date**: November 22, 2025
|
||||
**Status**: ✅ **COMPLETE**
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Successfully cleaned up all redundant and deprecated fields from the IGNY8 backend, migrated data to the new unified content architecture, and created a Sites content types interface endpoint.
|
||||
|
||||
---
|
||||
|
||||
## What Was Completed
|
||||
|
||||
### 1. ✅ Removed Deprecated Fields from Models
|
||||
|
||||
**ContentIdeas Model** (`/backend/igny8_core/business/planning/models.py`):
|
||||
- ❌ Removed: `content_structure` (replaced by `cluster_role`)
|
||||
- ❌ Removed: `content_type` (replaced by `site_entity_type`)
|
||||
- ✅ Kept: `site_entity_type` (post, page, product, service, taxonomy_term)
|
||||
- ✅ Kept: `cluster_role` (hub, supporting, attribute)
|
||||
|
||||
**Tasks Model** (`/backend/igny8_core/business/content/models.py`):
|
||||
- ❌ Removed: `content_structure` (replaced by `cluster_role`)
|
||||
- ❌ Removed: `content_type` (replaced by `entity_type`)
|
||||
- ❌ Removed: `content` (moved to Content model)
|
||||
- ❌ Removed: `word_count` (moved to Content model)
|
||||
- ❌ Removed: `meta_title` (moved to Content model)
|
||||
- ❌ Removed: `meta_description` (moved to Content model)
|
||||
- ❌ Removed: `assigned_post_id` (moved to Content model)
|
||||
- ❌ Removed: `post_url` (moved to Content model)
|
||||
- ✅ Kept: `entity_type` (post, page, product, service, taxonomy_term)
|
||||
- ✅ Kept: `cluster_role` (hub, supporting, attribute)
|
||||
|
||||
**Content Model** (`/backend/igny8_core/business/content/models.py`):
|
||||
- ❌ Removed: `categories` (JSON field, replaced by `taxonomies` M2M)
|
||||
- ❌ Removed: `tags` (JSON field, replaced by `taxonomies` M2M)
|
||||
- ✅ Kept: `entity_type` (post, page, product, service, taxonomy_term)
|
||||
- ✅ Kept: `content_format` (article, listicle, guide, comparison, review, roundup)
|
||||
- ✅ Kept: `cluster_role` (hub, supporting, attribute)
|
||||
- ✅ Kept: `taxonomies` (M2M to ContentTaxonomy)
|
||||
|
||||
---
|
||||
|
||||
### 2. ✅ Updated Admin Interfaces
|
||||
|
||||
**ContentIdeas Admin** (`/backend/igny8_core/modules/planner/admin.py`):
|
||||
- Removed deprecated fields from `readonly_fields`
|
||||
- Removed "Deprecated Fields" fieldset
|
||||
- Updated `list_display` to show only new fields
|
||||
- Updated `list_filter` to use only new fields
|
||||
|
||||
**Tasks Admin** (`/backend/igny8_core/modules/writer/admin.py`):
|
||||
- Added `entity_type` and `cluster_role` to `list_display`
|
||||
- Added `entity_type` and `cluster_role` to `list_filter`
|
||||
- Removed deprecated fields from fieldsets
|
||||
- Added "Content Classification" fieldset with new fields
|
||||
|
||||
**Content Admin** (`/backend/igny8_core/modules/writer/admin.py`):
|
||||
- Removed deprecated `categories` and `tags` from `readonly_fields`
|
||||
- Removed "Deprecated Fields" fieldset
|
||||
- All new fields properly displayed and filterable
|
||||
|
||||
---
|
||||
|
||||
### 3. ✅ Updated API Views
|
||||
|
||||
**ContentIdeasViewSet** (`/backend/igny8_core/modules/planner/views.py`):
|
||||
- `filterset_fields`: Uses `site_entity_type` and `cluster_role` (no deprecated fields)
|
||||
|
||||
**TasksViewSet** (`/backend/igny8_core/modules/writer/views.py`):
|
||||
- `filterset_fields`: Added `entity_type`, `cluster_role`
|
||||
- `ordering_fields`: Removed `word_count` (no longer in model)
|
||||
|
||||
**ContentViewSet** (`/backend/igny8_core/modules/writer/views.py`):
|
||||
- Already updated with all new fields
|
||||
- Filters working correctly
|
||||
|
||||
---
|
||||
|
||||
### 4. ✅ Data Migration
|
||||
|
||||
**Migration**: `0006_cleanup_migrate_and_drop_deprecated_fields.py`
|
||||
|
||||
**Data Migration Logic**:
|
||||
- Ensured all `Tasks` have default `entity_type` ('post') and `cluster_role` ('hub')
|
||||
- Ensured all `Content` inherit `entity_type` and `cluster_role` from their related `Task`
|
||||
- Set defaults for any `Content` without a task
|
||||
|
||||
**Database Changes**:
|
||||
- Dropped `content_structure` column from `igny8_content_ideas`
|
||||
- Dropped `content_type` column from `igny8_content_ideas`
|
||||
- Dropped `content_structure` column from `igny8_tasks`
|
||||
- Dropped `content_type` column from `igny8_tasks`
|
||||
- Dropped `content` column from `igny8_tasks`
|
||||
- Dropped `word_count` column from `igny8_tasks`
|
||||
- Dropped `meta_title` column from `igny8_tasks`
|
||||
- Dropped `meta_description` column from `igny8_tasks`
|
||||
- Dropped `assigned_post_id` column from `igny8_tasks`
|
||||
- Dropped `post_url` column from `igny8_tasks`
|
||||
- Dropped `categories` column from `igny8_content`
|
||||
- Dropped `tags` column from `igny8_content`
|
||||
|
||||
---
|
||||
|
||||
### 5. ✅ Created Sites Content Types Interface
|
||||
|
||||
**New Endpoint**: `GET /api/v1/integration/integrations/{id}/content-types/`
|
||||
|
||||
**Purpose**: Show WordPress synced content types with counts
|
||||
|
||||
**Response Format**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"post_types": {
|
||||
"post": {
|
||||
"label": "Posts",
|
||||
"count": 123,
|
||||
"synced_count": 50,
|
||||
"enabled": true,
|
||||
"fetch_limit": 100,
|
||||
"last_synced": "2025-11-22T10:00:00Z"
|
||||
},
|
||||
"page": {
|
||||
"label": "Pages",
|
||||
"count": 12,
|
||||
"synced_count": 12,
|
||||
"enabled": true,
|
||||
"fetch_limit": 50,
|
||||
"last_synced": "2025-11-22T10:00:00Z"
|
||||
},
|
||||
"product": {
|
||||
"label": "Products",
|
||||
"count": 456,
|
||||
"synced_count": 200,
|
||||
"enabled": true,
|
||||
"fetch_limit": 200,
|
||||
"last_synced": null
|
||||
}
|
||||
},
|
||||
"taxonomies": {
|
||||
"category": {
|
||||
"label": "Categories",
|
||||
"count": 25,
|
||||
"synced_count": 25,
|
||||
"enabled": true,
|
||||
"fetch_limit": 100,
|
||||
"last_synced": "2025-11-22T10:00:00Z"
|
||||
},
|
||||
"post_tag": {
|
||||
"label": "Tags",
|
||||
"count": 102,
|
||||
"synced_count": 80,
|
||||
"enabled": true,
|
||||
"fetch_limit": 200,
|
||||
"last_synced": "2025-11-22T10:00:00Z"
|
||||
},
|
||||
"product_cat": {
|
||||
"label": "Product Categories",
|
||||
"count": 15,
|
||||
"synced_count": 15,
|
||||
"enabled": false,
|
||||
"fetch_limit": 50,
|
||||
"last_synced": null
|
||||
}
|
||||
},
|
||||
"last_structure_fetch": "2025-11-22T10:00:00Z",
|
||||
"plugin_connection_enabled": true,
|
||||
"two_way_sync_enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Features**:
|
||||
- Shows WP content type counts from plugin
|
||||
- Shows synced counts from IGNY8 database
|
||||
- Shows enabled/disabled status
|
||||
- Shows fetch limits
|
||||
- Shows last sync timestamps
|
||||
|
||||
---
|
||||
|
||||
## Unified Field Structure
|
||||
|
||||
### Entity Type (Standardized)
|
||||
|
||||
**Field**: `entity_type`
|
||||
**Used In**: ContentIdeas (`site_entity_type`), Tasks, Content
|
||||
|
||||
**Values**:
|
||||
- `post` - Blog posts, articles
|
||||
- `page` - Static pages
|
||||
- `product` - WooCommerce products
|
||||
- `service` - Service pages
|
||||
- `taxonomy_term` - Category/tag pages
|
||||
|
||||
### Content Format (For Posts Only)
|
||||
|
||||
**Field**: `content_format`
|
||||
**Used In**: Content
|
||||
|
||||
**Values**:
|
||||
- `article` - Standard article
|
||||
- `listicle` - List-based content
|
||||
- `guide` - How-to guide
|
||||
- `comparison` - Comparison article
|
||||
- `review` - Product/service review
|
||||
- `roundup` - Roundup/collection
|
||||
|
||||
### Cluster Role
|
||||
|
||||
**Field**: `cluster_role`
|
||||
**Used In**: ContentIdeas, Tasks, Content
|
||||
|
||||
**Values**:
|
||||
- `hub` - Main cluster page
|
||||
- `supporting` - Supporting content
|
||||
- `attribute` - Attribute-focused page
|
||||
|
||||
---
|
||||
|
||||
## Database Schema (Final)
|
||||
|
||||
### igny8_content_ideas
|
||||
```sql
|
||||
- id
|
||||
- idea_title
|
||||
- description
|
||||
- site_entity_type ✅ NEW (replaces content_structure + content_type)
|
||||
- cluster_role ✅ NEW (replaces content_structure)
|
||||
- keyword_cluster_id
|
||||
- taxonomy_id
|
||||
- status
|
||||
- estimated_word_count
|
||||
- site_id, sector_id, account_id
|
||||
- created_at, updated_at
|
||||
```
|
||||
|
||||
### igny8_tasks
|
||||
```sql
|
||||
- id
|
||||
- title
|
||||
- description
|
||||
- keywords
|
||||
- entity_type ✅ NEW (replaces content_type)
|
||||
- cluster_role ✅ NEW (replaces content_structure)
|
||||
- cluster_id
|
||||
- idea_id
|
||||
- taxonomy_id
|
||||
- status
|
||||
- site_id, sector_id, account_id
|
||||
- created_at, updated_at
|
||||
```
|
||||
|
||||
### igny8_content
|
||||
```sql
|
||||
- id
|
||||
- task_id
|
||||
- cluster_id
|
||||
- title
|
||||
- html_content
|
||||
- word_count
|
||||
- entity_type ✅ NEW
|
||||
- content_format ✅ NEW
|
||||
- cluster_role ✅ NEW
|
||||
- external_type (WP post type)
|
||||
- external_id, external_url
|
||||
- source, sync_status
|
||||
- meta_title, meta_description
|
||||
- primary_keyword, secondary_keywords
|
||||
- taxonomies (M2M via ContentTaxonomyRelation) ✅ NEW
|
||||
- site_id, sector_id, account_id
|
||||
- generated_at, updated_at
|
||||
```
|
||||
|
||||
### igny8_content_taxonomies ✅ NEW
|
||||
```sql
|
||||
- id
|
||||
- name, slug
|
||||
- taxonomy_type (category, tag, product_cat, product_tag, product_attr)
|
||||
- parent_id
|
||||
- external_id, external_taxonomy
|
||||
- sync_status
|
||||
- count, description
|
||||
- metadata
|
||||
- site_id, sector_id, account_id
|
||||
- created_at, updated_at
|
||||
```
|
||||
|
||||
### igny8_content_attributes ✅ NEW
|
||||
```sql
|
||||
- id
|
||||
- content_id, task_id, cluster_id
|
||||
- attribute_type (product_spec, service_modifier, semantic_facet)
|
||||
- name, value
|
||||
- source (blueprint, manual, import, wordpress)
|
||||
- metadata
|
||||
- external_id, external_attribute_name
|
||||
- site_id, sector_id, account_id
|
||||
- created_at, updated_at
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints (Updated)
|
||||
|
||||
### Planner Module
|
||||
|
||||
**ContentIdeas**:
|
||||
- `GET /api/v1/planner/ideas/` - List (filters: `status`, `site_entity_type`, `cluster_role`)
|
||||
- `POST /api/v1/planner/ideas/` - Create
|
||||
- `GET /api/v1/planner/ideas/{id}/` - Retrieve
|
||||
- `PATCH /api/v1/planner/ideas/{id}/` - Update
|
||||
- `DELETE /api/v1/planner/ideas/{id}/` - Delete
|
||||
|
||||
### Writer Module
|
||||
|
||||
**Tasks**:
|
||||
- `GET /api/v1/writer/tasks/` - List (filters: `status`, `entity_type`, `cluster_role`, `cluster_id`)
|
||||
- `POST /api/v1/writer/tasks/` - Create
|
||||
- `GET /api/v1/writer/tasks/{id}/` - Retrieve
|
||||
- `PATCH /api/v1/writer/tasks/{id}/` - Update
|
||||
- `DELETE /api/v1/writer/tasks/{id}/` - Delete
|
||||
|
||||
**Content**:
|
||||
- `GET /api/v1/writer/content/` - List (filters: `entity_type`, `content_format`, `cluster_role`, `source`, `sync_status`, `external_type`)
|
||||
- `POST /api/v1/writer/content/` - Create
|
||||
- `GET /api/v1/writer/content/{id}/` - Retrieve
|
||||
- `PATCH /api/v1/writer/content/{id}/` - Update
|
||||
- `DELETE /api/v1/writer/content/{id}/` - Delete
|
||||
|
||||
**ContentTaxonomy** ✅ NEW:
|
||||
- `GET /api/v1/writer/taxonomies/` - List
|
||||
- `POST /api/v1/writer/taxonomies/` - Create
|
||||
- `GET /api/v1/writer/taxonomies/{id}/` - Retrieve
|
||||
- `PATCH /api/v1/writer/taxonomies/{id}/` - Update
|
||||
- `DELETE /api/v1/writer/taxonomies/{id}/` - Delete
|
||||
|
||||
**ContentAttribute** ✅ NEW:
|
||||
- `GET /api/v1/writer/attributes/` - List
|
||||
- `POST /api/v1/writer/attributes/` - Create
|
||||
- `GET /api/v1/writer/attributes/{id}/` - Retrieve
|
||||
- `PATCH /api/v1/writer/attributes/{id}/` - Update
|
||||
- `DELETE /api/v1/writer/attributes/{id}/` - Delete
|
||||
|
||||
### Integration Module ✅ NEW
|
||||
|
||||
**Content Types Summary**:
|
||||
- `GET /api/v1/integration/integrations/{id}/content-types/` - Get synced content types with counts
|
||||
|
||||
---
|
||||
|
||||
## Frontend Integration
|
||||
|
||||
### Sites Settings - Content Types Tab
|
||||
|
||||
**URL**: `/sites/{site_id}/settings` → "Content Types" tab
|
||||
|
||||
**API Call**:
|
||||
```javascript
|
||||
// Get integration for site
|
||||
const integration = await api.get(`/integration/integrations/?site_id=${siteId}&platform=wordpress`);
|
||||
|
||||
// Get content types summary
|
||||
const summary = await api.get(`/integration/integrations/${integration.id}/content-types/`);
|
||||
```
|
||||
|
||||
**Display**:
|
||||
1. **Post Types Section**
|
||||
- Show each post type with label, count, synced count
|
||||
- Enable/disable toggle
|
||||
- Fetch limit input
|
||||
- Last synced timestamp
|
||||
- Sync button
|
||||
|
||||
2. **Taxonomies Section**
|
||||
- Show each taxonomy with label, count, synced count
|
||||
- Enable/disable toggle
|
||||
- Fetch limit input
|
||||
- Last synced timestamp
|
||||
- Sync button
|
||||
|
||||
3. **Actions**
|
||||
- "Fetch Structure" button - Refresh from WordPress
|
||||
- "Sync All" button - Import enabled types
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### ✅ Backend Tests
|
||||
|
||||
- [x] Migrations applied successfully
|
||||
- [x] No deprecated fields in models
|
||||
- [x] Admin interfaces show only new fields
|
||||
- [x] API endpoints return correct data
|
||||
- [x] Filters work with new fields
|
||||
- [x] Content types endpoint returns data
|
||||
- [x] Backend restarted successfully
|
||||
|
||||
### ⏳ Frontend Tests (Pending)
|
||||
|
||||
- [ ] Sites settings page loads
|
||||
- [ ] Content Types tab visible
|
||||
- [ ] Content types summary displays
|
||||
- [ ] Enable/disable toggles work
|
||||
- [ ] Fetch limit inputs work
|
||||
- [ ] Sync buttons trigger API calls
|
||||
- [ ] Counts update after sync
|
||||
|
||||
---
|
||||
|
||||
## Migration Timeline
|
||||
|
||||
| Phase | Description | Status |
|
||||
|-------|-------------|--------|
|
||||
| Phase 1 | Add new models and fields | ✅ Complete |
|
||||
| Phase 2 | Migrate data to new structure | ✅ Complete |
|
||||
| Phase 3 | Mark deprecated fields | ✅ Complete |
|
||||
| Phase 4 | Update admin interfaces | ✅ Complete |
|
||||
| Phase 5 | Update API views | ✅ Complete |
|
||||
| Phase 6 | Migrate data and drop columns | ✅ Complete |
|
||||
| Phase 7 | Create Sites interface endpoint | ✅ Complete |
|
||||
| Phase 8 | Build frontend UI | ⏳ Pending |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Immediate (Backend Complete ✅)
|
||||
1. ✅ All deprecated fields removed
|
||||
2. ✅ All admin interfaces updated
|
||||
3. ✅ All API endpoints updated
|
||||
4. ✅ Data migrated successfully
|
||||
5. ✅ Sites content types endpoint created
|
||||
|
||||
### Soon (Frontend)
|
||||
1. Create "Content Types" tab in Sites Settings
|
||||
2. Display content types summary
|
||||
3. Add enable/disable toggles
|
||||
4. Add fetch limit inputs
|
||||
5. Add sync buttons
|
||||
6. Test end-to-end workflow
|
||||
|
||||
### Later (Advanced Features)
|
||||
1. Implement `IntegrationService.fetch_content_structure()`
|
||||
2. Implement `IntegrationService.import_taxonomies()`
|
||||
3. Implement `IntegrationService.import_content_titles()`
|
||||
4. Add AI semantic mapping for clusters
|
||||
5. Add bulk content optimization
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Status**: ✅ **BACKEND CLEANUP COMPLETE**
|
||||
|
||||
All redundant and deprecated fields have been removed from the backend. The unified content architecture is now fully implemented and operational. The Sites content types interface endpoint is ready for frontend integration.
|
||||
|
||||
**What Changed**:
|
||||
- ❌ Removed 14 deprecated fields across 3 models
|
||||
- ✅ Standardized on `entity_type`, `content_format`, `cluster_role`
|
||||
- ✅ Replaced JSON fields with proper M2M relationships
|
||||
- ✅ Updated all admin interfaces
|
||||
- ✅ Updated all API endpoints
|
||||
- ✅ Created Sites content types summary endpoint
|
||||
|
||||
**Result**: Clean, standardized, production-ready content architecture with WordPress integration support.
|
||||
|
||||
---
|
||||
|
||||
**Completion Time**: ~2 hours
|
||||
**Files Modified**: 12
|
||||
**Migrations Created**: 2
|
||||
**Database Columns Dropped**: 14
|
||||
**New API Endpoints**: 1
|
||||
|
||||
✅ **READY FOR FRONTEND INTEGRATION**
|
||||
|
||||
@@ -1,394 +0,0 @@
|
||||
# ✅ Complete Update Checklist - All Verified
|
||||
|
||||
**Date**: November 21, 2025
|
||||
**Status**: ✅ **ALL COMPLETE & VERIFIED**
|
||||
|
||||
---
|
||||
|
||||
## ✅ Phase 1: Database Migrations
|
||||
|
||||
### Migrations Applied
|
||||
```
|
||||
writer
|
||||
✅ 0001_initial
|
||||
✅ 0002_phase1_add_unified_taxonomy_and_attributes
|
||||
✅ 0003_phase1b_fix_taxonomy_relation
|
||||
✅ 0004_phase2_migrate_data_to_unified_structure
|
||||
✅ 0005_phase3_mark_deprecated_fields
|
||||
|
||||
planner
|
||||
✅ 0001_initial
|
||||
✅ 0002_initial
|
||||
```
|
||||
|
||||
### New Tables Created
|
||||
```sql
|
||||
✅ igny8_content_taxonomy_terms (16 columns, 23 indexes)
|
||||
✅ igny8_content_attributes (16 columns, 15 indexes)
|
||||
✅ igny8_content_taxonomy_relations (4 columns, 3 indexes)
|
||||
✅ igny8_content_taxonomy_terms_clusters (M2M table)
|
||||
```
|
||||
|
||||
### New Fields in Content Table
|
||||
```sql
|
||||
✅ cluster_id (bigint)
|
||||
✅ cluster_role (varchar)
|
||||
✅ content_format (varchar)
|
||||
✅ external_type (varchar)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Phase 2: Models Updated
|
||||
|
||||
### Writer Module (`igny8_core/business/content/models.py`)
|
||||
|
||||
#### Content Model
|
||||
- ✅ Added `content_format` field (article, listicle, guide, comparison, review, roundup)
|
||||
- ✅ Added `cluster_role` field (hub, supporting, attribute)
|
||||
- ✅ Added `external_type` field (WP post type)
|
||||
- ✅ Added `cluster` FK (direct cluster relationship)
|
||||
- ✅ Added `taxonomies` M2M (via ContentTaxonomyRelation)
|
||||
- ✅ Updated `entity_type` choices (post, page, product, service, taxonomy_term)
|
||||
- ✅ Marked `categories` and `tags` as deprecated
|
||||
|
||||
#### ContentTaxonomy Model (NEW)
|
||||
- ✅ Unified taxonomy model created
|
||||
- ✅ Supports categories, tags, product attributes
|
||||
- ✅ WordPress sync fields (external_id, external_taxonomy, sync_status)
|
||||
- ✅ Hierarchical support (parent FK)
|
||||
- ✅ Cluster mapping (M2M to Clusters)
|
||||
- ✅ 23 indexes for performance
|
||||
|
||||
#### ContentAttribute Model (NEW)
|
||||
- ✅ Enhanced from ContentAttributeMap
|
||||
- ✅ Added attribute_type (product_spec, service_modifier, semantic_facet)
|
||||
- ✅ Added WP sync fields (external_id, external_attribute_name)
|
||||
- ✅ Added cluster FK for semantic attributes
|
||||
- ✅ 15 indexes for performance
|
||||
|
||||
#### Tasks Model
|
||||
- ✅ Marked 10 fields as deprecated (help_text updated)
|
||||
- ✅ Fields preserved for backward compatibility
|
||||
|
||||
---
|
||||
|
||||
## ✅ Phase 3: Admin Interfaces Updated
|
||||
|
||||
### Writer Admin (`igny8_core/modules/writer/admin.py`)
|
||||
|
||||
#### TasksAdmin
|
||||
- ✅ Simplified list_display (removed deprecated fields)
|
||||
- ✅ Updated list_filter (removed content_type, content_structure)
|
||||
- ✅ Added fieldsets with "Deprecated Fields" section (collapsed)
|
||||
- ✅ Marked 6 fields as readonly
|
||||
|
||||
#### ContentAdmin
|
||||
- ✅ Added entity_type, content_format, cluster_role to list_display
|
||||
- ✅ Added source, sync_status to list_filter
|
||||
- ✅ Created 7 organized fieldsets
|
||||
- ✅ Removed filter_horizontal for taxonomies (through model issue)
|
||||
- ✅ Marked categories, tags as readonly
|
||||
|
||||
#### ContentTaxonomyAdmin (NEW)
|
||||
- ✅ Full CRUD interface
|
||||
- ✅ List display with all key fields
|
||||
- ✅ Filters: taxonomy_type, sync_status, parent
|
||||
- ✅ Search: name, slug, description
|
||||
- ✅ filter_horizontal for clusters M2M
|
||||
- ✅ 4 organized fieldsets
|
||||
|
||||
#### ContentAttributeAdmin (NEW)
|
||||
- ✅ Full CRUD interface
|
||||
- ✅ List display with all key fields
|
||||
- ✅ Filters: attribute_type, source
|
||||
- ✅ Search: name, value, external_attribute_name
|
||||
- ✅ 3 organized fieldsets
|
||||
|
||||
### Planner Admin (`igny8_core/modules/planner/admin.py`)
|
||||
|
||||
#### ContentIdeasAdmin
|
||||
- ✅ Replaced content_structure, content_type with site_entity_type, cluster_role
|
||||
- ✅ Updated list_display
|
||||
- ✅ Updated list_filter
|
||||
- ✅ Added fieldsets with deprecated fields section
|
||||
- ✅ Marked old fields as readonly
|
||||
|
||||
---
|
||||
|
||||
## ✅ Phase 4: API Views & Serializers Updated
|
||||
|
||||
### Writer Views (`igny8_core/modules/writer/views.py`)
|
||||
|
||||
#### TasksViewSet
|
||||
- ✅ Removed deprecated filters (content_type, content_structure)
|
||||
- ✅ Simplified filterset_fields to ['status', 'cluster_id']
|
||||
|
||||
#### ContentViewSet
|
||||
- ✅ Optimized queryset (select_related, prefetch_related)
|
||||
- ✅ Added 5 new filters: entity_type, content_format, cluster_role, source, sync_status
|
||||
- ✅ Added external_type filter
|
||||
- ✅ Added external_url to search_fields
|
||||
- ✅ Updated ordering_fields
|
||||
|
||||
#### ContentTaxonomyViewSet (NEW)
|
||||
- ✅ Full CRUD endpoints
|
||||
- ✅ Filters: taxonomy_type, sync_status, parent, external_id, external_taxonomy
|
||||
- ✅ Search: name, slug, description
|
||||
- ✅ Custom action: map_to_cluster
|
||||
- ✅ Custom action: contents (get all content for taxonomy)
|
||||
- ✅ Optimized queryset
|
||||
|
||||
#### ContentAttributeViewSet (NEW)
|
||||
- ✅ Full CRUD endpoints
|
||||
- ✅ Filters: attribute_type, source, content, cluster, external_id
|
||||
- ✅ Search: name, value, external_attribute_name
|
||||
- ✅ Optimized queryset
|
||||
|
||||
### Writer Serializers (`igny8_core/modules/writer/serializers.py`)
|
||||
|
||||
#### ContentTaxonomySerializer (NEW)
|
||||
- ✅ All fields exposed
|
||||
- ✅ parent_name computed field
|
||||
- ✅ cluster_names computed field
|
||||
- ✅ content_count computed field
|
||||
|
||||
#### ContentAttributeSerializer (NEW)
|
||||
- ✅ All fields exposed
|
||||
- ✅ content_title computed field
|
||||
- ✅ cluster_name computed field
|
||||
|
||||
#### ContentTaxonomyRelationSerializer (NEW)
|
||||
- ✅ Through model serializer
|
||||
- ✅ content_title, taxonomy_name, taxonomy_type computed fields
|
||||
|
||||
### Planner Views (`igny8_core/modules/planner/views.py`)
|
||||
|
||||
#### ContentIdeasViewSet
|
||||
- ✅ Updated filterset_fields: replaced content_structure, content_type with site_entity_type, cluster_role
|
||||
|
||||
---
|
||||
|
||||
## ✅ Phase 5: URL Routes Updated
|
||||
|
||||
### Writer URLs (`igny8_core/modules/writer/urls.py`)
|
||||
- ✅ Added taxonomies route: `/api/v1/writer/taxonomies/`
|
||||
- ✅ Added attributes route: `/api/v1/writer/attributes/`
|
||||
|
||||
---
|
||||
|
||||
## ✅ Phase 6: Backend Status
|
||||
|
||||
### Server
|
||||
- ✅ Backend restarted successfully
|
||||
- ✅ 4 gunicorn workers running
|
||||
- ✅ No errors in logs
|
||||
- ✅ No linter errors
|
||||
|
||||
### Database
|
||||
- ✅ All migrations applied
|
||||
- ✅ New tables verified
|
||||
- ✅ New fields verified
|
||||
- ✅ M2M relationships functional
|
||||
|
||||
---
|
||||
|
||||
## 📊 Complete Feature Matrix
|
||||
|
||||
### Content Management
|
||||
|
||||
| Feature | Old | New | Status |
|
||||
|---------|-----|-----|--------|
|
||||
| Entity Type | Multiple overlapping fields | Single `entity_type` + `content_format` | ✅ |
|
||||
| Categories/Tags | JSON arrays | M2M ContentTaxonomy | ✅ |
|
||||
| Attributes | ContentAttributeMap | Enhanced ContentAttribute | ✅ |
|
||||
| WP Sync | No support | Full sync fields | ✅ |
|
||||
| Cluster Mapping | Via mapping table | Direct FK + M2M | ✅ |
|
||||
|
||||
### Admin Interfaces
|
||||
|
||||
| Model | List Display | Filters | Fieldsets | Status |
|
||||
|-------|-------------|---------|-----------|--------|
|
||||
| Tasks | Updated | Simplified | 3 sections | ✅ |
|
||||
| Content | Enhanced | 9 filters | 7 sections | ✅ |
|
||||
| ContentTaxonomy | NEW | 5 filters | 4 sections | ✅ |
|
||||
| ContentAttribute | NEW | 4 filters | 3 sections | ✅ |
|
||||
| ContentIdeas | Updated | Updated | 4 sections | ✅ |
|
||||
|
||||
### API Endpoints
|
||||
|
||||
| Endpoint | Methods | Filters | Custom Actions | Status |
|
||||
|----------|---------|---------|----------------|--------|
|
||||
| /writer/tasks/ | CRUD | 2 filters | Multiple | ✅ |
|
||||
| /writer/content/ | CRUD | 9 filters | Multiple | ✅ |
|
||||
| /writer/taxonomies/ | CRUD | 5 filters | 2 actions | ✅ NEW |
|
||||
| /writer/attributes/ | CRUD | 5 filters | - | ✅ NEW |
|
||||
| /planner/ideas/ | CRUD | 4 filters | Multiple | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Verification Tests
|
||||
|
||||
### Database Tests
|
||||
```bash
|
||||
✅ SELECT COUNT(*) FROM igny8_content_taxonomy_terms;
|
||||
✅ SELECT COUNT(*) FROM igny8_content_attributes;
|
||||
✅ SELECT COUNT(*) FROM igny8_content_taxonomy_relations;
|
||||
✅ \d igny8_content (verify new columns exist)
|
||||
```
|
||||
|
||||
### Admin Tests
|
||||
```bash
|
||||
✅ Access /admin/writer/tasks/ - loads without errors
|
||||
✅ Access /admin/writer/content/ - shows new filters
|
||||
✅ Access /admin/writer/contenttaxonomy/ - NEW admin works
|
||||
✅ Access /admin/writer/contentattribute/ - NEW admin works
|
||||
✅ Access /admin/planner/contentideas/ - updated fields visible
|
||||
```
|
||||
|
||||
### API Tests
|
||||
```bash
|
||||
✅ GET /api/v1/writer/tasks/ - returns data
|
||||
✅ GET /api/v1/writer/content/?entity_type=post - filters work
|
||||
✅ GET /api/v1/writer/taxonomies/ - NEW endpoint accessible
|
||||
✅ GET /api/v1/writer/attributes/ - NEW endpoint accessible
|
||||
✅ GET /api/v1/planner/ideas/?site_entity_type=post - filters work
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Updated Files Summary
|
||||
|
||||
### Models
|
||||
- ✅ `igny8_core/business/content/models.py` (3 new models, enhanced Content)
|
||||
|
||||
### Admin
|
||||
- ✅ `igny8_core/modules/writer/admin.py` (4 admin classes updated/added)
|
||||
- ✅ `igny8_core/modules/planner/admin.py` (1 admin class updated)
|
||||
|
||||
### Views
|
||||
- ✅ `igny8_core/modules/writer/views.py` (4 ViewSets updated/added)
|
||||
- ✅ `igny8_core/modules/planner/views.py` (1 ViewSet updated)
|
||||
|
||||
### Serializers
|
||||
- ✅ `igny8_core/modules/writer/serializers.py` (3 new serializers added)
|
||||
|
||||
### URLs
|
||||
- ✅ `igny8_core/modules/writer/urls.py` (2 new routes added)
|
||||
|
||||
### Migrations
|
||||
- ✅ 5 new migration files created and applied
|
||||
|
||||
---
|
||||
|
||||
## 🎯 What's Now Available
|
||||
|
||||
### For Developers
|
||||
1. ✅ Unified content entity system (entity_type + content_format)
|
||||
2. ✅ Real taxonomy relationships (not JSON)
|
||||
3. ✅ Enhanced attribute system with WP sync
|
||||
4. ✅ Direct cluster relationships
|
||||
5. ✅ Full CRUD APIs for all new models
|
||||
6. ✅ Comprehensive admin interfaces
|
||||
|
||||
### For WordPress Integration
|
||||
1. ✅ ContentTaxonomy model ready for WP terms
|
||||
2. ✅ ContentAttribute model ready for WooCommerce attributes
|
||||
3. ✅ Content model has all WP sync fields
|
||||
4. ✅ API endpoints ready for import/sync
|
||||
5. ✅ Semantic cluster mapping ready
|
||||
|
||||
### For Frontend
|
||||
1. ✅ New filter options for content (entity_type, content_format, cluster_role)
|
||||
2. ✅ Taxonomy management endpoints
|
||||
3. ✅ Attribute management endpoints
|
||||
4. ✅ WordPress sync status tracking
|
||||
5. ✅ Cluster mapping capabilities
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation Created
|
||||
|
||||
1. ✅ `/data/app/igny8/backend/MIGRATION_SUMMARY.md`
|
||||
- Complete database migration details
|
||||
- Phase 1, 2, 3 breakdown
|
||||
- Rollback instructions
|
||||
|
||||
2. ✅ `/data/app/igny8/backend/NEW_ARCHITECTURE_GUIDE.md`
|
||||
- Quick reference guide
|
||||
- Usage examples
|
||||
- Query patterns
|
||||
- WordPress sync workflows
|
||||
|
||||
3. ✅ `/data/app/igny8/backend/ADMIN_VIEWS_UPDATE_SUMMARY.md`
|
||||
- Admin interface changes
|
||||
- API endpoint details
|
||||
- Filter documentation
|
||||
- Testing checklist
|
||||
|
||||
4. ✅ `/data/app/igny8/backend/COMPLETE_UPDATE_CHECKLIST.md` (this file)
|
||||
- Comprehensive verification
|
||||
- All changes documented
|
||||
- Status tracking
|
||||
|
||||
---
|
||||
|
||||
## ✅ Final Status
|
||||
|
||||
### All Tasks Complete
|
||||
|
||||
| Task | Status |
|
||||
|------|--------|
|
||||
| Database migrations | ✅ COMPLETE |
|
||||
| Model updates | ✅ COMPLETE |
|
||||
| Admin interfaces | ✅ COMPLETE |
|
||||
| API views | ✅ COMPLETE |
|
||||
| Serializers | ✅ COMPLETE |
|
||||
| URL routes | ✅ COMPLETE |
|
||||
| Filters updated | ✅ COMPLETE |
|
||||
| Forms updated | ✅ COMPLETE |
|
||||
| Backend restart | ✅ SUCCESS |
|
||||
| Documentation | ✅ COMPLETE |
|
||||
|
||||
### Zero Issues
|
||||
- ✅ No migration errors
|
||||
- ✅ No linter errors
|
||||
- ✅ No admin errors
|
||||
- ✅ No API errors
|
||||
- ✅ No startup errors
|
||||
|
||||
### Production Ready
|
||||
- ✅ Backward compatible
|
||||
- ✅ Non-breaking changes
|
||||
- ✅ Deprecated fields preserved
|
||||
- ✅ All tests passing
|
||||
- ✅ Documentation complete
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Next Steps (When Ready)
|
||||
|
||||
### Phase 4: WordPress Integration Implementation
|
||||
1. Backend service methods for WP import
|
||||
2. Frontend "Content Types" tab in Site Settings
|
||||
3. AI semantic mapping endpoint
|
||||
4. Sync status tracking UI
|
||||
5. Bulk import workflows
|
||||
|
||||
### Phase 5: Blueprint Cleanup (Optional)
|
||||
1. Migrate remaining blueprint data
|
||||
2. Drop deprecated blueprint tables
|
||||
3. Remove deprecated fields from models
|
||||
4. Final cleanup migration
|
||||
|
||||
---
|
||||
|
||||
**✅ ALL MIGRATIONS RUN**
|
||||
**✅ ALL TABLES UPDATED**
|
||||
**✅ ALL FORMS UPDATED**
|
||||
**✅ ALL FILTERS UPDATED**
|
||||
**✅ ALL ADMIN INTERFACES UPDATED**
|
||||
**✅ ALL API ENDPOINTS UPDATED**
|
||||
|
||||
**Status: PRODUCTION READY** 🎉
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
# WordPress Integration Fixes
|
||||
|
||||
## Issues Fixed
|
||||
|
||||
### 1. Validation Error (400 Bad Request)
|
||||
**Problem**: When creating WordPress integration with API key-only authentication, the backend was rejecting the request because `username` and `app_password` were empty strings.
|
||||
|
||||
**Root Cause**: The serializer was validating credentials but didn't account for API key-only authentication where username/password are optional.
|
||||
|
||||
**Fix**: Added custom validation in `SiteIntegrationSerializer` to allow API key-only authentication for WordPress platform:
|
||||
- If `api_key` is provided in `credentials_json`, username and app_password are optional
|
||||
- If `api_key` is not provided, username and app_password are required (traditional auth)
|
||||
|
||||
**File**: `backend/igny8_core/modules/integration/views.py`
|
||||
|
||||
### 2. Status Indicator Not Showing Connected
|
||||
**Problem**: Status indicator showed "Not configured" even when integration existed and was active.
|
||||
|
||||
**Root Cause**: Status check only looked for `site?.wp_api_key` but didn't check for API key in integration's `credentials_json`.
|
||||
|
||||
**Fix**: Updated status check to look for API key in both:
|
||||
- Site's `wp_api_key` field
|
||||
- Integration's `credentials_json.api_key` field
|
||||
|
||||
**File**: `frontend/src/pages/Sites/Settings.tsx`
|
||||
|
||||
### 3. Integration Creation Error Handling
|
||||
**Problem**: When toggling integration enabled without API key, no clear error was shown.
|
||||
|
||||
**Fix**: Added error handling to show clear message when trying to enable integration without API key.
|
||||
|
||||
**File**: `frontend/src/components/sites/WordPressIntegrationForm.tsx`
|
||||
|
||||
## Content Sync Status
|
||||
|
||||
Content sync will work as long as:
|
||||
1. ✅ Integration exists in database
|
||||
2. ✅ Integration `is_active = True`
|
||||
3. ✅ Integration `sync_enabled = True`
|
||||
|
||||
The sync service checks these conditions before performing sync operations.
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [x] Create WordPress integration with API key only (no username/password)
|
||||
- [x] Status indicator shows "Configured" when integration exists and is active
|
||||
- [x] Status indicator shows "Connected" after successful connection test
|
||||
- [x] Content sync works when integration is active and sync_enabled
|
||||
- [x] Error messages are clear when API key is missing
|
||||
|
||||
@@ -1,329 +0,0 @@
|
||||
# IGNY8 Content Architecture Migration Summary
|
||||
|
||||
**Date**: November 21, 2025
|
||||
**Status**: ✅ **COMPLETED SUCCESSFULLY**
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Complete migration from fragmented content/taxonomy structure to unified WordPress-ready architecture.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: New Models & Fields ✅
|
||||
|
||||
### New Models Created
|
||||
|
||||
#### 1. `ContentTaxonomy` (`igny8_content_taxonomy_terms`)
|
||||
Unified taxonomy model for categories, tags, and product attributes.
|
||||
|
||||
**Key Fields:**
|
||||
- `name`, `slug`, `taxonomy_type` (category, tag, product_cat, product_tag, product_attr, service_cat)
|
||||
- `external_id`, `external_taxonomy` (WordPress sync fields)
|
||||
- `sync_status` (native, imported, synced)
|
||||
- `count` (post count from WP)
|
||||
- `parent` (hierarchical taxonomies)
|
||||
- M2M to `Clusters` (semantic mapping)
|
||||
|
||||
**Indexes:** 14 total including composite indexes for WP sync lookups
|
||||
|
||||
#### 2. `ContentAttribute` (`igny8_content_attributes`)
|
||||
Renamed from `ContentAttributeMap` with enhanced WP sync support.
|
||||
|
||||
**Key Fields:**
|
||||
- `attribute_type` (product_spec, service_modifier, semantic_facet)
|
||||
- `name`, `value`
|
||||
- `external_id`, `external_attribute_name` (WooCommerce sync)
|
||||
- FK to `Content`, `Cluster`
|
||||
|
||||
**Indexes:** 7 total for efficient attribute lookups
|
||||
|
||||
#### 3. `ContentTaxonomyRelation` (`igny8_content_taxonomy_relations`)
|
||||
Through model for Content ↔ ContentTaxonomy M2M.
|
||||
|
||||
**Note:** Simplified to avoid tenant_id constraint issues.
|
||||
|
||||
### Content Model Enhancements
|
||||
|
||||
**New Fields:**
|
||||
- `content_format` (article, listicle, guide, comparison, review, roundup)
|
||||
- `cluster_role` (hub, supporting, attribute)
|
||||
- `external_type` (WP post type: post, page, product, service)
|
||||
- `cluster` FK (direct cluster relationship)
|
||||
- `taxonomies` M2M (replaces JSON categories/tags)
|
||||
|
||||
**Updated Fields:**
|
||||
- `entity_type` now uses: post, page, product, service, taxonomy_term (legacy values preserved)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Data Migration ✅
|
||||
|
||||
### Migrations Performed
|
||||
|
||||
1. **Content Entity Types** (`migrate_content_entity_types`)
|
||||
- Converted legacy `blog_post` → `post` + `content_format='article'`
|
||||
- Converted `article` → `post` + `content_format='article'`
|
||||
- Converted `taxonomy` → `taxonomy_term`
|
||||
|
||||
2. **Task Entity Types** (`migrate_task_entity_types`)
|
||||
- Migrated `Tasks.entity_type` → `Content.entity_type` + `content_format`
|
||||
- Migrated `Tasks.cluster_role` → `Content.cluster_role`
|
||||
- Migrated `Tasks.cluster_id` → `Content.cluster_id`
|
||||
|
||||
3. **Categories & Tags** (`migrate_content_categories_tags_to_taxonomy`)
|
||||
- Converted `Content.categories` JSON → `ContentTaxonomy` records (type: category)
|
||||
- Converted `Content.tags` JSON → `ContentTaxonomy` records (type: tag)
|
||||
- Created M2M relationships via `ContentTaxonomyRelation`
|
||||
|
||||
4. **Blueprint Taxonomies** (`migrate_blueprint_taxonomies`)
|
||||
- Migrated `SiteBlueprintTaxonomy` → `ContentTaxonomy`
|
||||
- Preserved `external_reference` as `external_id`
|
||||
- Preserved cluster mappings
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Deprecation & Cleanup ✅
|
||||
|
||||
### Deprecated Fields (Marked, Not Removed)
|
||||
|
||||
**In `Tasks` model:**
|
||||
- `content` → Use `Content.html_content`
|
||||
- `word_count` → Use `Content.word_count`
|
||||
- `meta_title` → Use `Content.meta_title`
|
||||
- `meta_description` → Use `Content.meta_description`
|
||||
- `assigned_post_id` → Use `Content.external_id`
|
||||
- `post_url` → Use `Content.external_url`
|
||||
- `entity_type` → Use `Content.entity_type`
|
||||
- `cluster_role` → Use `Content.cluster_role`
|
||||
- `content_structure` → Merged into `Content.content_format`
|
||||
- `content_type` → Merged into `Content.entity_type + content_format`
|
||||
|
||||
**In `Content` model:**
|
||||
- `categories` → Use `Content.taxonomies` M2M
|
||||
- `tags` → Use `Content.taxonomies` M2M
|
||||
|
||||
**Reason for Preservation:** Backward compatibility during transition period. Can be removed in future migration after ensuring no dependencies.
|
||||
|
||||
### Blueprint Tables Status
|
||||
|
||||
Tables **preserved** (1 active blueprint found):
|
||||
- `igny8_site_blueprints`
|
||||
- `igny8_page_blueprints`
|
||||
- `igny8_site_blueprint_clusters`
|
||||
- `igny8_site_blueprint_taxonomies`
|
||||
|
||||
**Note:** These can be dropped in Phase 4 if/when site builder is fully replaced by WP import flow.
|
||||
|
||||
---
|
||||
|
||||
## Applied Migrations
|
||||
|
||||
```
|
||||
writer
|
||||
[X] 0001_initial
|
||||
[X] 0002_phase1_add_unified_taxonomy_and_attributes
|
||||
[X] 0003_phase1b_fix_taxonomy_relation
|
||||
[X] 0004_phase2_migrate_data_to_unified_structure
|
||||
[X] 0005_phase3_mark_deprecated_fields
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Serializers Updated ✅
|
||||
|
||||
### New Serializers Created
|
||||
|
||||
1. `ContentTaxonomySerializer`
|
||||
- Includes parent_name, cluster_names, content_count
|
||||
- Full CRUD support
|
||||
|
||||
2. `ContentAttributeSerializer`
|
||||
- Includes content_title, cluster_name
|
||||
- WP sync field support
|
||||
|
||||
3. `ContentTaxonomyRelationSerializer`
|
||||
- M2M relationship details
|
||||
- Read-only access to relation metadata
|
||||
|
||||
### Existing Serializers Updated
|
||||
|
||||
- `TasksSerializer`: Updated to use `ContentAttribute` (backward compatible alias)
|
||||
- `ContentSerializer`: Updated attribute mappings to use new model
|
||||
|
||||
---
|
||||
|
||||
## Database Verification ✅
|
||||
|
||||
### New Tables Confirmed
|
||||
|
||||
```sql
|
||||
✓ igny8_content_taxonomy_terms (16 columns, 23 indexes)
|
||||
✓ igny8_content_attributes (16 columns, 15 indexes)
|
||||
✓ igny8_content_taxonomy_relations (4 columns, 3 indexes)
|
||||
✓ igny8_content_taxonomy_terms_clusters (M2M table)
|
||||
```
|
||||
|
||||
### New Content Fields Confirmed
|
||||
|
||||
```sql
|
||||
✓ cluster_id (bigint)
|
||||
✓ cluster_role (varchar)
|
||||
✓ content_format (varchar)
|
||||
✓ external_type (varchar)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backend Status ✅
|
||||
|
||||
**Container:** `igny8_backend`
|
||||
**Status:** Running and healthy
|
||||
**Workers:** 4 gunicorn workers booted successfully
|
||||
**No errors detected in startup logs**
|
||||
|
||||
---
|
||||
|
||||
## WordPress Integration Readiness
|
||||
|
||||
### Ready for WP Sync
|
||||
|
||||
1. **Content Type Detection**
|
||||
- `Content.entity_type` = WP post_type (post, page, product)
|
||||
- `Content.external_type` = source post_type name
|
||||
- `Content.external_id` = WP post ID
|
||||
- `Content.external_url` = WP post permalink
|
||||
|
||||
2. **Taxonomy Sync**
|
||||
- `ContentTaxonomy.external_id` = WP term ID
|
||||
- `ContentTaxonomy.external_taxonomy` = WP taxonomy name (category, post_tag, product_cat, pa_*)
|
||||
- `ContentTaxonomy.taxonomy_type` = mapped type
|
||||
- `ContentTaxonomy.sync_status` = import tracking
|
||||
|
||||
3. **Product Attributes**
|
||||
- `ContentAttribute.external_id` = WooCommerce attribute term ID
|
||||
- `ContentAttribute.external_attribute_name` = WP attribute slug (pa_color, pa_size)
|
||||
- `ContentAttribute.attribute_type` = product_spec
|
||||
|
||||
4. **Semantic Mapping**
|
||||
- `ContentTaxonomy.clusters` M2M = AI cluster assignments
|
||||
- `Content.cluster` FK = primary semantic cluster
|
||||
- `Content.cluster_role` = hub/supporting/attribute
|
||||
|
||||
---
|
||||
|
||||
## Next Steps for WP Integration
|
||||
|
||||
### Immediate (Already Prepared)
|
||||
|
||||
1. ✅ Plugin `/site-metadata/` endpoint exists
|
||||
2. ✅ Database structure ready
|
||||
3. ✅ Models & serializers ready
|
||||
|
||||
### Phase 4 (Next Session)
|
||||
|
||||
1. **Backend Service Layer**
|
||||
- `IntegrationService.fetch_content_structure(integration_id)`
|
||||
- `IntegrationService.import_taxonomies(integration_id, taxonomy_type, limit)`
|
||||
- `IntegrationService.import_content_titles(integration_id, post_type, limit)`
|
||||
- `IntegrationService.fetch_full_content(content_id)` (on-demand)
|
||||
|
||||
2. **Backend Endpoints**
|
||||
- `POST /api/v1/integration/integrations/{id}/fetch-structure/`
|
||||
- `POST /api/v1/integration/integrations/{id}/import-taxonomies/`
|
||||
- `POST /api/v1/integration/integrations/{id}/import-content/`
|
||||
- `GET /api/v1/integration/content-taxonomies/` (ViewSet)
|
||||
- `GET /api/v1/integration/content-attributes/` (ViewSet)
|
||||
|
||||
3. **Frontend UI**
|
||||
- New tab: "Content Types" in Site Settings
|
||||
- Display detected post types & taxonomies
|
||||
- Enable/disable toggles
|
||||
- Fetch limit inputs
|
||||
- Sync status indicators
|
||||
|
||||
4. **AI Semantic Mapping**
|
||||
- Endpoint: `POST /api/v1/integration/integrations/{id}/generate-semantic-map/`
|
||||
- Input: Content titles + taxonomy terms
|
||||
- Output: Cluster recommendations + attribute suggestions
|
||||
- Auto-create clusters and map taxonomies
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan (If Needed)
|
||||
|
||||
### Critical Data Preserved
|
||||
|
||||
- ✅ Original JSON categories/tags still in Content table
|
||||
- ✅ Original blueprint taxonomies table intact
|
||||
- ✅ Legacy entity_type values preserved in choices
|
||||
- ✅ All task fields still functional
|
||||
|
||||
### To Rollback
|
||||
|
||||
```bash
|
||||
# Rollback to before migration
|
||||
python manage.py migrate writer 0001
|
||||
|
||||
# Remove new tables manually if needed
|
||||
DROP TABLE igny8_content_taxonomy_relations CASCADE;
|
||||
DROP TABLE igny8_content_taxonomy_terms_clusters CASCADE;
|
||||
DROP TABLE igny8_content_taxonomy_terms CASCADE;
|
||||
DROP TABLE igny8_content_attributes CASCADE;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Notes
|
||||
|
||||
- All new tables have appropriate indexes
|
||||
- Composite indexes for WP sync lookups (external_id + external_taxonomy)
|
||||
- Indexes on taxonomy_type, sync_status for filtering
|
||||
- M2M through table is minimal (no tenant_id to avoid constraint issues)
|
||||
|
||||
---
|
||||
|
||||
## Testing Recommendations
|
||||
|
||||
### Manual Tests
|
||||
|
||||
1. ✅ Backend restart successful
|
||||
2. ✅ Database tables created correctly
|
||||
3. ✅ Migrations applied without errors
|
||||
4. 🔲 Create new ContentTaxonomy via API
|
||||
5. 🔲 Assign taxonomies to content via M2M
|
||||
6. 🔲 Create ContentAttribute for product
|
||||
7. 🔲 Query taxonomies by external_id
|
||||
8. 🔲 Test cluster → taxonomy mapping
|
||||
|
||||
### Integration Tests (Next Phase)
|
||||
|
||||
1. WP `/site-metadata/` → Backend storage
|
||||
2. WP category import → ContentTaxonomy creation
|
||||
3. WP product attribute import → ContentAttribute creation
|
||||
4. Content → Taxonomy M2M assignment
|
||||
5. AI semantic mapping with imported data
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**All 3 phases completed successfully:**
|
||||
|
||||
✅ **Phase 1**: New models & fields added
|
||||
✅ **Phase 2**: Existing data migrated
|
||||
✅ **Phase 3**: Deprecated fields marked
|
||||
|
||||
**Current Status**: Production-ready, backward compatible, WordPress integration prepared.
|
||||
|
||||
**Zero downtime**: All changes non-breaking, existing functionality preserved.
|
||||
|
||||
---
|
||||
|
||||
**Migration Completed By**: AI Assistant
|
||||
**Total Migrations**: 5
|
||||
**Total New Tables**: 4
|
||||
**Total New Fields in Content**: 4
|
||||
**Deprecated Fields**: 12 (marked, not removed)
|
||||
|
||||
@@ -1,433 +0,0 @@
|
||||
# IGNY8 Unified Content Architecture - Quick Reference
|
||||
|
||||
## ✅ What Changed
|
||||
|
||||
### Old Way ❌
|
||||
```python
|
||||
# Scattered entity types
|
||||
task.entity_type = 'blog_post'
|
||||
task.content_type = 'article'
|
||||
task.content_structure = 'pillar_page'
|
||||
|
||||
# JSON arrays for taxonomies
|
||||
content.categories = ['SEO', 'WordPress']
|
||||
content.tags = ['tutorial', 'guide']
|
||||
|
||||
# Fragmented attributes
|
||||
ContentAttributeMap(name='Color', value='Blue')
|
||||
```
|
||||
|
||||
### New Way ✅
|
||||
```python
|
||||
# Single unified entity type
|
||||
content.entity_type = 'post' # What it is
|
||||
content.content_format = 'article' # How it's structured
|
||||
content.cluster_role = 'hub' # Semantic role
|
||||
|
||||
# Real M2M relationships
|
||||
content.taxonomies.add(seo_category)
|
||||
content.taxonomies.add(tutorial_tag)
|
||||
|
||||
# Enhanced attributes with WP sync
|
||||
ContentAttribute(
|
||||
content=content,
|
||||
attribute_type='product_spec',
|
||||
name='Color',
|
||||
value='Blue',
|
||||
external_id=101, # WP term ID
|
||||
external_attribute_name='pa_color'
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Core Models
|
||||
|
||||
### 1. Content (Enhanced)
|
||||
```python
|
||||
from igny8_core.business.content.models import Content
|
||||
|
||||
# Create content
|
||||
content = Content.objects.create(
|
||||
title="Best SEO Tools 2025",
|
||||
entity_type='post', # post, page, product, service, taxonomy_term
|
||||
content_format='listicle', # article, listicle, guide, comparison, review
|
||||
cluster_role='hub', # hub, supporting, attribute
|
||||
html_content="<h1>Best SEO Tools...</h1>",
|
||||
|
||||
# WordPress sync
|
||||
external_id=427, # WP post ID
|
||||
external_url="https://site.com/seo-tools/",
|
||||
external_type='post', # WP post_type
|
||||
source='wordpress',
|
||||
sync_status='imported',
|
||||
|
||||
# SEO
|
||||
meta_title="15 Best SEO Tools...",
|
||||
primary_keyword="seo tools",
|
||||
|
||||
# Relationships
|
||||
cluster=seo_cluster,
|
||||
site=site,
|
||||
sector=sector,
|
||||
)
|
||||
|
||||
# Add taxonomies
|
||||
content.taxonomies.add(seo_category, tools_tag)
|
||||
```
|
||||
|
||||
### 2. ContentTaxonomy (New)
|
||||
```python
|
||||
from igny8_core.business.content.models import ContentTaxonomy
|
||||
|
||||
# WordPress category
|
||||
category = ContentTaxonomy.objects.create(
|
||||
name="SEO",
|
||||
slug="seo",
|
||||
taxonomy_type='category', # category, tag, product_cat, product_tag, product_attr
|
||||
description="All about SEO",
|
||||
|
||||
# WordPress sync
|
||||
external_id=12, # WP term ID
|
||||
external_taxonomy='category', # WP taxonomy name
|
||||
sync_status='imported',
|
||||
count=45, # Post count from WP
|
||||
|
||||
site=site,
|
||||
sector=sector,
|
||||
)
|
||||
|
||||
# Map to semantic clusters
|
||||
category.clusters.add(seo_cluster, content_marketing_cluster)
|
||||
|
||||
# Hierarchical taxonomy
|
||||
subcategory = ContentTaxonomy.objects.create(
|
||||
name="Technical SEO",
|
||||
slug="technical-seo",
|
||||
taxonomy_type='category',
|
||||
parent=category, # Parent category
|
||||
site=site,
|
||||
sector=sector,
|
||||
)
|
||||
```
|
||||
|
||||
### 3. ContentAttribute (Enhanced)
|
||||
```python
|
||||
from igny8_core.business.content.models import ContentAttribute
|
||||
|
||||
# WooCommerce product attribute
|
||||
attribute = ContentAttribute.objects.create(
|
||||
content=product_content,
|
||||
attribute_type='product_spec', # product_spec, service_modifier, semantic_facet
|
||||
name='Color',
|
||||
value='Blue',
|
||||
|
||||
# WooCommerce sync
|
||||
external_id=101, # WP attribute term ID
|
||||
external_attribute_name='pa_color', # WP attribute slug
|
||||
|
||||
source='wordpress',
|
||||
site=site,
|
||||
sector=sector,
|
||||
)
|
||||
|
||||
# Semantic cluster attribute
|
||||
semantic_attr = ContentAttribute.objects.create(
|
||||
cluster=enterprise_seo_cluster,
|
||||
attribute_type='semantic_facet',
|
||||
name='Target Audience',
|
||||
value='Enterprise',
|
||||
source='manual',
|
||||
site=site,
|
||||
sector=sector,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 WordPress Sync Workflows
|
||||
|
||||
### Scenario 1: Import WP Categories
|
||||
```python
|
||||
from igny8_core.business.content.models import ContentTaxonomy
|
||||
|
||||
# Fetch from WP /wp-json/wp/v2/categories
|
||||
wp_categories = [
|
||||
{'id': 12, 'name': 'SEO', 'slug': 'seo', 'count': 45},
|
||||
{'id': 15, 'name': 'WordPress', 'slug': 'wordpress', 'count': 32},
|
||||
]
|
||||
|
||||
for wp_cat in wp_categories:
|
||||
taxonomy, created = ContentTaxonomy.objects.update_or_create(
|
||||
site=site,
|
||||
external_id=wp_cat['id'],
|
||||
external_taxonomy='category',
|
||||
defaults={
|
||||
'name': wp_cat['name'],
|
||||
'slug': wp_cat['slug'],
|
||||
'taxonomy_type': 'category',
|
||||
'count': wp_cat['count'],
|
||||
'sync_status': 'imported',
|
||||
'sector': site.sectors.first(),
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Scenario 2: Import WP Posts (Titles Only)
|
||||
```python
|
||||
from igny8_core.business.content.models import Content, ContentTaxonomy
|
||||
|
||||
# Fetch from WP /wp-json/wp/v2/posts
|
||||
wp_posts = [
|
||||
{
|
||||
'id': 427,
|
||||
'title': {'rendered': 'Best SEO Tools 2025'},
|
||||
'link': 'https://site.com/seo-tools/',
|
||||
'type': 'post',
|
||||
'categories': [12, 15],
|
||||
'tags': [45, 67],
|
||||
}
|
||||
]
|
||||
|
||||
for wp_post in wp_posts:
|
||||
# Create content (title only, no html_content yet)
|
||||
content, created = Content.objects.update_or_create(
|
||||
site=site,
|
||||
external_id=wp_post['id'],
|
||||
defaults={
|
||||
'title': wp_post['title']['rendered'],
|
||||
'entity_type': 'post',
|
||||
'external_url': wp_post['link'],
|
||||
'external_type': wp_post['type'],
|
||||
'source': 'wordpress',
|
||||
'sync_status': 'imported',
|
||||
'sector': site.sectors.first(),
|
||||
}
|
||||
)
|
||||
|
||||
# Map categories
|
||||
for cat_id in wp_post['categories']:
|
||||
try:
|
||||
taxonomy = ContentTaxonomy.objects.get(
|
||||
site=site,
|
||||
external_id=cat_id,
|
||||
taxonomy_type='category'
|
||||
)
|
||||
content.taxonomies.add(taxonomy)
|
||||
except ContentTaxonomy.DoesNotExist:
|
||||
pass
|
||||
|
||||
# Map tags
|
||||
for tag_id in wp_post['tags']:
|
||||
try:
|
||||
taxonomy = ContentTaxonomy.objects.get(
|
||||
site=site,
|
||||
external_id=tag_id,
|
||||
taxonomy_type='tag'
|
||||
)
|
||||
content.taxonomies.add(taxonomy)
|
||||
except ContentTaxonomy.DoesNotExist:
|
||||
pass
|
||||
```
|
||||
|
||||
### Scenario 3: Fetch Full Content On-Demand
|
||||
```python
|
||||
def fetch_full_content(content_id):
|
||||
"""Fetch full HTML content from WP when needed for AI analysis."""
|
||||
content = Content.objects.get(id=content_id)
|
||||
|
||||
if content.source == 'wordpress' and content.external_id:
|
||||
# Fetch from WP /wp-json/wp/v2/posts/{external_id}
|
||||
wp_response = requests.get(
|
||||
f"{content.site.url}/wp-json/wp/v2/posts/{content.external_id}"
|
||||
)
|
||||
wp_data = wp_response.json()
|
||||
|
||||
# Update content
|
||||
content.html_content = wp_data['content']['rendered']
|
||||
content.word_count = len(wp_data['content']['rendered'].split())
|
||||
content.meta_title = wp_data.get('yoast_head_json', {}).get('title', '')
|
||||
content.meta_description = wp_data.get('yoast_head_json', {}).get('description', '')
|
||||
content.save()
|
||||
|
||||
return content
|
||||
```
|
||||
|
||||
### Scenario 4: Import WooCommerce Product Attributes
|
||||
```python
|
||||
from igny8_core.business.content.models import Content, ContentAttribute
|
||||
|
||||
# Fetch from WP /wp-json/wc/v3/products/{id}
|
||||
wp_product = {
|
||||
'id': 88,
|
||||
'name': 'Blue Widget',
|
||||
'type': 'simple',
|
||||
'attributes': [
|
||||
{'id': 1, 'name': 'Color', 'slug': 'pa_color', 'option': 'Blue'},
|
||||
{'id': 2, 'name': 'Size', 'slug': 'pa_size', 'option': 'Large'},
|
||||
]
|
||||
}
|
||||
|
||||
# Create product content
|
||||
product = Content.objects.create(
|
||||
site=site,
|
||||
title=wp_product['name'],
|
||||
entity_type='product',
|
||||
external_id=wp_product['id'],
|
||||
external_type='product',
|
||||
source='wordpress',
|
||||
sync_status='imported',
|
||||
sector=site.sectors.first(),
|
||||
)
|
||||
|
||||
# Import attributes
|
||||
for attr in wp_product['attributes']:
|
||||
ContentAttribute.objects.create(
|
||||
content=product,
|
||||
attribute_type='product_spec',
|
||||
name=attr['name'],
|
||||
value=attr['option'],
|
||||
external_attribute_name=attr['slug'],
|
||||
source='wordpress',
|
||||
site=site,
|
||||
sector=site.sectors.first(),
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Query Examples
|
||||
|
||||
### Find Content by Entity Type
|
||||
```python
|
||||
# All blog posts
|
||||
posts = Content.objects.filter(entity_type='post')
|
||||
|
||||
# All listicles
|
||||
listicles = Content.objects.filter(entity_type='post', content_format='listicle')
|
||||
|
||||
# All hub pages
|
||||
hubs = Content.objects.filter(cluster_role='hub')
|
||||
|
||||
# All WP-synced products
|
||||
products = Content.objects.filter(
|
||||
entity_type='product',
|
||||
source='wordpress',
|
||||
sync_status='imported'
|
||||
)
|
||||
```
|
||||
|
||||
### Find Taxonomies
|
||||
```python
|
||||
# All categories with WP sync
|
||||
categories = ContentTaxonomy.objects.filter(
|
||||
taxonomy_type='category',
|
||||
external_id__isnull=False
|
||||
)
|
||||
|
||||
# Product attributes (color, size, etc.)
|
||||
product_attrs = ContentTaxonomy.objects.filter(taxonomy_type='product_attr')
|
||||
|
||||
# Taxonomies mapped to a cluster
|
||||
cluster_terms = ContentTaxonomy.objects.filter(clusters=seo_cluster)
|
||||
|
||||
# Get all content for a taxonomy
|
||||
seo_content = Content.objects.filter(taxonomies=seo_category)
|
||||
```
|
||||
|
||||
### Find Attributes
|
||||
```python
|
||||
# All product specs for a content
|
||||
specs = ContentAttribute.objects.filter(
|
||||
content=product,
|
||||
attribute_type='product_spec'
|
||||
)
|
||||
|
||||
# All attributes in a cluster
|
||||
cluster_attrs = ContentAttribute.objects.filter(
|
||||
cluster=enterprise_cluster,
|
||||
attribute_type='semantic_facet'
|
||||
)
|
||||
|
||||
# Find content by attribute value
|
||||
blue_products = Content.objects.filter(
|
||||
attributes__name='Color',
|
||||
attributes__value='Blue'
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Relationships Diagram
|
||||
|
||||
```
|
||||
Site
|
||||
├─ Content (post, page, product, service, taxonomy_term)
|
||||
│ ├─ entity_type (what it is)
|
||||
│ ├─ content_format (how it's structured)
|
||||
│ ├─ cluster_role (semantic role)
|
||||
│ ├─ cluster FK → Clusters
|
||||
│ ├─ taxonomies M2M → ContentTaxonomy
|
||||
│ └─ attributes FK ← ContentAttribute
|
||||
│
|
||||
├─ ContentTaxonomy (category, tag, product_cat, product_tag, product_attr)
|
||||
│ ├─ external_id (WP term ID)
|
||||
│ ├─ external_taxonomy (WP taxonomy name)
|
||||
│ ├─ parent FK → self (hierarchical)
|
||||
│ ├─ clusters M2M → Clusters
|
||||
│ └─ contents M2M ← Content
|
||||
│
|
||||
└─ Clusters
|
||||
├─ contents FK ← Content
|
||||
├─ taxonomy_terms M2M ← ContentTaxonomy
|
||||
└─ attributes FK ← ContentAttribute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Migration Notes
|
||||
|
||||
### Deprecated Fields (Still Available)
|
||||
|
||||
**Don't use these anymore:**
|
||||
```python
|
||||
# ❌ Old way
|
||||
task.content = "..." # Use Content.html_content
|
||||
task.entity_type = "..." # Use Content.entity_type
|
||||
content.categories = ["SEO"] # Use content.taxonomies M2M
|
||||
content.tags = ["tutorial"] # Use content.taxonomies M2M
|
||||
```
|
||||
|
||||
**Use these instead:**
|
||||
```python
|
||||
# ✅ New way
|
||||
content.html_content = "..."
|
||||
content.entity_type = "post"
|
||||
content.taxonomies.add(seo_category)
|
||||
content.taxonomies.add(tutorial_tag)
|
||||
```
|
||||
|
||||
### Backward Compatibility
|
||||
|
||||
Legacy values still work:
|
||||
```python
|
||||
# These still map correctly
|
||||
content.entity_type = 'blog_post' # → internally handled as 'post'
|
||||
content.entity_type = 'article' # → internally handled as 'post'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Next: Frontend Integration
|
||||
|
||||
Ready for Phase 4:
|
||||
1. Site Settings → "Content Types" tab
|
||||
2. Display imported taxonomies
|
||||
3. Enable/disable sync per type
|
||||
4. Set fetch limits
|
||||
5. Trigger AI semantic mapping
|
||||
|
||||
---
|
||||
|
||||
**Questions?** Check `/data/app/igny8/backend/MIGRATION_SUMMARY.md` for full migration details.
|
||||
|
||||
@@ -1,705 +0,0 @@
|
||||
# Sites Integration Plan - Content Types Structure
|
||||
|
||||
**Date**: November 22, 2025
|
||||
**Status**: 📋 **PLANNING**
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Integrate the new unified content architecture (ContentTaxonomy, ContentAttribute, entity_type, content_format) with the Sites module and SiteIntegration model to enable WordPress content type discovery, configuration, and sync.
|
||||
|
||||
---
|
||||
|
||||
## Current State Analysis
|
||||
|
||||
### ✅ What We Have
|
||||
|
||||
**1. Unified Content Architecture (COMPLETE)**
|
||||
- `Content` model with `entity_type`, `content_format`, `cluster_role`
|
||||
- `ContentTaxonomy` model for categories, tags, product attributes
|
||||
- `ContentAttribute` model for product specs, service modifiers
|
||||
- WordPress sync fields (`external_id`, `external_taxonomy`, `sync_status`)
|
||||
|
||||
**2. Site Model**
|
||||
- Basic site information (name, domain, industry)
|
||||
- `site_type` field (marketing, ecommerce, blog, portfolio, corporate)
|
||||
- `hosting_type` field (igny8_sites, wordpress, shopify, multi)
|
||||
- Legacy WP fields (`wp_url`, `wp_username`, `wp_api_key`)
|
||||
|
||||
**3. SiteIntegration Model**
|
||||
- Platform-specific integrations (wordpress, shopify, custom)
|
||||
- `config_json` for configuration
|
||||
- `credentials_json` for API keys/tokens
|
||||
- `sync_enabled` flag for two-way sync
|
||||
|
||||
**4. WordPress Plugin**
|
||||
- `/wp-json/igny8/v1/site-metadata/` endpoint
|
||||
- Returns post types, taxonomies, and counts
|
||||
- API key authentication support
|
||||
|
||||
### ❌ What's Missing
|
||||
|
||||
1. **Content Type Configuration Storage**
|
||||
- No place to store which post types/taxonomies are enabled
|
||||
- No fetch limits per content type
|
||||
- No sync preferences per taxonomy
|
||||
|
||||
2. **Site → Integration Connection**
|
||||
- No clear link between Site.site_type and available content types
|
||||
- No mapping of WP post types to IGNY8 entity types
|
||||
|
||||
3. **Frontend UI**
|
||||
- No "Content Types" tab in Site Settings
|
||||
- No interface to enable/disable content types
|
||||
- No way to set fetch limits
|
||||
|
||||
4. **Backend Service Methods**
|
||||
- No method to fetch WP structure and store in `config_json`
|
||||
- No method to import taxonomies
|
||||
- No method to import content titles
|
||||
|
||||
---
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
### Phase 1: Extend SiteIntegration.config_json Structure
|
||||
|
||||
Store WordPress content type configuration in `SiteIntegration.config_json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "https://example.com",
|
||||
"api_version": "v1",
|
||||
"plugin_version": "1.0.0",
|
||||
"content_types": {
|
||||
"post_types": {
|
||||
"post": {
|
||||
"label": "Posts",
|
||||
"count": 123,
|
||||
"enabled": true,
|
||||
"fetch_limit": 100,
|
||||
"entity_type": "post",
|
||||
"content_format": "article",
|
||||
"last_synced": "2025-11-22T10:00:00Z"
|
||||
},
|
||||
"page": {
|
||||
"label": "Pages",
|
||||
"count": 12,
|
||||
"enabled": true,
|
||||
"fetch_limit": 50,
|
||||
"entity_type": "page",
|
||||
"content_format": null,
|
||||
"last_synced": null
|
||||
},
|
||||
"product": {
|
||||
"label": "Products",
|
||||
"count": 456,
|
||||
"enabled": true,
|
||||
"fetch_limit": 200,
|
||||
"entity_type": "product",
|
||||
"content_format": null,
|
||||
"last_synced": null
|
||||
}
|
||||
},
|
||||
"taxonomies": {
|
||||
"category": {
|
||||
"label": "Categories",
|
||||
"count": 25,
|
||||
"enabled": true,
|
||||
"fetch_limit": 100,
|
||||
"taxonomy_type": "category",
|
||||
"last_synced": "2025-11-22T10:00:00Z"
|
||||
},
|
||||
"post_tag": {
|
||||
"label": "Tags",
|
||||
"count": 102,
|
||||
"enabled": true,
|
||||
"fetch_limit": 200,
|
||||
"taxonomy_type": "tag",
|
||||
"last_synced": null
|
||||
},
|
||||
"product_cat": {
|
||||
"label": "Product Categories",
|
||||
"count": 15,
|
||||
"enabled": true,
|
||||
"fetch_limit": 50,
|
||||
"taxonomy_type": "product_cat",
|
||||
"last_synced": null
|
||||
},
|
||||
"pa_color": {
|
||||
"label": "Color",
|
||||
"count": 10,
|
||||
"enabled": true,
|
||||
"fetch_limit": 50,
|
||||
"taxonomy_type": "product_attr",
|
||||
"attribute_name": "Color",
|
||||
"last_synced": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugin_connection_enabled": true,
|
||||
"two_way_sync_enabled": true,
|
||||
"last_structure_fetch": "2025-11-22T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Backend Service Methods
|
||||
|
||||
#### 1. **IntegrationService.fetch_content_structure()**
|
||||
|
||||
```python
|
||||
def fetch_content_structure(self, integration_id: int) -> Dict[str, Any]:
|
||||
"""
|
||||
Fetch content structure from WordPress plugin and store in config_json.
|
||||
|
||||
Steps:
|
||||
1. GET /wp-json/igny8/v1/site-metadata/
|
||||
2. Parse response
|
||||
3. Update integration.config_json['content_types']
|
||||
4. Return structure
|
||||
"""
|
||||
integration = SiteIntegration.objects.get(id=integration_id)
|
||||
|
||||
# Call WordPress plugin
|
||||
wp_url = integration.config_json.get('url')
|
||||
api_key = integration.credentials_json.get('api_key')
|
||||
|
||||
response = requests.get(
|
||||
f"{wp_url}/wp-json/igny8/v1/site-metadata/",
|
||||
headers={'X-IGNY8-API-KEY': api_key}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()['data']
|
||||
|
||||
# Transform to our structure
|
||||
content_types = {
|
||||
'post_types': {},
|
||||
'taxonomies': {}
|
||||
}
|
||||
|
||||
# Map post types
|
||||
for wp_type, info in data['post_types'].items():
|
||||
content_types['post_types'][wp_type] = {
|
||||
'label': info['label'],
|
||||
'count': info['count'],
|
||||
'enabled': False, # Default disabled
|
||||
'fetch_limit': 100, # Default limit
|
||||
'entity_type': self._map_wp_type_to_entity(wp_type),
|
||||
'content_format': None,
|
||||
'last_synced': None
|
||||
}
|
||||
|
||||
# Map taxonomies
|
||||
for wp_tax, info in data['taxonomies'].items():
|
||||
content_types['taxonomies'][wp_tax] = {
|
||||
'label': info['label'],
|
||||
'count': info['count'],
|
||||
'enabled': False, # Default disabled
|
||||
'fetch_limit': 100, # Default limit
|
||||
'taxonomy_type': self._map_wp_tax_to_type(wp_tax),
|
||||
'last_synced': None
|
||||
}
|
||||
|
||||
# Update config
|
||||
if 'content_types' not in integration.config_json:
|
||||
integration.config_json['content_types'] = {}
|
||||
|
||||
integration.config_json['content_types'] = content_types
|
||||
integration.config_json['last_structure_fetch'] = timezone.now().isoformat()
|
||||
integration.save()
|
||||
|
||||
return content_types
|
||||
else:
|
||||
raise Exception(f"Failed to fetch structure: {response.status_code}")
|
||||
|
||||
def _map_wp_type_to_entity(self, wp_type: str) -> str:
|
||||
"""Map WordPress post type to IGNY8 entity_type"""
|
||||
mapping = {
|
||||
'post': 'post',
|
||||
'page': 'page',
|
||||
'product': 'product',
|
||||
'service': 'service',
|
||||
}
|
||||
return mapping.get(wp_type, 'post')
|
||||
|
||||
def _map_wp_tax_to_type(self, wp_tax: str) -> str:
|
||||
"""Map WordPress taxonomy to ContentTaxonomy type"""
|
||||
mapping = {
|
||||
'category': 'category',
|
||||
'post_tag': 'tag',
|
||||
'product_cat': 'product_cat',
|
||||
'product_tag': 'product_tag',
|
||||
}
|
||||
|
||||
# Product attributes start with pa_
|
||||
if wp_tax.startswith('pa_'):
|
||||
return 'product_attr'
|
||||
|
||||
return mapping.get(wp_tax, 'category')
|
||||
```
|
||||
|
||||
#### 2. **IntegrationService.import_taxonomies()**
|
||||
|
||||
```python
|
||||
def import_taxonomies(
|
||||
self,
|
||||
integration_id: int,
|
||||
taxonomy_type: str = None,
|
||||
limit: int = None
|
||||
) -> int:
|
||||
"""
|
||||
Import taxonomy terms from WordPress to ContentTaxonomy.
|
||||
|
||||
Args:
|
||||
integration_id: SiteIntegration ID
|
||||
taxonomy_type: Specific taxonomy to import (e.g., 'category', 'post_tag')
|
||||
limit: Max terms to import per taxonomy
|
||||
|
||||
Returns:
|
||||
Number of terms imported
|
||||
"""
|
||||
integration = SiteIntegration.objects.get(id=integration_id)
|
||||
site = integration.site
|
||||
|
||||
# Get enabled taxonomies from config
|
||||
content_types = integration.config_json.get('content_types', {})
|
||||
taxonomies = content_types.get('taxonomies', {})
|
||||
|
||||
imported_count = 0
|
||||
|
||||
for wp_tax, config in taxonomies.items():
|
||||
# Skip if not enabled or not requested
|
||||
if not config.get('enabled'):
|
||||
continue
|
||||
if taxonomy_type and wp_tax != taxonomy_type:
|
||||
continue
|
||||
|
||||
# Fetch from WordPress
|
||||
fetch_limit = limit or config.get('fetch_limit', 100)
|
||||
wp_url = integration.config_json.get('url')
|
||||
api_key = integration.credentials_json.get('api_key')
|
||||
|
||||
# Map taxonomy endpoint
|
||||
endpoint = self._get_wp_taxonomy_endpoint(wp_tax)
|
||||
|
||||
response = requests.get(
|
||||
f"{wp_url}/wp-json/wp/v2/{endpoint}?per_page={fetch_limit}",
|
||||
headers={'X-IGNY8-API-KEY': api_key}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
terms = response.json()
|
||||
|
||||
for term in terms:
|
||||
# Create or update ContentTaxonomy
|
||||
taxonomy, created = ContentTaxonomy.objects.update_or_create(
|
||||
site=site,
|
||||
external_id=term['id'],
|
||||
external_taxonomy=wp_tax,
|
||||
defaults={
|
||||
'name': term['name'],
|
||||
'slug': term['slug'],
|
||||
'taxonomy_type': config['taxonomy_type'],
|
||||
'description': term.get('description', ''),
|
||||
'count': term.get('count', 0),
|
||||
'sync_status': 'imported',
|
||||
'account': site.account,
|
||||
'sector': site.sectors.first(), # Default to first sector
|
||||
}
|
||||
)
|
||||
|
||||
if created:
|
||||
imported_count += 1
|
||||
|
||||
# Update last_synced
|
||||
config['last_synced'] = timezone.now().isoformat()
|
||||
integration.save()
|
||||
|
||||
return imported_count
|
||||
|
||||
def _get_wp_taxonomy_endpoint(self, wp_tax: str) -> str:
|
||||
"""Get WordPress REST endpoint for taxonomy"""
|
||||
mapping = {
|
||||
'category': 'categories',
|
||||
'post_tag': 'tags',
|
||||
'product_cat': 'products/categories',
|
||||
'product_tag': 'products/tags',
|
||||
}
|
||||
|
||||
# Product attributes
|
||||
if wp_tax.startswith('pa_'):
|
||||
attr_id = wp_tax.replace('pa_', '')
|
||||
return f'products/attributes/{attr_id}/terms'
|
||||
|
||||
return mapping.get(wp_tax, wp_tax)
|
||||
```
|
||||
|
||||
#### 3. **IntegrationService.import_content_titles()**
|
||||
|
||||
```python
|
||||
def import_content_titles(
|
||||
self,
|
||||
integration_id: int,
|
||||
post_type: str = None,
|
||||
limit: int = None
|
||||
) -> int:
|
||||
"""
|
||||
Import content titles (not full content) from WordPress.
|
||||
|
||||
Args:
|
||||
integration_id: SiteIntegration ID
|
||||
post_type: Specific post type to import (e.g., 'post', 'product')
|
||||
limit: Max items to import per type
|
||||
|
||||
Returns:
|
||||
Number of content items imported
|
||||
"""
|
||||
integration = SiteIntegration.objects.get(id=integration_id)
|
||||
site = integration.site
|
||||
|
||||
# Get enabled post types from config
|
||||
content_types = integration.config_json.get('content_types', {})
|
||||
post_types = content_types.get('post_types', {})
|
||||
|
||||
imported_count = 0
|
||||
|
||||
for wp_type, config in post_types.items():
|
||||
# Skip if not enabled or not requested
|
||||
if not config.get('enabled'):
|
||||
continue
|
||||
if post_type and wp_type != post_type:
|
||||
continue
|
||||
|
||||
# Fetch from WordPress
|
||||
fetch_limit = limit or config.get('fetch_limit', 100)
|
||||
wp_url = integration.config_json.get('url')
|
||||
api_key = integration.credentials_json.get('api_key')
|
||||
|
||||
# Determine endpoint
|
||||
endpoint = 'products' if wp_type == 'product' else wp_type + 's'
|
||||
|
||||
response = requests.get(
|
||||
f"{wp_url}/wp-json/wp/v2/{endpoint}?per_page={fetch_limit}",
|
||||
headers={'X-IGNY8-API-KEY': api_key}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
items = response.json()
|
||||
|
||||
for item in items:
|
||||
# Create or update Content (title only, no html_content yet)
|
||||
content, created = Content.objects.update_or_create(
|
||||
site=site,
|
||||
external_id=item['id'],
|
||||
external_type=wp_type,
|
||||
defaults={
|
||||
'title': item['title']['rendered'] if isinstance(item['title'], dict) else item['title'],
|
||||
'entity_type': config['entity_type'],
|
||||
'content_format': config.get('content_format'),
|
||||
'external_url': item.get('link', ''),
|
||||
'source': 'wordpress',
|
||||
'sync_status': 'imported',
|
||||
'account': site.account,
|
||||
'sector': site.sectors.first(),
|
||||
}
|
||||
)
|
||||
|
||||
# Map taxonomies
|
||||
if 'categories' in item:
|
||||
for cat_id in item['categories']:
|
||||
try:
|
||||
taxonomy = ContentTaxonomy.objects.get(
|
||||
site=site,
|
||||
external_id=cat_id,
|
||||
taxonomy_type='category'
|
||||
)
|
||||
content.taxonomies.add(taxonomy)
|
||||
except ContentTaxonomy.DoesNotExist:
|
||||
pass
|
||||
|
||||
if 'tags' in item:
|
||||
for tag_id in item['tags']:
|
||||
try:
|
||||
taxonomy = ContentTaxonomy.objects.get(
|
||||
site=site,
|
||||
external_id=tag_id,
|
||||
taxonomy_type='tag'
|
||||
)
|
||||
content.taxonomies.add(taxonomy)
|
||||
except ContentTaxonomy.DoesNotExist:
|
||||
pass
|
||||
|
||||
if created:
|
||||
imported_count += 1
|
||||
|
||||
# Update last_synced
|
||||
config['last_synced'] = timezone.now().isoformat()
|
||||
integration.save()
|
||||
|
||||
return imported_count
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Backend API Endpoints
|
||||
|
||||
Add new actions to `IntegrationViewSet`:
|
||||
|
||||
```python
|
||||
@action(detail=True, methods=['post'], url_path='fetch-structure')
|
||||
def fetch_structure(self, request, pk=None):
|
||||
"""
|
||||
POST /api/v1/integration/integrations/{id}/fetch-structure/
|
||||
|
||||
Fetch content type structure from WordPress and store in config.
|
||||
"""
|
||||
integration = self.get_object()
|
||||
service = IntegrationService()
|
||||
|
||||
try:
|
||||
structure = service.fetch_content_structure(integration.id)
|
||||
|
||||
return success_response(
|
||||
data=structure,
|
||||
message="Content structure fetched successfully",
|
||||
request=request
|
||||
)
|
||||
except Exception as e:
|
||||
return error_response(
|
||||
error=str(e),
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
@action(detail=True, methods=['post'], url_path='import-taxonomies')
|
||||
def import_taxonomies(self, request, pk=None):
|
||||
"""
|
||||
POST /api/v1/integration/integrations/{id}/import-taxonomies/
|
||||
{
|
||||
"taxonomy_type": "category", // optional
|
||||
"limit": 100 // optional
|
||||
}
|
||||
|
||||
Import taxonomy terms from WordPress.
|
||||
"""
|
||||
integration = self.get_object()
|
||||
service = IntegrationService()
|
||||
|
||||
taxonomy_type = request.data.get('taxonomy_type')
|
||||
limit = request.data.get('limit')
|
||||
|
||||
try:
|
||||
count = service.import_taxonomies(integration.id, taxonomy_type, limit)
|
||||
|
||||
return success_response(
|
||||
data={'imported_count': count},
|
||||
message=f"Imported {count} taxonomy terms",
|
||||
request=request
|
||||
)
|
||||
except Exception as e:
|
||||
return error_response(
|
||||
error=str(e),
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
@action(detail=True, methods=['post'], url_path='import-content')
|
||||
def import_content(self, request, pk=None):
|
||||
"""
|
||||
POST /api/v1/integration/integrations/{id}/import-content/
|
||||
{
|
||||
"post_type": "post", // optional
|
||||
"limit": 100 // optional
|
||||
}
|
||||
|
||||
Import content titles from WordPress.
|
||||
"""
|
||||
integration = self.get_object()
|
||||
service = IntegrationService()
|
||||
|
||||
post_type = request.data.get('post_type')
|
||||
limit = request.data.get('limit')
|
||||
|
||||
try:
|
||||
count = service.import_content_titles(integration.id, post_type, limit)
|
||||
|
||||
return success_response(
|
||||
data={'imported_count': count},
|
||||
message=f"Imported {count} content items",
|
||||
request=request
|
||||
)
|
||||
except Exception as e:
|
||||
return error_response(
|
||||
error=str(e),
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
@action(detail=True, methods=['patch'], url_path='update-content-types')
|
||||
def update_content_types(self, request, pk=None):
|
||||
"""
|
||||
PATCH /api/v1/integration/integrations/{id}/update-content-types/
|
||||
{
|
||||
"post_types": {
|
||||
"post": {"enabled": true, "fetch_limit": 200}
|
||||
},
|
||||
"taxonomies": {
|
||||
"category": {"enabled": true, "fetch_limit": 150}
|
||||
}
|
||||
}
|
||||
|
||||
Update content type configuration.
|
||||
"""
|
||||
integration = self.get_object()
|
||||
|
||||
post_types = request.data.get('post_types', {})
|
||||
taxonomies = request.data.get('taxonomies', {})
|
||||
|
||||
# Update config
|
||||
if 'content_types' not in integration.config_json:
|
||||
integration.config_json['content_types'] = {'post_types': {}, 'taxonomies': {}}
|
||||
|
||||
for wp_type, updates in post_types.items():
|
||||
if wp_type in integration.config_json['content_types']['post_types']:
|
||||
integration.config_json['content_types']['post_types'][wp_type].update(updates)
|
||||
|
||||
for wp_tax, updates in taxonomies.items():
|
||||
if wp_tax in integration.config_json['content_types']['taxonomies']:
|
||||
integration.config_json['content_types']['taxonomies'][wp_tax].update(updates)
|
||||
|
||||
integration.save()
|
||||
|
||||
return success_response(
|
||||
data=integration.config_json['content_types'],
|
||||
message="Content types configuration updated",
|
||||
request=request
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Frontend UI - "Content Types" Tab
|
||||
|
||||
**Location:** Site Settings → Content Types
|
||||
|
||||
**Features:**
|
||||
1. Display fetched content types from `config_json`
|
||||
2. Enable/disable toggles per type
|
||||
3. Fetch limit inputs
|
||||
4. Last synced timestamps
|
||||
5. Sync buttons (Fetch Structure, Import Taxonomies, Import Content)
|
||||
|
||||
**API Calls:**
|
||||
```javascript
|
||||
// Fetch structure
|
||||
POST /api/v1/integration/integrations/{id}/fetch-structure/
|
||||
|
||||
// Update configuration
|
||||
PATCH /api/v1/integration/integrations/{id}/update-content-types/
|
||||
{
|
||||
"post_types": {
|
||||
"post": {"enabled": true, "fetch_limit": 200}
|
||||
}
|
||||
}
|
||||
|
||||
// Import taxonomies
|
||||
POST /api/v1/integration/integrations/{id}/import-taxonomies/
|
||||
|
||||
// Import content
|
||||
POST /api/v1/integration/integrations/{id}/import-content/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: Backend Service Methods ✅ READY TO IMPLEMENT
|
||||
- [ ] Add `fetch_content_structure()` to IntegrationService
|
||||
- [ ] Add `import_taxonomies()` to IntegrationService
|
||||
- [ ] Add `import_content_titles()` to IntegrationService
|
||||
- [ ] Add helper methods for WP type mapping
|
||||
|
||||
### Step 2: Backend API Endpoints ✅ READY TO IMPLEMENT
|
||||
- [ ] Add `fetch_structure` action to IntegrationViewSet
|
||||
- [ ] Add `import_taxonomies` action to IntegrationViewSet
|
||||
- [ ] Add `import_content` action to IntegrationViewSet
|
||||
- [ ] Add `update_content_types` action to IntegrationViewSet
|
||||
|
||||
### Step 3: Frontend UI ⏳ PENDING
|
||||
- [ ] Create "Content Types" tab component
|
||||
- [ ] Add post types list with toggles
|
||||
- [ ] Add taxonomies list with toggles
|
||||
- [ ] Add fetch limit inputs
|
||||
- [ ] Add sync buttons
|
||||
- [ ] Add last synced timestamps
|
||||
|
||||
### Step 4: Testing ⏳ PENDING
|
||||
- [ ] Test structure fetch from WP plugin
|
||||
- [ ] Test taxonomy import
|
||||
- [ ] Test content title import
|
||||
- [ ] Test configuration updates
|
||||
- [ ] Test UI interactions
|
||||
|
||||
---
|
||||
|
||||
## Migration Status
|
||||
|
||||
### ✅ Database Ready
|
||||
- All tables exist
|
||||
- All fields exist
|
||||
- All migrations applied
|
||||
|
||||
### ✅ Models Ready
|
||||
- ContentTaxonomy model complete
|
||||
- ContentAttribute model complete
|
||||
- Content model enhanced
|
||||
- SiteIntegration model ready
|
||||
|
||||
### ✅ Admin Ready
|
||||
- All admin interfaces updated
|
||||
- All filters configured
|
||||
|
||||
### ⏳ Services Pending
|
||||
- IntegrationService methods need implementation
|
||||
|
||||
### ⏳ API Endpoints Pending
|
||||
- IntegrationViewSet actions need implementation
|
||||
|
||||
### ⏳ Frontend Pending
|
||||
- Content Types tab needs creation
|
||||
|
||||
---
|
||||
|
||||
## Next Actions
|
||||
|
||||
**IMMEDIATE:**
|
||||
1. Implement IntegrationService methods (fetch_structure, import_taxonomies, import_content_titles)
|
||||
2. Add API endpoints to IntegrationViewSet
|
||||
3. Test with WordPress plugin
|
||||
|
||||
**SOON:**
|
||||
4. Create frontend "Content Types" tab
|
||||
5. Test end-to-end workflow
|
||||
6. Add AI semantic mapping endpoint
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**We are going in the RIGHT direction!** ✅
|
||||
|
||||
The unified content architecture is complete and production-ready. Now we need to:
|
||||
|
||||
1. **Store WP structure** in `SiteIntegration.config_json`
|
||||
2. **Add service methods** to fetch and import from WP
|
||||
3. **Add API endpoints** for frontend to trigger imports
|
||||
4. **Build frontend UI** to manage content types
|
||||
|
||||
The deleted migration file was incorrect (wrong location, wrong approach). The correct approach is to use `SiteIntegration.config_json` to store content type configuration, not database migrations.
|
||||
|
||||
**Status: Ready to implement backend service methods!**
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Test script to verify field rename is complete
|
||||
Run after SQL migration: python manage.py shell < test_field_rename.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import django
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings')
|
||||
django.setup()
|
||||
|
||||
from igny8_core.business.planning.models import ContentIdeas
|
||||
from igny8_core.business.content.models import Tasks, Content
|
||||
from django.db import connection
|
||||
|
||||
def test_models():
|
||||
"""Test that models can access new field names"""
|
||||
print("\n=== Testing Model Field Access ===")
|
||||
|
||||
# Test ContentIdeas
|
||||
try:
|
||||
idea = ContentIdeas.objects.first()
|
||||
if idea:
|
||||
print(f"✓ ContentIdeas.content_type: {idea.content_type}")
|
||||
print(f"✓ ContentIdeas.content_structure: {idea.content_structure}")
|
||||
else:
|
||||
print("⚠ No ContentIdeas records to test")
|
||||
except Exception as e:
|
||||
print(f"✗ ContentIdeas error: {e}")
|
||||
|
||||
# Test Tasks
|
||||
try:
|
||||
task = Tasks.objects.first()
|
||||
if task:
|
||||
print(f"✓ Tasks.content_type: {task.content_type}")
|
||||
print(f"✓ Tasks.content_structure: {task.content_structure}")
|
||||
else:
|
||||
print("⚠ No Tasks records to test")
|
||||
except Exception as e:
|
||||
print(f"✗ Tasks error: {e}")
|
||||
|
||||
# Test Content
|
||||
try:
|
||||
content = Content.objects.first()
|
||||
if content:
|
||||
print(f"✓ Content.content_type: {content.content_type}")
|
||||
print(f"✓ Content.content_structure: {content.content_structure}")
|
||||
print(f"✓ Content.content_html: {len(content.content_html) if content.content_html else 0} chars")
|
||||
else:
|
||||
print("⚠ No Content records to test")
|
||||
except Exception as e:
|
||||
print(f"✗ Content error: {e}")
|
||||
|
||||
def test_database_columns():
|
||||
"""Verify database columns exist"""
|
||||
print("\n=== Testing Database Column Names ===")
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
# Check igny8_content_ideas
|
||||
cursor.execute("""
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_name = 'igny8_content_ideas'
|
||||
AND column_name IN ('content_type', 'content_structure', 'site_entity_type', 'cluster_role')
|
||||
ORDER BY column_name
|
||||
""")
|
||||
cols = [row[0] for row in cursor.fetchall()]
|
||||
print(f"igny8_content_ideas columns: {cols}")
|
||||
if 'content_type' in cols and 'content_structure' in cols:
|
||||
print("✓ ContentIdeas: new columns exist")
|
||||
if 'site_entity_type' in cols or 'cluster_role' in cols:
|
||||
print("✗ ContentIdeas: old columns still exist")
|
||||
|
||||
# Check igny8_tasks
|
||||
cursor.execute("""
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_name = 'igny8_tasks'
|
||||
AND column_name IN ('content_type', 'content_structure', 'entity_type', 'cluster_role')
|
||||
ORDER BY column_name
|
||||
""")
|
||||
cols = [row[0] for row in cursor.fetchall()]
|
||||
print(f"igny8_tasks columns: {cols}")
|
||||
if 'content_type' in cols and 'content_structure' in cols:
|
||||
print("✓ Tasks: new columns exist")
|
||||
if 'entity_type' in cols or 'cluster_role' in cols:
|
||||
print("✗ Tasks: old columns still exist")
|
||||
|
||||
# Check igny8_content
|
||||
cursor.execute("""
|
||||
SELECT column_name FROM information_schema.columns
|
||||
WHERE table_name = 'igny8_content'
|
||||
AND column_name IN ('content_type', 'content_structure', 'content_html', 'entity_type', 'cluster_role', 'html_content')
|
||||
ORDER BY column_name
|
||||
""")
|
||||
cols = [row[0] for row in cursor.fetchall()]
|
||||
print(f"igny8_content columns: {cols}")
|
||||
if 'content_type' in cols and 'content_structure' in cols and 'content_html' in cols:
|
||||
print("✓ Content: new columns exist")
|
||||
if 'entity_type' in cols or 'cluster_role' in cols or 'html_content' in cols:
|
||||
print("✗ Content: old columns still exist")
|
||||
|
||||
def test_choices():
|
||||
"""Test that CHOICES are correctly defined"""
|
||||
print("\n=== Testing Model CHOICES ===")
|
||||
|
||||
print(f"ContentIdeas.CONTENT_TYPE_CHOICES: {len(ContentIdeas.CONTENT_TYPE_CHOICES)} types")
|
||||
print(f"ContentIdeas.CONTENT_STRUCTURE_CHOICES: {len(ContentIdeas.CONTENT_STRUCTURE_CHOICES)} structures")
|
||||
|
||||
print(f"Tasks.CONTENT_TYPE_CHOICES: {len(Tasks.CONTENT_TYPE_CHOICES)} types")
|
||||
print(f"Tasks.CONTENT_STRUCTURE_CHOICES: {len(Tasks.CONTENT_STRUCTURE_CHOICES)} structures")
|
||||
|
||||
print(f"Content.CONTENT_TYPE_CHOICES: {len(Content.CONTENT_TYPE_CHOICES)} types")
|
||||
print(f"Content.CONTENT_STRUCTURE_CHOICES: {len(Content.CONTENT_STRUCTURE_CHOICES)} structures")
|
||||
|
||||
# Verify all 14 structures are present
|
||||
all_structures = [s[0] for s in Tasks.CONTENT_STRUCTURE_CHOICES]
|
||||
expected = ['article', 'guide', 'comparison', 'review', 'listicle',
|
||||
'landing_page', 'business_page', 'service_page', 'general', 'cluster_hub',
|
||||
'product_page', 'category_archive', 'tag_archive', 'attribute_archive']
|
||||
|
||||
for struct in expected:
|
||||
if struct in all_structures:
|
||||
print(f"✓ {struct}")
|
||||
else:
|
||||
print(f"✗ Missing: {struct}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("="*60)
|
||||
print("Field Rename Validation Test")
|
||||
print("="*60)
|
||||
|
||||
test_models()
|
||||
test_database_columns()
|
||||
test_choices()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("Test Complete")
|
||||
print("="*60)
|
||||
@@ -1,174 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Test script to run inside Docker container to diagnose generate_content issues
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import django
|
||||
|
||||
# Setup Django
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings')
|
||||
django.setup()
|
||||
|
||||
import logging
|
||||
from django.contrib.auth import get_user_model
|
||||
from igny8_core.auth.models import Account
|
||||
from igny8_core.business.site_building.models import PageBlueprint, SiteBlueprint
|
||||
from igny8_core.business.site_building.services.page_generation_service import PageGenerationService
|
||||
from igny8_core.modules.system.models import IntegrationSettings
|
||||
from igny8_core.ai.settings import get_model_config
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def main():
|
||||
print("=" * 80)
|
||||
print("GENERATE_CONTENT DIAGNOSTIC TEST")
|
||||
print("=" * 80)
|
||||
|
||||
# 1. Test User Authentication and get Account
|
||||
print("\n1. Testing User Authentication...")
|
||||
User = get_user_model()
|
||||
user = User.objects.filter(email='dev@igny8.com').first()
|
||||
|
||||
if not user:
|
||||
print("❌ ERROR: User 'dev@igny8.com' not found!")
|
||||
return
|
||||
|
||||
print(f"✓ User found: {user.email} (ID: {user.id})")
|
||||
|
||||
# Get the associated account
|
||||
account = user.account if hasattr(user, 'account') else None
|
||||
if not account:
|
||||
print("❌ ERROR: User has no associated Account!")
|
||||
return
|
||||
|
||||
print(f"✓ Account found: {account.id}")
|
||||
|
||||
# 2. Check Integration Settings
|
||||
print("\n2. Checking Integration Settings...")
|
||||
try:
|
||||
integration = IntegrationSettings.objects.filter(account=account, is_active=True).first()
|
||||
if integration:
|
||||
print(f"✓ Integration found: {integration.integration_type}")
|
||||
print(f" - Config keys: {list(integration.config.keys()) if integration.config else 'None'}")
|
||||
print(f" - Active: {integration.is_active}")
|
||||
else:
|
||||
print("❌ WARNING: No active IntegrationSettings found!")
|
||||
print(" This will cause AI requests to fail!")
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR checking integration: {e}")
|
||||
|
||||
# 3. Test Model Configuration
|
||||
print("\n3. Testing Model Configuration...")
|
||||
try:
|
||||
model_config = get_model_config('generate_page_content', account=account)
|
||||
print(f"✓ Model config loaded:")
|
||||
print(f" - Model: {model_config.get('model')}")
|
||||
print(f" - Max tokens: {model_config.get('max_tokens')}")
|
||||
print(f" - Temperature: {model_config.get('temperature')}")
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# 4. Check for Site Blueprints
|
||||
print("\n4. Checking for Site Blueprints...")
|
||||
site_blueprints = SiteBlueprint.objects.filter(account=account)
|
||||
print(f" - Found {site_blueprints.count()} site blueprints")
|
||||
|
||||
if site_blueprints.exists():
|
||||
sb = site_blueprints.first()
|
||||
print(f" - First blueprint: {sb.name} (ID: {sb.id})")
|
||||
print(f" - Pages: {sb.pages.count()}")
|
||||
|
||||
# 5. Check for Page Blueprints
|
||||
print("\n5. Checking for Page Blueprints...")
|
||||
pages = PageBlueprint.objects.filter(account=account)
|
||||
print(f" - Found {pages.count()} page blueprints")
|
||||
|
||||
if not pages.exists():
|
||||
print("❌ WARNING: No page blueprints found! Creating a test page...")
|
||||
|
||||
# Create a test site blueprint if needed
|
||||
if not site_blueprints.exists():
|
||||
from igny8_core.modules.planner.models import Sector
|
||||
sector = Sector.objects.filter(account=account).first()
|
||||
|
||||
if not sector:
|
||||
print("❌ ERROR: No sector found for account. Cannot create test blueprint.")
|
||||
return
|
||||
|
||||
sb = SiteBlueprint.objects.create(
|
||||
account=account,
|
||||
sector=sector,
|
||||
name="Test Site Blueprint",
|
||||
site_type="business",
|
||||
status="draft"
|
||||
)
|
||||
print(f"✓ Created test site blueprint: {sb.id}")
|
||||
else:
|
||||
sb = site_blueprints.first()
|
||||
|
||||
# Create a test page
|
||||
page = PageBlueprint.objects.create(
|
||||
account=account,
|
||||
site_blueprint=sb,
|
||||
sector=sb.sector,
|
||||
title="Test Home Page",
|
||||
slug="home",
|
||||
type="home",
|
||||
status="draft",
|
||||
blocks_json=[
|
||||
{
|
||||
"type": "hero",
|
||||
"heading": "Welcome to Our Test Site",
|
||||
"subheading": "This is a test page for content generation"
|
||||
},
|
||||
{
|
||||
"type": "features",
|
||||
"heading": "Our Features"
|
||||
}
|
||||
]
|
||||
)
|
||||
print(f"✓ Created test page blueprint: {page.id}")
|
||||
else:
|
||||
page = pages.first()
|
||||
print(f" - Using existing page: {page.title} (ID: {page.id})")
|
||||
|
||||
# 6. Test generate_page_content
|
||||
print("\n6. Testing generate_page_content...")
|
||||
print(f" - Page ID: {page.id}")
|
||||
print(f" - Page title: {page.title}")
|
||||
print(f" - Page type: {page.type}")
|
||||
print(f" - Page status: {page.status}")
|
||||
print(f" - Blocks count: {len(page.blocks_json) if page.blocks_json else 0}")
|
||||
|
||||
try:
|
||||
service = PageGenerationService()
|
||||
print("\n Calling service.generate_page_content()...")
|
||||
result = service.generate_page_content(page, force_regenerate=False)
|
||||
|
||||
print(f"\n✓ SUCCESS!")
|
||||
print(f" Result: {result}")
|
||||
|
||||
except ValueError as e:
|
||||
print(f"\n❌ ValueError: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Exception: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print("TEST COMPLETE")
|
||||
print("=" * 80)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,129 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple test to check generate_content function execution
|
||||
Run this to see exact error messages
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import django
|
||||
import logging
|
||||
import json
|
||||
|
||||
# Setup Django
|
||||
sys.path.insert(0, '/data/app/igny8/backend')
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings')
|
||||
django.setup()
|
||||
|
||||
# Setup logging to see all messages
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s'
|
||||
)
|
||||
|
||||
from igny8_core.auth.models import Account
|
||||
from igny8_core.modules.writer.models import Tasks
|
||||
from igny8_core.ai.registry import get_function_instance
|
||||
from igny8_core.ai.engine import AIEngine
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("SIMPLE GENERATE_CONTENT TEST")
|
||||
print("="*80 + "\n")
|
||||
|
||||
# Get first account and task
|
||||
try:
|
||||
account = Account.objects.first()
|
||||
print(f"✓ Account: {account.id} - {account.email}")
|
||||
except Exception as e:
|
||||
print(f"✗ Failed to get account: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
task = Tasks.objects.filter(account=account).first()
|
||||
if not task:
|
||||
print("✗ No tasks found")
|
||||
sys.exit(1)
|
||||
print(f"✓ Task: {task.id} - {task.title or 'Untitled'}")
|
||||
print(f" Status: {task.status}")
|
||||
print(f" Cluster: {task.cluster.name if task.cluster else 'None'}")
|
||||
print(f" Taxonomy: {task.taxonomy_term.name if task.taxonomy_term else 'None'}")
|
||||
except Exception as e:
|
||||
print(f"✗ Failed to get task: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Get function
|
||||
try:
|
||||
fn = get_function_instance('generate_content')
|
||||
print(f"✓ Function loaded: {fn.get_name()}")
|
||||
except Exception as e:
|
||||
print(f"✗ Failed to load function: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
# Test validation
|
||||
print("\nTesting validation...")
|
||||
try:
|
||||
payload = {'ids': [task.id]}
|
||||
result = fn.validate(payload, account)
|
||||
if result['valid']:
|
||||
print(f"✓ Validation passed")
|
||||
else:
|
||||
print(f"✗ Validation failed: {result.get('error')}")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"✗ Validation error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
# Test prepare
|
||||
print("\nTesting prepare...")
|
||||
try:
|
||||
data = fn.prepare(payload, account)
|
||||
print(f"✓ Prepare succeeded: {len(data) if isinstance(data, list) else 1} task(s)")
|
||||
except Exception as e:
|
||||
print(f"✗ Prepare error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
# Test build_prompt
|
||||
print("\nTesting build_prompt...")
|
||||
try:
|
||||
prompt = fn.build_prompt(data, account)
|
||||
print(f"✓ Prompt built: {len(prompt)} characters")
|
||||
print(f"\nPrompt preview (first 500 chars):")
|
||||
print("-" * 80)
|
||||
print(prompt[:500])
|
||||
print("-" * 80)
|
||||
except Exception as e:
|
||||
print(f"✗ Build prompt error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
# Test model config
|
||||
print("\nTesting model config...")
|
||||
try:
|
||||
from igny8_core.ai.settings import get_model_config
|
||||
model_config = get_model_config('generate_content', account=account)
|
||||
print(f"✓ Model config loaded:")
|
||||
print(f" Model: {model_config.get('model')}")
|
||||
print(f" Max tokens: {model_config.get('max_tokens')}")
|
||||
print(f" Temperature: {model_config.get('temperature')}")
|
||||
except Exception as e:
|
||||
print(f"✗ Model config error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("All tests passed! The function structure is correct.")
|
||||
print("If content generation still fails, the issue is likely:")
|
||||
print("1. API key is invalid or missing")
|
||||
print("2. OpenAI API error (rate limit, quota, etc.)")
|
||||
print("3. Prompt is too long or has invalid format")
|
||||
print("4. Celery worker is not running or has errors")
|
||||
print("\nCheck Celery worker logs with:")
|
||||
print(" journalctl -u celery-worker -f")
|
||||
print("="*80 + "\n")
|
||||
@@ -1,162 +0,0 @@
|
||||
"""
|
||||
Test Script to Manually Push WordPress Structure to IGNY8 Backend
|
||||
This simulates what the WordPress plugin would send
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
# Configuration
|
||||
API_BASE = "https://api.igny8.com/api/v1"
|
||||
SITE_ID = 5 # From the URL: https://app.igny8.com/sites/5/settings
|
||||
|
||||
# You need to get an API key from Django admin or use your access token
|
||||
# For now, this script shows what data should be sent
|
||||
API_KEY = "YOUR_API_KEY_HERE" # Replace with actual API key
|
||||
|
||||
# Step 1: Get the integration ID for this WordPress site
|
||||
def get_integration_id():
|
||||
url = f"{API_BASE}/integration/integrations/"
|
||||
params = {"site": SITE_ID, "platform": "wordpress"}
|
||||
headers = {"Authorization": f"Bearer {API_KEY}"}
|
||||
|
||||
response = requests.get(url, params=params, headers=headers)
|
||||
print(f"Get Integration Response: {response.status_code}")
|
||||
print(json.dumps(response.json(), indent=2))
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
# Handle paginated response
|
||||
if 'results' in data:
|
||||
integrations = data['results']
|
||||
elif isinstance(data, list):
|
||||
integrations = data
|
||||
else:
|
||||
integrations = [data]
|
||||
|
||||
if integrations:
|
||||
return integrations[0]['id']
|
||||
|
||||
return None
|
||||
|
||||
# Step 2: Push WordPress structure to backend
|
||||
def push_structure(integration_id):
|
||||
url = f"{API_BASE}/integration/integrations/{integration_id}/update-structure/"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {API_KEY}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# Sample WordPress structure data
|
||||
payload = {
|
||||
"post_types": {
|
||||
"post": {
|
||||
"label": "Posts",
|
||||
"count": 150,
|
||||
"enabled": True,
|
||||
"fetch_limit": 100
|
||||
},
|
||||
"page": {
|
||||
"label": "Pages",
|
||||
"count": 25,
|
||||
"enabled": True,
|
||||
"fetch_limit": 100
|
||||
},
|
||||
"product": {
|
||||
"label": "Products",
|
||||
"count": 89,
|
||||
"enabled": True,
|
||||
"fetch_limit": 100
|
||||
}
|
||||
},
|
||||
"taxonomies": {
|
||||
"category": {
|
||||
"label": "Categories",
|
||||
"count": 15,
|
||||
"enabled": True,
|
||||
"fetch_limit": 100
|
||||
},
|
||||
"post_tag": {
|
||||
"label": "Tags",
|
||||
"count": 234,
|
||||
"enabled": True,
|
||||
"fetch_limit": 100
|
||||
},
|
||||
"product_cat": {
|
||||
"label": "Product Categories",
|
||||
"count": 12,
|
||||
"enabled": True,
|
||||
"fetch_limit": 100
|
||||
}
|
||||
},
|
||||
"timestamp": "2025-11-22T04:20:00+00:00",
|
||||
"site_url": "https://example.com",
|
||||
"wordpress_version": "6.4",
|
||||
"plugin_connection_enabled": True,
|
||||
"two_way_sync_enabled": True
|
||||
}
|
||||
|
||||
response = requests.post(url, json=payload, headers=headers)
|
||||
print(f"\nPush Structure Response: {response.status_code}")
|
||||
print(json.dumps(response.json(), indent=2))
|
||||
|
||||
return response.status_code == 200
|
||||
|
||||
# Step 3: Verify the data was stored
|
||||
def verify_content_types(integration_id):
|
||||
url = f"{API_BASE}/integration/integrations/{integration_id}/content-types/"
|
||||
headers = {"Authorization": f"Bearer {API_KEY}"}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
print(f"\nGet Content Types Response: {response.status_code}")
|
||||
print(json.dumps(response.json(), indent=2))
|
||||
|
||||
return response.status_code == 200
|
||||
|
||||
# Main execution
|
||||
if __name__ == "__main__":
|
||||
print("=== WordPress Structure Push Test ===\n")
|
||||
print(f"Site ID: {SITE_ID}")
|
||||
print(f"API Base: {API_BASE}\n")
|
||||
|
||||
if API_KEY == "YOUR_API_KEY_HERE":
|
||||
print("ERROR: Please set your API_KEY in the script!")
|
||||
print("\nTo get an API key:")
|
||||
print("1. Go to Django admin")
|
||||
print("2. Generate a WordPress API key for your site")
|
||||
print("3. Replace 'YOUR_API_KEY_HERE' in this script")
|
||||
exit(1)
|
||||
|
||||
# Step 1
|
||||
print("Step 1: Getting Integration ID...")
|
||||
integration_id = get_integration_id()
|
||||
|
||||
if not integration_id:
|
||||
print("\nERROR: Could not find WordPress integration for site!")
|
||||
print("Make sure:")
|
||||
print("1. The site exists in the database")
|
||||
print("2. A WordPress integration exists for this site")
|
||||
print("3. Your API key is valid")
|
||||
exit(1)
|
||||
|
||||
print(f"\n✓ Found Integration ID: {integration_id}")
|
||||
|
||||
# Step 2
|
||||
print("\nStep 2: Pushing WordPress structure...")
|
||||
if push_structure(integration_id):
|
||||
print("\n✓ Structure pushed successfully!")
|
||||
else:
|
||||
print("\n✗ Failed to push structure!")
|
||||
exit(1)
|
||||
|
||||
# Step 3
|
||||
print("\nStep 3: Verifying content types...")
|
||||
if verify_content_types(integration_id):
|
||||
print("\n✓ Content types verified!")
|
||||
print("\n=== TEST COMPLETE ===")
|
||||
print("\nNow refresh the frontend page:")
|
||||
print(f"https://app.igny8.com/sites/{SITE_ID}/settings?tab=content-types")
|
||||
else:
|
||||
print("\n✗ Could not verify content types!")
|
||||
exit(1)
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Test generate_content for Writer tasks"""
|
||||
import os
|
||||
import sys
|
||||
import django
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings')
|
||||
django.setup()
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from igny8_core.business.content.models import Tasks
|
||||
from igny8_core.business.content.services.content_generation_service import ContentGenerationService
|
||||
|
||||
print("=" * 80)
|
||||
print("TESTING WRITER GENERATE_CONTENT")
|
||||
print("=" * 80)
|
||||
|
||||
User = get_user_model()
|
||||
user = User.objects.filter(email='dev@igny8.com').first()
|
||||
|
||||
if not user:
|
||||
print("❌ User not found")
|
||||
sys.exit(1)
|
||||
|
||||
account = user.account if hasattr(user, 'account') else None
|
||||
|
||||
if not account:
|
||||
print("❌ No account found for user")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"✓ User: {user.email}")
|
||||
print(f"✓ Account: {account.id}")
|
||||
|
||||
# Get tasks
|
||||
tasks = Tasks.objects.filter(account=account, status='queued')[:5]
|
||||
print(f"\n✓ Found {tasks.count()} queued tasks")
|
||||
|
||||
if tasks.exists():
|
||||
task = tasks.first()
|
||||
print(f"\nTesting with task:")
|
||||
print(f" - ID: {task.id}")
|
||||
print(f" - Title: {task.title}")
|
||||
print(f" - Status: {task.status}")
|
||||
print(f" - Cluster: {task.cluster.name if task.cluster else 'None'}")
|
||||
|
||||
print("\nCalling generate_content service...")
|
||||
service = ContentGenerationService()
|
||||
|
||||
try:
|
||||
result = service.generate_content([task.id], account)
|
||||
print(f"\n✓ SUCCESS!")
|
||||
print(f"Result: {result}")
|
||||
except Exception as e:
|
||||
print(f"\n❌ ERROR: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print("\n⚠️ No queued tasks found. Creating a test task...")
|
||||
|
||||
from igny8_core.modules.planner.models import Clusters
|
||||
cluster = Clusters.objects.filter(account=account).first()
|
||||
|
||||
if not cluster:
|
||||
print("❌ No clusters found. Cannot create test task.")
|
||||
sys.exit(1)
|
||||
|
||||
task = Tasks.objects.create(
|
||||
account=account,
|
||||
site=cluster.site,
|
||||
sector=cluster.sector,
|
||||
cluster=cluster,
|
||||
title="Test Article: Benefits of Organic Cotton Bedding",
|
||||
description="Comprehensive guide covering health benefits, environmental impact, and buying guide",
|
||||
content_type='post',
|
||||
content_structure='article',
|
||||
status='queued'
|
||||
)
|
||||
print(f"✓ Created test task: {task.id} - {task.title}")
|
||||
|
||||
print("\nCalling generate_content service...")
|
||||
service = ContentGenerationService()
|
||||
|
||||
try:
|
||||
result = service.generate_content([task.id], account)
|
||||
print(f"\n✓ SUCCESS!")
|
||||
print(f"Result: {result}")
|
||||
except Exception as e:
|
||||
print(f"\n❌ ERROR: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print("TEST COMPLETE")
|
||||
print("=" * 80)
|
||||
@@ -1,687 +0,0 @@
|
||||
0. GOAL
|
||||
|
||||
When this refactor is complete:
|
||||
|
||||
You enter keywords in Planner.
|
||||
|
||||
Clusters and ideas are created.
|
||||
|
||||
Writer generates tasks and content.
|
||||
|
||||
Content Manager becomes the single source of truth.
|
||||
|
||||
One click publish sends content to WordPress.
|
||||
|
||||
All mappings (cluster, taxonomies, site) are correct.
|
||||
|
||||
Linker, Optimizer, IGNY8 hosted sites stay out of scope for now.
|
||||
|
||||
This spec assumes all earlier decisions you made about models and statuses are final.
|
||||
|
||||
1. END TO END FLOW (KEYWORD TO PUBLISH)
|
||||
1.1 High level flow
|
||||
|
||||
Planner
|
||||
|
||||
Input keywords
|
||||
|
||||
Generate clusters
|
||||
|
||||
Generate ideas per cluster
|
||||
|
||||
Convert ideas into Writer tasks
|
||||
|
||||
Writer
|
||||
|
||||
Tasks created with content_type and content_structure
|
||||
|
||||
AI generates draft content
|
||||
|
||||
Task marked completed
|
||||
|
||||
Content saved as draft entry
|
||||
|
||||
Content Manager
|
||||
|
||||
Lists all content for a site (imported and AI written)
|
||||
|
||||
You edit, assign cluster and taxonomies if needed
|
||||
|
||||
You click publish for selected content
|
||||
|
||||
Publish to WordPress
|
||||
|
||||
IGNY8 sends content HTML and metadata to WP via REST
|
||||
|
||||
WP creates or updates post / page / product
|
||||
|
||||
WP returns ID and URL
|
||||
|
||||
IGNY8 saves external_id and external_url
|
||||
|
||||
Content status goes from draft to published
|
||||
|
||||
Task status already completed when content was generated
|
||||
|
||||
Only Content has draft/published.
|
||||
Only Task has queued/completed.
|
||||
Planner and Writer do not track sync.
|
||||
|
||||
2. BACKEND DATA MODEL (FINAL)
|
||||
|
||||
This is the corrected model set the whole system is built on.
|
||||
|
||||
2.1 Cluster
|
||||
|
||||
Pure topic. Parent of everything else.
|
||||
|
||||
Not tied to a content type.
|
||||
|
||||
Fields:
|
||||
|
||||
id
|
||||
|
||||
site
|
||||
|
||||
sector
|
||||
|
||||
name
|
||||
|
||||
description
|
||||
|
||||
created_at
|
||||
|
||||
updated_at
|
||||
|
||||
Remove: context_type, dimension_meta.
|
||||
|
||||
2.2 Task
|
||||
|
||||
Planner → Writer contract for content creation.
|
||||
|
||||
Fields:
|
||||
|
||||
id
|
||||
|
||||
site
|
||||
|
||||
sector
|
||||
|
||||
cluster (FK → Cluster, required)
|
||||
|
||||
content_type (Article, Page, Product, Taxonomy)
|
||||
|
||||
content_structure (Review, Comparison, Tutorial, Listicle, Landing Page, Archive Page, etc)
|
||||
|
||||
taxonomy_term (FK → ContentTaxonomy, optional, used when targeting a taxonomy archive)
|
||||
|
||||
keywords (M2M → Keyword)
|
||||
|
||||
status (queued, completed)
|
||||
|
||||
created_at
|
||||
|
||||
updated_at
|
||||
|
||||
Remove: cluster_role, sync_status.
|
||||
|
||||
2.3 Content
|
||||
|
||||
Writer output and imported WP content, managed in Content Manager.
|
||||
|
||||
Fields:
|
||||
|
||||
id
|
||||
|
||||
site
|
||||
|
||||
sector
|
||||
|
||||
cluster (FK → Cluster, required)
|
||||
|
||||
content_type (Article, Page, Product, Taxonomy)
|
||||
|
||||
content_structure
|
||||
|
||||
title
|
||||
|
||||
content_html
|
||||
|
||||
taxonomy_terms (M2M → ContentTaxonomy)
|
||||
|
||||
external_id (WP post_id, nullable)
|
||||
|
||||
external_url (WP permalink, nullable)
|
||||
|
||||
status (draft, published)
|
||||
|
||||
source (igny8, wordpress)
|
||||
|
||||
created_at
|
||||
|
||||
updated_at
|
||||
|
||||
Remove: cluster_role, sync_status.
|
||||
|
||||
2.4 ContentTaxonomy
|
||||
|
||||
WordPress taxonomies and IGNY8 cluster-like taxonomies.
|
||||
|
||||
Fields:
|
||||
|
||||
id
|
||||
|
||||
site
|
||||
|
||||
sector
|
||||
|
||||
name
|
||||
|
||||
slug
|
||||
|
||||
taxonomy_type (category, tag, product_category, product_attribute, cluster, service_category if you keep it)
|
||||
|
||||
external_taxonomy (WP taxonomy slug, e.g. category, post_tag, product_cat, pa_size; null for cluster)
|
||||
|
||||
external_id (WP term_id; null for igy8 custom terms and clusters)
|
||||
|
||||
created_at
|
||||
|
||||
updated_at
|
||||
|
||||
Remove: sync_status.
|
||||
|
||||
3. WORDPRESS ↔ IGNY8 DATA FLOWS
|
||||
3.1 WordPress → IGNY8 (import / sync)
|
||||
Plugin endpoints
|
||||
|
||||
WordPress plugin provides:
|
||||
|
||||
/wp-json/igny8/v1/site-metadata
|
||||
|
||||
/wp-json/igny8/v1/posts
|
||||
|
||||
/wp-json/igny8/v1/taxonomies
|
||||
|
||||
Possibly specific endpoints like /post-by-task-id, /post-by-content-id
|
||||
|
||||
Plugin responsibilities:
|
||||
|
||||
Export posts, pages, products with:
|
||||
|
||||
post_id
|
||||
|
||||
post_type
|
||||
|
||||
post_title
|
||||
|
||||
post_content
|
||||
|
||||
permalink
|
||||
|
||||
taxonomies (term_ids + taxonomy slugs)
|
||||
|
||||
meta keys for any IGNY8 links (optional)
|
||||
|
||||
Export taxonomies:
|
||||
|
||||
term_id
|
||||
|
||||
name
|
||||
|
||||
slug
|
||||
|
||||
taxonomy
|
||||
|
||||
IGNY8 backend behaviour
|
||||
|
||||
When importing posts:
|
||||
|
||||
Create Content rows:
|
||||
|
||||
site, sector inferred from site record
|
||||
|
||||
cluster: null initially (to be assigned later)
|
||||
|
||||
content_type mapped from WP post_type:
|
||||
|
||||
post ⇒ Article
|
||||
|
||||
page ⇒ Page
|
||||
|
||||
product ⇒ Product
|
||||
|
||||
content_structure: optional default (e.g. ImportedArticle, ImportedPage)
|
||||
|
||||
title, content_html from WP
|
||||
|
||||
external_id = post_id
|
||||
|
||||
external_url = permalink
|
||||
|
||||
status = draft
|
||||
|
||||
source = wordpress
|
||||
|
||||
Link taxonomy terms:
|
||||
|
||||
For each term on the WP post:
|
||||
|
||||
upsert ContentTaxonomy by external_id + external_taxonomy
|
||||
|
||||
attach to Content.taxonomy_terms
|
||||
|
||||
When importing taxonomies:
|
||||
|
||||
For each WP term:
|
||||
|
||||
Create ContentTaxonomy if it does not exist:
|
||||
|
||||
name
|
||||
|
||||
slug
|
||||
|
||||
taxonomy_type derived from taxonomy (category, tag, product_category, product_attribute)
|
||||
|
||||
external_taxonomy = WP taxonomy slug
|
||||
|
||||
external_id = term_id
|
||||
|
||||
No sync_status is needed.
|
||||
Imported items are identified by source=wordpress and external_id present.
|
||||
|
||||
3.2 IGNY8 → WordPress (publish)
|
||||
|
||||
Publishing is done only from Content Manager, not from Planner or Writer.
|
||||
|
||||
Backend publishing service
|
||||
|
||||
Given a Content id:
|
||||
|
||||
Load Content:
|
||||
|
||||
ensure status = draft
|
||||
|
||||
ensure site record configured with WP endpoint and auth
|
||||
|
||||
Prepare payload:
|
||||
|
||||
post_type based on content_type
|
||||
|
||||
post_title from Content.title
|
||||
|
||||
post_content from Content.content_html
|
||||
|
||||
tax_input from Content.taxonomy_terms mapped through external_taxonomy and external_id
|
||||
|
||||
Call WordPress REST:
|
||||
|
||||
POST /wp-json/wp/v2/{post_type}
|
||||
|
||||
On success:
|
||||
|
||||
response.id ⇒ Content.external_id
|
||||
|
||||
response.link ⇒ Content.external_url
|
||||
|
||||
Content.status ⇒ published
|
||||
|
||||
Do not touch Task; Task was marked completed when content was generated.
|
||||
|
||||
If the Content row already has an external_id, publishing should update that post instead of creating a new one.
|
||||
|
||||
WordPress plugin side
|
||||
|
||||
Accept incoming POST from IGNY8:
|
||||
|
||||
Validate token / key
|
||||
|
||||
Create or update the post:
|
||||
|
||||
set post_title, post_content, post_type, post_status=publish
|
||||
|
||||
set taxonomies based on term IDs
|
||||
|
||||
Return:
|
||||
|
||||
id
|
||||
|
||||
link
|
||||
|
||||
Plugin may optionally store meta keys like _igny8_content_id or _igny8_cluster_id, but this is optional.
|
||||
|
||||
4. CONTENT MANAGER AS SOURCE OF TRUTH
|
||||
|
||||
Content Manager is the last part of the flow.
|
||||
|
||||
4.1 Role
|
||||
|
||||
Content Manager:
|
||||
|
||||
Shows all content for a site:
|
||||
|
||||
imported from WP
|
||||
|
||||
generated by Writer
|
||||
|
||||
Allows editing and assigning:
|
||||
|
||||
cluster
|
||||
|
||||
taxonomy_terms
|
||||
|
||||
Controls publish to WordPress.
|
||||
|
||||
Reflects the final status for each content item:
|
||||
|
||||
draft
|
||||
|
||||
published
|
||||
|
||||
All other modules are upstream.
|
||||
They feed into Content Manager, but do not override its status.
|
||||
|
||||
4.2 Content Manager data model usage
|
||||
|
||||
Content listing uses:
|
||||
|
||||
Content.id
|
||||
|
||||
Content.title
|
||||
|
||||
Content.content_type
|
||||
|
||||
Content.content_structure
|
||||
|
||||
Content.cluster
|
||||
|
||||
Content.taxonomy_terms
|
||||
|
||||
Content.status
|
||||
|
||||
Content.external_id
|
||||
|
||||
Content.external_url
|
||||
|
||||
Content.source
|
||||
|
||||
Content.created_at / updated_at
|
||||
|
||||
4.3 Content Manager UX requirements
|
||||
|
||||
Table columns:
|
||||
|
||||
Title
|
||||
|
||||
Type (Article / Page / Product / Taxonomy)
|
||||
|
||||
Structure (Review, Landing Page, Archive Page, etc)
|
||||
|
||||
Cluster (with link to cluster detail)
|
||||
|
||||
Taxonomies (comma separated list of taxonomy term names)
|
||||
|
||||
Status (draft/published)
|
||||
|
||||
Source (IGNY8, WordPress)
|
||||
|
||||
URL (clickable if published)
|
||||
|
||||
Actions (Edit, Publish, View in WordPress)
|
||||
|
||||
Filters:
|
||||
|
||||
content_type
|
||||
|
||||
status
|
||||
|
||||
cluster
|
||||
|
||||
taxonomy_type (optional)
|
||||
|
||||
source
|
||||
|
||||
Row actions:
|
||||
|
||||
Edit:
|
||||
|
||||
Opens editor on Content:
|
||||
|
||||
edit title
|
||||
|
||||
edit content_html
|
||||
|
||||
attach or change cluster
|
||||
|
||||
attach or change taxonomy_terms
|
||||
|
||||
Publish:
|
||||
|
||||
Calls backend publish service
|
||||
|
||||
On success:
|
||||
|
||||
status becomes published
|
||||
|
||||
external_id and external_url are filled
|
||||
|
||||
View in WordPress:
|
||||
|
||||
Opens external_url in new tab
|
||||
|
||||
4.4 Cluster and taxonomy assignment rules
|
||||
|
||||
New content generated from tasks:
|
||||
|
||||
cluster is assigned automatically from the Task.cluster
|
||||
|
||||
taxonomy_terms can be empty initially
|
||||
|
||||
Imported content from WordPress:
|
||||
|
||||
cluster must be manually assigned in Content Manager or via auto mapping later
|
||||
|
||||
taxonomy_terms are imported automatically
|
||||
|
||||
Content Manager must allow:
|
||||
|
||||
batch cluster assignment:
|
||||
|
||||
select multiple rows
|
||||
|
||||
assign one cluster
|
||||
|
||||
batch taxonomy assignment:
|
||||
|
||||
select rows
|
||||
|
||||
attach a taxonomy term
|
||||
|
||||
5. FRONTEND MODULES ALIGNED WITH THIS FLOW
|
||||
5.1 Planner
|
||||
|
||||
Key UI responsibilities:
|
||||
|
||||
Keywords input and cluster generation
|
||||
|
||||
Idea generation per cluster
|
||||
|
||||
Create tasks that are passed to Writer
|
||||
|
||||
Data it needs to pass to Writer:
|
||||
|
||||
cluster_id
|
||||
|
||||
content_type
|
||||
|
||||
content_structure
|
||||
|
||||
primary_keyword
|
||||
|
||||
any extra parameters (tone, target country etc)
|
||||
|
||||
5.2 Writer
|
||||
|
||||
Key UI responsibilities:
|
||||
|
||||
List tasks with status (queued/completed)
|
||||
|
||||
For each task:
|
||||
|
||||
show cluster
|
||||
|
||||
content_type
|
||||
|
||||
content_structure
|
||||
|
||||
primary_keyword
|
||||
|
||||
Actions:
|
||||
|
||||
Generate draft content
|
||||
|
||||
View generated draft
|
||||
|
||||
Writer does not publish and does not talk to WordPress directly.
|
||||
|
||||
5.3 Sites
|
||||
|
||||
For this DDAY refactor, keep only:
|
||||
|
||||
grid view of sites
|
||||
|
||||
for each site card:
|
||||
|
||||
Dashboard button
|
||||
|
||||
Content Manager button
|
||||
|
||||
Settings button
|
||||
|
||||
Active/inactive toggle at the top
|
||||
|
||||
Remove:
|
||||
|
||||
Pages button and its backend logic
|
||||
|
||||
Sectors button from card (moved into Site Settings tab)
|
||||
|
||||
Site builder and blueprints buttons (out of scope)
|
||||
|
||||
The Content Manager for a site is reachable from the site card.
|
||||
|
||||
5.4 Cluster Detail Page
|
||||
|
||||
When clicking a cluster:
|
||||
|
||||
Show:
|
||||
|
||||
cluster name and description
|
||||
|
||||
tabs or sections per content_type:
|
||||
|
||||
Articles
|
||||
|
||||
Pages
|
||||
|
||||
Products
|
||||
|
||||
Taxonomy Pages
|
||||
|
||||
each section listing content entries linked to that cluster
|
||||
|
||||
This view can internally reuse the Content Manager data but filtered by cluster.
|
||||
|
||||
6. STATE MACHINE SUMMARY
|
||||
6.1 Task
|
||||
|
||||
queued:
|
||||
|
||||
created from Planner or manually
|
||||
|
||||
completed:
|
||||
|
||||
AI content generated and saved to Content table
|
||||
|
||||
Task never changes when publish happens.
|
||||
|
||||
6.2 Content
|
||||
|
||||
draft:
|
||||
|
||||
created from Writer completion or from WordPress import
|
||||
|
||||
published:
|
||||
|
||||
after successful publish to WordPress
|
||||
|
||||
There is no separate sync_status in this refactor.
|
||||
If external_id is present and status is published, it is live.
|
||||
|
||||
7. IMPLEMENTATION CHECKLIST
|
||||
|
||||
To actually complete this DDAY refactor:
|
||||
|
||||
Backend
|
||||
|
||||
Update models:
|
||||
|
||||
Remove fields: cluster_role, sync_status, context_type, dimension_meta, ContentTaxonomy.sync_status
|
||||
|
||||
Add or rename fields: taxonomy_term on Task, taxonomy_terms M2M on Content if not already consistent
|
||||
|
||||
Regenerate migrations.
|
||||
|
||||
Update serializers:
|
||||
|
||||
Remove removed fields
|
||||
|
||||
Add or rename fields accordingly
|
||||
|
||||
Review all viewsets and services:
|
||||
|
||||
Task creation and listing
|
||||
|
||||
Content creation, listing, edit
|
||||
|
||||
WordPress import service
|
||||
|
||||
WordPress publish service
|
||||
|
||||
Confirm WordPress plugin endpoints match expected payloads.
|
||||
|
||||
Frontend
|
||||
|
||||
Planner:
|
||||
|
||||
Ensure ideas create tasks with cluster_id, content_type, content_structure.
|
||||
|
||||
Writer:
|
||||
|
||||
Only show queued/completed status.
|
||||
|
||||
No sync related fields.
|
||||
|
||||
Link to open draft in Content Manager (optional but helpful).
|
||||
|
||||
Sites:
|
||||
|
||||
Remove builder-related buttons.
|
||||
|
||||
Only show Dashboard, Content, Settings.
|
||||
|
||||
Move toggle and sectors UI as per your earlier 18 points.
|
||||
|
||||
Content Manager:
|
||||
|
||||
Implement table with final columns and filters.
|
||||
|
||||
Implement Edit and Publish actions as defined.
|
||||
|
||||
Ensure every content row came from Content model only.
|
||||
|
||||
Integrate cluster and taxonomy selectors.
|
||||
|
||||
Cluster detail:
|
||||
|
||||
Filter Content list by cluster_id and group by content_type
|
||||
Reference in New Issue
Block a user