36 KiB
Writer Module Comprehensive Refactoring Plan
Date: December 2024
Purpose: Complete refactoring of Writer module pages (Tasks, Content, Images, Published) based on deep analysis and user requirements
Executive Summary
This document outlines a comprehensive refactoring plan for the IGNY8 Writer module. After deep analysis of all Writer pages, configurations, and data flows, we've identified critical issues and improvements needed to create a cohesive, efficient content workflow from task creation through WordPress publishing.
Key Objectives:
- Fix critical bugs (bulk select, delete functions)
- Restructure Published page to show Content (not Tasks)
- Implement proper WordPress publishing workflow
- Add "review" status to content lifecycle
- Improve UX with better status indicators and content viewing
Current State Analysis
Page Structure Overview
Writer Module Flow:
Tasks (queued/completed) → Content (draft/published) → Images (pending/generated/failed) → Published (?)
1. Tasks Page (Tasks.tsx - 787 lines)
Purpose: Task queue management for content generation
Current Implementation:
- ✅ Loads Tasks table via
fetchTasks()API - ✅ Status workflow:
queued→completed - ✅ Row actions: Edit, Generate Content
- ✅ Bulk actions: Update Status, Export
- ✅ Progress modal for AI functions
- ✅ Selection and pagination working
- ✅ AI logs when Resource Debug enabled
Issues:
- None critical
Data Model:
interface Task {
id: number;
title: string;
cluster: string;
taxonomy: string;
content_type: string;
content_structure: string;
status: 'queued' | 'completed';
word_count: number;
created_at: string;
}
2. Content Page (Content.tsx - 315 lines)
Purpose: Content management and editing
Current Implementation:
- ✅ Loads Content table via
fetchContent()API - ✅ Status workflow:
draft→published - ✅ Row actions: Edit, View on WordPress (if published), Generate Image Prompts
- ✅ Bulk actions: Update Status, Export, Publish Selected
- ✅ Progress modal for image prompt generation
- ✅ Selection and pagination working
Issues:
- ❌ No content viewer link - Title column not clickable to view content
- ❌ Missing status indicators - No visual feedback for:
- Prompt generation status (pending/complete)
- Image generation status (pending/generating/complete)
- ⚠️ Publish action ambiguity - Bulk "Publish Selected" action exists but unclear what it does
Data Model:
interface ContentType {
id: number;
title: string;
sector: string;
content_type: string;
content_structure: string;
cluster: string;
taxonomy: string;
status: 'draft' | 'published';
word_count: number;
source: string;
created_at: string;
external_id?: string | null; // WordPress post ID
external_url?: string | null; // WordPress post URL
sync_status?: string;
}
Configuration (content.config.tsx - 376 lines):
- Columns: title, sector, content_type, content_structure, cluster, taxonomy, status, word_count, source, created_at
- Missing columns: prompts_status, images_status
3. Images Page (Images.tsx - 738 lines)
Purpose: Image management grouped by content
Current Implementation:
- ✅ Loads ContentImagesGroup via
fetchContentImages()API - ✅ Client-side filtering, sorting, pagination
- ✅ Row actions: Publish to WordPress, Update Status
- ✅ Bulk actions: Bulk Publish Ready to WordPress
- ✅ Image generation functionality with queue modal
- ✅ AI logs when Resource Debug enabled
Issues:
- ❌ Bulk select checkbox not working - Root cause:
ContentImagesGroupinterface hascontent_idbut TablePageTemplate expectsidfield for selection - ❌ Delete functions not working - No delete handlers implemented
- ⚠️ Wrong location for WordPress publishing - Should be on Published page, not here
Data Model:
interface ContentImagesGroup {
content_id: number; // ⚠️ No 'id' field - breaks selection!
content_title: string;
featured_image: ContentImage | null;
in_article_images: ContentImage[];
overall_status: 'pending' | 'partial' | 'complete' | 'failed';
// Missing fields for WordPress publishing context:
external_id?: string;
external_url?: string;
sync_status?: string;
status?: 'draft' | 'published' | 'review';
}
interface ContentImage {
id: number;
image_url: string | null;
image_path: string | null;
prompt: string | null;
status: 'pending' | 'generated' | 'failed';
position: number;
}
Configuration (images.config.tsx):
- Columns: content_title, featured_image, in_article_1-5, overall_status, actions
- Row actions: publish_wordpress, update_status
- Bulk actions: bulk_publish_wordpress
4. Published Page (Published.tsx - 13 lines)
Purpose: Show published content with WordPress publishing capabilities
Current Implementation:
import Tasks from './Tasks';
export default function Published() {
return <Tasks />;
}
Issues:
- ❌ Wrong table loaded - Renders
<Tasks />instead of Content table - ❌ No WordPress publishing UI - Should have edit and publish functionality
- ❌ No published item indicators - Missing visual styling for published items
What It Should Be:
- Load Content table filtered by status
- Show WordPress publishing actions
- Allow editing before publishing
- Display WordPress publish status
- Show external URL links
5. Table Actions Configuration (table-actions.config.tsx - 359 lines)
Current Implementation:
- ✅
/writer/tasks- edit, generate_content - ✅
/writer/content- edit, view_on_wordpress, generate_image_prompts, publish - ✅
/writer/published- edit (minimal) - ✅
/writer/images- publish_wordpress, update_status
Issues:
- ⚠️ Published page actions too minimal
- ⚠️ Images page has publishing (should be removed)
Root Cause Analysis
Issue #1: Images Page Bulk Select Not Working
Root Cause: Data model mismatch
ContentImagesGroupusescontent_idas primary identifierTablePageTemplateexpectsidfield for selection (selectedIdsarray)- No
idfield exists inContentImagesGroup
Solution Options:
- Option A (Recommended): Add
idfield toContentImagesGroupthat mirrorscontent_id - Option B: Modify TablePageTemplate to accept custom ID field name
- Option C: Transform data in Images.tsx to add
id: content_id
Recommended Fix: Option C (least invasive)
const transformedImages = images.map(group => ({
...group,
id: group.content_id // Add id field for TablePageTemplate
}));
Issue #2: Images Page Delete Not Working
Root Cause: No delete handlers implemented
- TablePageTemplate supports
onDeleteandonBulkDeleteprops - Images.tsx doesn't pass these handlers
- No API endpoints being called
Solution: Implement delete handlers using Content API
const handleDelete = async (id: number) => {
await deleteContent(id); // Delete content (cascade deletes images)
loadImages();
};
const handleBulkDelete = async (ids: number[]) => {
await bulkDeleteContent(ids);
loadImages();
};
Issue #3: Published Page Structure
Root Cause: Placeholder implementation
- Published.tsx is just a wrapper around Tasks component
- No actual published content filtering
- No WordPress publishing UI
Solution: Complete reimplementation
- Duplicate Content.tsx structure
- Add WordPress-specific actions
- Filter for published/review status
- Add visual indicators
Issue #4: Missing "Review" Status
Root Cause: Status workflow incomplete
- Current: Task (queued → completed) → Content (draft → published)
- Missing: Content (draft → review → published)
- No auto-status change when images generated
Solution:
- Add "review" status to Content model (backend)
- Update API to support new status
- Add auto-transition: when images generated, change status from draft to review
- Update all frontend status filters and badges
Detailed Implementation Plan
Priority 1: Critical Bug Fixes
Task 1.1: Fix Images Page Bulk Select
Files to modify:
frontend/src/pages/Writer/Images.tsx
Changes:
// In loadImages callback, transform data
const transformedResults = paginatedResults.map(group => ({
...group,
id: group.content_id // Add id field for selection
}));
setImages(transformedResults);
Testing:
- Bulk select checkbox appears and works
- Can select/deselect all
- Can select individual rows
- Selected IDs are content_id values
Task 1.2: Fix Images Page Delete Functions
Files to modify:
frontend/src/pages/Writer/Images.tsx
Changes:
// Add delete handlers
const handleDelete = useCallback(async (id: number) => {
try {
await deleteContent(id);
toast.success('Content and images deleted successfully');
loadImages();
} catch (error: any) {
toast.error(`Failed to delete: ${error.message}`);
throw error;
}
}, [loadImages, toast]);
const handleBulkDelete = useCallback(async (ids: number[]) => {
try {
const result = await bulkDeleteContent(ids);
toast.success(`Deleted ${result.deleted_count} content items and their images`);
loadImages();
return result;
} catch (error: any) {
toast.error(`Failed to bulk delete: ${error.message}`);
throw error;
}
}, [loadImages, toast]);
// In TablePageTemplate
<TablePageTemplate
// ... existing props
onDelete={handleDelete}
onBulkDelete={handleBulkDelete}
/>
Testing:
- Single delete works (deletes content + images)
- Bulk delete works
- Confirmation modals appear
- Data refreshes after delete
Priority 2: Published Page Restructuring
Task 2.1: Reimplement Published Page
Files to modify:
frontend/src/pages/Writer/Published.tsx(complete rewrite)
Implementation:
/**
* Published Page - Built with TablePageTemplate
* Shows published/review content with WordPress publishing capabilities
*/
import { useState, useEffect, useMemo, useCallback, useRef } from 'react';
import TablePageTemplate from '../../templates/TablePageTemplate';
import {
fetchContent,
ContentType,
ContentListResponse,
ContentFilters,
publishToWordPress, // Use unified publisher API
} from '../../services/api';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { FileIcon, CheckCircleIcon, TaskIcon, ImageIcon } from '../../icons';
import { createPublishedPageConfig } from '../../config/pages/published.config'; // New config file
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import { useNavigate } from 'react-router';
export default function Published() {
const toast = useToast();
const navigate = useNavigate();
// Data state
const [content, setContent] = useState<ContentType[]>([]);
const [loading, setLoading] = useState(true);
// Filter state - default to published/review status
const [searchTerm, setSearchTerm] = useState('');
const [statusFilter, setStatusFilter] = useState('published'); // Default filter
const [publishStatusFilter, setPublishStatusFilter] = useState(''); // WordPress publish status
const [selectedIds, setSelectedIds] = useState<string[]>([]);
// Pagination state
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
const [totalCount, setTotalCount] = useState(0);
const pageSize = 20;
// Sorting state
const [sortBy, setSortBy] = useState<string>('created_at');
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
const [showContent, setShowContent] = useState(false);
// Load content - filtered for published/review
const loadContent = useCallback(async () => {
setLoading(true);
setShowContent(false);
try {
const ordering = sortBy ? `${sortDirection === 'desc' ? '-' : ''}${sortBy}` : '-created_at';
const filters: ContentFilters = {
...(searchTerm && { search: searchTerm }),
// Filter for published or review status only
...(statusFilter && { status: statusFilter }),
page: currentPage,
page_size: pageSize,
ordering,
};
const data: ContentListResponse = await fetchContent(filters);
setContent(data.results || []);
setTotalCount(data.count || 0);
setTotalPages(Math.ceil((data.count || 0) / pageSize));
setTimeout(() => {
setShowContent(true);
setLoading(false);
}, 100);
} catch (error: any) {
console.error('Error loading content:', error);
toast.error(`Failed to load content: ${error.message}`);
setShowContent(true);
setLoading(false);
}
}, [currentPage, statusFilter, sortBy, sortDirection, searchTerm, toast, pageSize]);
useEffect(() => {
loadContent();
}, [loadContent]);
// Handle sorting
const handleSort = (field: string, direction: 'asc' | 'desc') => {
setSortBy(field || 'created_at');
setSortDirection(direction);
setCurrentPage(1);
};
// Row action handler
const handleRowAction = useCallback(async (action: string, row: ContentType) => {
if (action === 'publish_wordpress') {
try {
const result = await publishToWordPress([row.id]);
if (result.success) {
toast.success(`Published "${row.title}" to WordPress`);
loadContent();
} else {
toast.error(result.error || 'Failed to publish');
}
} catch (error: any) {
toast.error(`Failed to publish: ${error.message}`);
}
} else if (action === 'view_on_wordpress') {
if (row.external_url) {
window.open(row.external_url, '_blank');
}
} else if (action === 'edit') {
// Navigate to content editor
navigate(`/writer/content?id=${row.id}`);
}
}, [toast, loadContent, navigate]);
// Bulk WordPress publish
const handleBulkPublishWordPress = useCallback(async (ids: string[]) => {
try {
const numIds = ids.map(id => parseInt(id));
const result = await publishToWordPress(numIds);
if (result.success) {
toast.success(`Published ${result.published_count || ids.length} items to WordPress`);
loadContent();
} else {
toast.error(result.error || 'Failed to bulk publish');
}
} catch (error: any) {
toast.error(`Failed to bulk publish: ${error.message}`);
throw error;
}
}, [toast, loadContent]);
// Create page config
const pageConfig = useMemo(() => {
return createPublishedPageConfig({
searchTerm,
setSearchTerm,
statusFilter,
setStatusFilter,
publishStatusFilter,
setPublishStatusFilter,
setCurrentPage,
});
}, [searchTerm, statusFilter, publishStatusFilter]);
// Calculate header metrics
const headerMetrics = useMemo(() => {
if (!pageConfig?.headerMetrics) return [];
return pageConfig.headerMetrics.map((metric) => ({
label: metric.label,
value: metric.calculate({ content, totalCount }),
accentColor: metric.accentColor,
}));
}, [pageConfig?.headerMetrics, content, totalCount]);
// Writer navigation tabs
const writerTabs = [
{ label: 'Tasks', path: '/writer/tasks', icon: <TaskIcon /> },
{ label: 'Content', path: '/writer/content', icon: <FileIcon /> },
{ label: 'Images', path: '/writer/images', icon: <ImageIcon /> },
{ label: 'Published', path: '/writer/published', icon: <CheckCircleIcon /> },
];
return (
<>
<PageHeader
title="Published Content"
badge={{ icon: <CheckCircleIcon />, color: 'green' }}
navigation={<ModuleNavigationTabs tabs={writerTabs} />}
/>
<TablePageTemplate
columns={pageConfig.columns}
data={content}
loading={loading}
showContent={showContent}
filters={pageConfig.filters}
filterValues={{
search: searchTerm,
status: statusFilter,
publishStatus: publishStatusFilter,
}}
onFilterChange={(key: string, value: any) => {
if (key === 'search') {
setSearchTerm(value);
} else if (key === 'status') {
setStatusFilter(value);
setCurrentPage(1);
} else if (key === 'publishStatus') {
setPublishStatusFilter(value);
setCurrentPage(1);
}
}}
pagination={{
currentPage,
totalPages,
totalCount,
onPageChange: setCurrentPage,
}}
selection={{
selectedIds,
onSelectionChange: setSelectedIds,
}}
sorting={{
sortBy,
sortDirection,
onSort: handleSort,
}}
headerMetrics={headerMetrics}
onRowAction={handleRowAction}
onBulkAction={async (action: string, ids: string[]) => {
if (action === 'bulk_publish_wordpress') {
await handleBulkPublishWordPress(ids);
}
}}
getItemDisplayName={(row: ContentType) => row.title || `Content #${row.id}`}
/>
</>
);
}
Task 2.2: Create Published Page Configuration
Files to create:
frontend/src/config/pages/published.config.tsx
Implementation:
import { ColumnConfig } from '../../templates/TablePageTemplate';
import { ContentType } from '../../services/api';
import { Badge } from '../../components/ui/badge';
import { ExternalLinkIcon, CheckCircleIcon } from '../../icons';
export function createPublishedPageConfig(params: {
searchTerm: string;
setSearchTerm: (value: string) => void;
statusFilter: string;
setStatusFilter: (value: string) => void;
publishStatusFilter: string;
setPublishStatusFilter: (value: string) => void;
setCurrentPage: (page: number) => void;
}) {
const columns: ColumnConfig[] = [
{
key: 'title',
label: 'Title',
sortable: true,
render: (value: string, row: ContentType) => (
<div className="flex items-center gap-2">
<span className="font-medium">{value}</span>
{row.external_url && (
<a
href={row.external_url}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:text-blue-600"
>
<ExternalLinkIcon className="w-4 h-4" />
</a>
)}
</div>
),
},
{
key: 'status',
label: 'Content Status',
sortable: true,
badge: true,
render: (value: string) => (
<Badge
variant={value === 'published' ? 'success' : value === 'review' ? 'warning' : 'default'}
>
{value}
</Badge>
),
},
{
key: 'sync_status',
label: 'WordPress Status',
sortable: false,
render: (value: string, row: ContentType) => {
if (row.external_id) {
return (
<Badge variant="success">
<CheckCircleIcon className="w-3 h-3 mr-1" />
Published
</Badge>
);
}
return (
<Badge variant="default">Not Published</Badge>
);
},
},
{
key: 'content_type',
label: 'Type',
sortable: true,
},
{
key: 'word_count',
label: 'Words',
sortable: true,
numeric: true,
},
{
key: 'created_at',
label: 'Created',
sortable: true,
date: true,
},
];
const filters = [
{
key: 'search',
label: 'Search',
type: 'text' as const,
placeholder: 'Search published content...',
},
{
key: 'status',
label: 'Content Status',
type: 'select' as const,
options: [
{ value: '', label: 'All' },
{ value: 'review', label: 'In Review' },
{ value: 'published', label: 'Published' },
],
},
{
key: 'publishStatus',
label: 'WordPress Status',
type: 'select' as const,
options: [
{ value: '', label: 'All' },
{ value: 'published', label: 'Published to WP' },
{ value: 'not_published', label: 'Not Published' },
],
},
];
const headerMetrics = [
{
label: 'Total Published',
calculate: (data: { totalCount: number }) => data.totalCount,
accentColor: 'green' as const,
},
{
label: 'On WordPress',
calculate: (data: { content: ContentType[] }) =>
data.content.filter(c => c.external_id).length,
accentColor: 'blue' as const,
},
{
label: 'In Review',
calculate: (data: { content: ContentType[] }) =>
data.content.filter(c => c.status === 'review').length,
accentColor: 'amber' as const,
},
];
return {
columns,
filters,
headerMetrics,
};
}
Task 2.3: Update Table Actions for Published Page
Files to modify:
frontend/src/config/pages/table-actions.config.tsx
Changes:
'/writer/published': {
rowActions: [
{
key: 'edit',
label: 'Edit Content',
icon: EditIcon,
variant: 'primary',
},
{
key: 'publish_wordpress',
label: 'Publish to WordPress',
icon: <ArrowRightIcon className="w-5 h-5" />,
variant: 'success',
shouldShow: (row: any) => !row.external_id, // Only show if not published
},
{
key: 'view_on_wordpress',
label: 'View on WordPress',
icon: <ExternalLinkIcon className="w-5 h-5" />,
variant: 'secondary',
shouldShow: (row: any) => !!row.external_id, // Only show if published
},
],
bulkActions: [
{
key: 'bulk_publish_wordpress',
label: 'Publish to WordPress',
icon: <ArrowRightIcon className="w-4 h-4" />,
variant: 'success',
},
{
key: 'update_status',
label: 'Update Status',
icon: <CheckCircleIcon className="w-4 h-4 text-success-500" />,
variant: 'secondary',
},
{
key: 'export',
label: 'Export Selected',
icon: <DownloadIcon className="w-4 h-4 text-blue-light-500" />,
variant: 'secondary',
},
],
},
Priority 3: Content Page Enhancements
Task 3.1: Add Content Viewer Link
Files to modify:
frontend/src/config/pages/content.config.tsx
Changes:
// In columns array, update title column
{
key: 'title',
label: 'Title',
sortable: true,
render: (value: string, row: ContentType) => (
<button
onClick={() => {
// Open content viewer modal
if (params.onViewContent) {
params.onViewContent(row);
}
}}
className="text-blue-500 hover:text-blue-600 hover:underline text-left font-medium"
>
{value}
</button>
),
},
Files to modify:
frontend/src/pages/Writer/Content.tsx
Changes:
// Add state for content viewer modal
const [isViewerModalOpen, setIsViewerModalOpen] = useState(false);
const [viewerContentId, setViewerContentId] = useState<number | null>(null);
// Add handler
const handleViewContent = useCallback((row: ContentType) => {
setViewerContentId(row.id);
setIsViewerModalOpen(true);
}, []);
// Update pageConfig call
const pageConfig = useMemo(() => {
return createContentPageConfig({
// ... existing params
onViewContent: handleViewContent,
});
}, [/* deps */, handleViewContent]);
// Add ContentViewerModal component (import from shared components)
<ContentViewerModal
isOpen={isViewerModalOpen}
onClose={() => {
setIsViewerModalOpen(false);
setViewerContentId(null);
}}
contentId={viewerContentId}
/>
Task 3.2: Add Status Indicator Columns
Files to modify:
frontend/src/config/pages/content.config.tsx
Changes:
// Add after 'status' column
{
key: 'prompts_status',
label: 'Prompts',
sortable: false,
render: (value: any, row: ContentType) => {
// Check if prompts exist (need to add this to API response)
const hasPrompts = row.image_prompts_count > 0;
return (
<Badge variant={hasPrompts ? 'success' : 'default'}>
{hasPrompts ? (
<>
<CheckCircleIcon className="w-3 h-3 mr-1" />
{row.image_prompts_count} prompts
</>
) : (
'No prompts'
)}
</Badge>
);
},
},
{
key: 'images_status',
label: 'Images',
sortable: false,
render: (value: any, row: ContentType) => {
const generatedCount = row.images_generated_count || 0;
const totalCount = row.images_total_count || 0;
if (totalCount === 0) {
return <Badge variant="default">No images</Badge>;
}
const isComplete = generatedCount === totalCount;
const isPartial = generatedCount > 0 && generatedCount < totalCount;
return (
<Badge variant={isComplete ? 'success' : isPartial ? 'warning' : 'default'}>
{isComplete && <CheckCircleIcon className="w-3 h-3 mr-1" />}
{generatedCount}/{totalCount}
</Badge>
);
},
},
Backend Changes Needed:
- Add fields to Content API response:
image_prompts_countimages_generated_countimages_total_count
Priority 4: Add "Review" Status Workflow
Task 4.1: Backend Model Changes
Files to modify:
backend/igny8_core/modules/writer/models.py
Changes:
class Content(models.Model):
STATUS_CHOICES = [
('draft', 'Draft'),
('review', 'In Review'), # NEW STATUS
('published', 'Published'),
]
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='draft',
db_index=True,
)
Migration:
cd backend
python manage.py makemigrations writer
python manage.py migrate writer
Task 4.2: Auto-Status Change on Image Generation
Files to modify:
backend/igny8_core/modules/writer/services/image_generation.py(or wherever images are generated)
Changes:
def on_images_generated(content_id):
"""Called when all images for content are successfully generated"""
content = Content.objects.get(id=content_id)
# Auto-transition draft → review when images complete
if content.status == 'draft':
content.status = 'review'
content.save(update_fields=['status'])
# Optional: Create notification/log entry
logger.info(f"Content {content_id} auto-transitioned to 'review' after image generation")
Task 4.3: Frontend Status Updates
Files to modify:
- All status badge renderers
- All status filter options
- All status update modals
Changes:
// Update status options everywhere
const STATUS_OPTIONS = [
{ value: 'draft', label: 'Draft' },
{ value: 'review', label: 'In Review' }, // NEW
{ value: 'published', label: 'Published' },
];
// Update badge variants
const getStatusVariant = (status: string) => {
switch (status) {
case 'draft': return 'default';
case 'review': return 'warning'; // NEW
case 'published': return 'success';
default: return 'default';
}
};
Priority 5: Remove WordPress Publishing from Images Page
Task 5.1: Update Images Page Configuration
Files to modify:
frontend/src/config/pages/table-actions.config.tsx
Changes:
'/writer/images': {
rowActions: [
{
key: 'update_status',
label: 'Update Status',
icon: <CheckCircleIcon className="w-5 h-5" />,
variant: 'primary',
},
// REMOVED: publish_wordpress action
],
bulkActions: [
// REMOVED: bulk_publish_wordpress action
],
},
Task 5.2: Clean Up Images Page Code
Files to modify:
frontend/src/pages/Writer/Images.tsx
Changes:
- Remove WordPress publishing handlers
- Remove related imports
- Simplify row action handler
- Remove WordPress-related state
Priority 6: Visual Indicators for Published Items
Task 6.1: Add Published Badge to All Tables
Files to modify:
- All page configs (tasks, content, images, published)
Changes:
// Add to title or status column render
{
key: 'title',
label: 'Title',
render: (value: string, row: any) => (
<div className="flex items-center gap-2">
<span className="font-medium">{value}</span>
{row.external_id && (
<Badge variant="success" size="sm">
<CheckCircleIcon className="w-3 h-3 mr-1" />
WP
</Badge>
)}
</div>
),
},
Implementation Checklist
Phase 1: Critical Bugs (Week 1)
- Fix Images page bulk select (add
idfield) - Fix Images page delete functions
- Test both fixes thoroughly
Phase 2: Published Page (Week 1-2)
- Create
published.config.tsx - Rewrite
Published.tsxcomponent - Update table actions config
- Add WordPress publishing handlers
- Test all functionality
Phase 3: Content Enhancements (Week 2)
- Add content viewer link to title column
- Create/import ContentViewerModal
- Add backend fields for prompt/image counts
- Add status indicator columns
- Update content.config.tsx
- Test viewer and indicators
Phase 4: Review Status (Week 2-3)
- Create Django migration for new status
- Update backend model
- Add auto-transition logic
- Update all frontend status options
- Update all status badge renders
- Update filters across all pages
- Test complete workflow
Phase 5: WordPress Publishing Migration (Week 3)
- Remove WordPress actions from Images page
- Remove WordPress code from Images.tsx
- Verify Published page has all WordPress functionality
- Test end-to-end publishing workflow
Phase 6: Visual Polish (Week 3)
- Add published badges to all table titles
- Add WordPress status indicators
- Add color coding for statuses
- Add icons for published items
- Polish UI across all pages
Phase 7: Testing & Documentation (Week 4)
- Full regression testing
- User acceptance testing
- Update user documentation
- Create migration guide for users
- Deploy to staging
- Final production deployment
API Endpoints Required
New Endpoints
POST /v1/publisher/publish/
- Publish content to WordPress
- Body: { content_id, destinations: ['wordpress'] }
- Response: { success, data: { external_id, external_url }, error }
GET /v1/writer/content/{id}/
- Get single content with full details
- Response includes: prompts_count, images_count, etc.
PATCH /v1/writer/content/{id}/
- Update content status
- Body: { status: 'draft' | 'review' | 'published' }
Enhanced Endpoints
GET /v1/writer/content/
- Add fields to response:
- image_prompts_count
- images_generated_count
- images_total_count
- sync_status
- external_id
- external_url
Data Flow Diagrams
Current Flow
Tasks (queued)
→ Generate Content
→ Tasks (completed) + Content (draft)
→ Generate Image Prompts
→ Content (draft) + ImagePrompts
→ Generate Images
→ Content (draft) + Images
→ [Manual publish from Images page]
→ WordPress
New Flow (After Refactoring)
Tasks (queued)
→ Generate Content
→ Tasks (completed) + Content (draft)
→ Generate Image Prompts
→ Content (draft) + ImagePrompts
→ Generate Images
→ Content (review) + Images ← AUTO STATUS CHANGE
→ [Review in Published page]
→ [Edit if needed]
→ [Publish to WordPress from Published page]
→ Content (published) + WordPress Post
Risk Assessment
High Risk
-
Database Migration for Review Status
- Mitigation: Test on staging first, have rollback plan
- Impact: Could affect existing content if not handled properly
-
Breaking Changes to Content API
- Mitigation: Add new fields as optional, maintain backward compatibility
- Impact: Other parts of app might depend on current response shape
Medium Risk
-
Auto-Status Transition Logic
- Mitigation: Make it configurable, add feature flag
- Impact: Could change status unexpectedly if logic is wrong
-
WordPress Publishing Removal from Images
- Mitigation: Ensure Published page fully functional before removing
- Impact: Users might look for publish button in wrong place
Low Risk
-
UI Changes (badges, indicators)
- Mitigation: Can be easily reverted
- Impact: Purely cosmetic
-
Content Viewer Modal
- Mitigation: Independent feature, doesn't affect core functionality
- Impact: Just adds convenience
Success Metrics
Functional Metrics
- All bulk select checkboxes working across all pages
- Delete functions working (single and bulk)
- Published page shows Content table (not Tasks)
- WordPress publishing only available on Published page
- "Review" status visible and functional
- Auto-status change working when images generated
- Content viewer accessible from title links
- Status indicators showing prompt/image progress
User Experience Metrics
- Reduced clicks to publish content (consolidated on one page)
- Clear visual feedback for publish status
- Intuitive workflow: draft → review → published
- Easy access to content viewing
- Clear status progression indicators
Technical Metrics
- No console errors
- All API calls successful
- Proper error handling throughout
- Consistent response times
- Proper loading states
Rollback Plan
If critical issues arise:
-
Phase 1-2 Issues (Bugs/Published Page):
- Revert Published.tsx to
<Tasks />wrapper - Disable new delete handlers
- Restore selection functionality
- Revert Published.tsx to
-
Phase 4 Issues (Review Status):
- Rollback database migration
- Restore previous status options
- Disable auto-transition logic
-
Phase 5 Issues (WordPress Migration):
- Re-enable WordPress publishing on Images page
- Disable on Published page temporarily
Future Enhancements (Post-Refactoring)
- Bulk Edit Mode - Edit multiple content items at once
- Scheduling - Schedule WordPress publishing for future dates
- Publishing Templates - Save WordPress settings as templates
- Draft Revisions - Track content changes before publishing
- Publishing Analytics - Track WordPress publish success rates
- Multi-destination Publishing - Publish to multiple WordPress sites
- Content Preview - Preview how content will look on WordPress
- SEO Checker - Validate SEO before publishing
Appendix: File Inventory
Files to Modify
frontend/src/pages/Writer/
- Tasks.tsx (minor - visual indicators)
- Content.tsx (major - viewer link, status columns)
- Images.tsx (major - fix select, delete, remove WP)
- Published.tsx (complete rewrite)
frontend/src/config/pages/
- content.config.tsx (add columns, viewer link)
- images.config.tsx (minor updates)
- table-actions.config.tsx (update all writer sections)
- published.config.tsx (NEW FILE)
backend/igny8_core/modules/writer/
- models.py (add review status)
- services/image_generation.py (auto-status change)
- serializers.py (add new fields)
- views.py (update filters)
Files to Create
frontend/src/config/pages/published.config.tsx
frontend/src/components/common/ContentViewerModal.tsx (if doesn't exist)
backend/igny8_core/modules/writer/migrations/XXXX_add_review_status.py
Total Estimated Changes
- Modified Files: ~15
- New Files: ~3
- Lines Changed: ~2,000
- Estimated Hours: 40-60 hours
- Estimated Calendar Time: 3-4 weeks
End of Document
Last Updated: December 2024 Document Version: 1.0 Author: AI Assistant (Deep Analysis Mode)