Files
igny8/docs/WRITER_MODULE_REFACTORING_PLAN.md
alorig 8d096b383a 21
2025-11-29 14:33:07 +05:00

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:

  1. Fix critical bugs (bulk select, delete functions)
  2. Restructure Published page to show Content (not Tasks)
  3. Implement proper WordPress publishing workflow
  4. Add "review" status to content lifecycle
  5. 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: queuedcompleted
  • 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: draftpublished
  • 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:

  1. No content viewer link - Title column not clickable to view content
  2. Missing status indicators - No visual feedback for:
    • Prompt generation status (pending/complete)
    • Image generation status (pending/generating/complete)
  3. ⚠️ 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:

  1. Bulk select checkbox not working - Root cause: ContentImagesGroup interface has content_id but TablePageTemplate expects id field for selection
  2. Delete functions not working - No delete handlers implemented
  3. ⚠️ 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:

  1. Wrong table loaded - Renders <Tasks /> instead of Content table
  2. No WordPress publishing UI - Should have edit and publish functionality
  3. 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

  • ContentImagesGroup uses content_id as primary identifier
  • TablePageTemplate expects id field for selection (selectedIds array)
  • No id field exists in ContentImagesGroup

Solution Options:

  1. Option A (Recommended): Add id field to ContentImagesGroup that mirrors content_id
  2. Option B: Modify TablePageTemplate to accept custom ID field name
  3. 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 onDelete and onBulkDelete props
  • 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:

  1. Add "review" status to Content model (backend)
  2. Update API to support new status
  3. Add auto-transition: when images generated, change status from draft to review
  4. 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

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_count
    • images_generated_count
    • images_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 id field)
  • Fix Images page delete functions
  • Test both fixes thoroughly

Phase 2: Published Page (Week 1-2)

  • Create published.config.tsx
  • Rewrite Published.tsx component
  • 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

  1. Database Migration for Review Status

    • Mitigation: Test on staging first, have rollback plan
    • Impact: Could affect existing content if not handled properly
  2. 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

  1. Auto-Status Transition Logic

    • Mitigation: Make it configurable, add feature flag
    • Impact: Could change status unexpectedly if logic is wrong
  2. 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

  1. UI Changes (badges, indicators)

    • Mitigation: Can be easily reverted
    • Impact: Purely cosmetic
  2. 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:

  1. Phase 1-2 Issues (Bugs/Published Page):

    • Revert Published.tsx to <Tasks /> wrapper
    • Disable new delete handlers
    • Restore selection functionality
  2. Phase 4 Issues (Review Status):

    • Rollback database migration
    • Restore previous status options
    • Disable auto-transition logic
  3. Phase 5 Issues (WordPress Migration):

    • Re-enable WordPress publishing on Images page
    • Disable on Published page temporarily

Future Enhancements (Post-Refactoring)

  1. Bulk Edit Mode - Edit multiple content items at once
  2. Scheduling - Schedule WordPress publishing for future dates
  3. Publishing Templates - Save WordPress settings as templates
  4. Draft Revisions - Track content changes before publishing
  5. Publishing Analytics - Track WordPress publish success rates
  6. Multi-destination Publishing - Publish to multiple WordPress sites
  7. Content Preview - Preview how content will look on WordPress
  8. 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)