/** * Review Page Configuration * Centralized config for Review page table, filters, and actions */ import { Content } from '../../services/api'; import { Link } from 'react-router-dom'; import Badge from '../../components/ui/badge/Badge'; import { formatRelativeDate } from '../../utils/date'; import { CheckCircleIcon } from '../../icons'; import { STRUCTURE_LABELS, TYPE_LABELS } from '../structureMapping'; export interface ColumnConfig { key: string; label: string; sortable?: boolean; sortField?: string; align?: 'left' | 'center' | 'right'; width?: string; numeric?: boolean; date?: boolean; render?: (value: any, row: any) => React.ReactNode; toggleable?: boolean; toggleContentKey?: string; toggleContentLabel?: string; defaultVisible?: boolean; } export interface FilterConfig { key: string; label: string; type: 'text' | 'select'; placeholder?: string; options?: Array<{ value: string; label: string }>; } export interface HeaderMetricConfig { label: string; accentColor: 'blue' | 'green' | 'amber' | 'purple'; calculate: (data: { content: Content[]; totalCount: number }) => number; } export interface ReviewPageConfig { columns: ColumnConfig[]; filters: FilterConfig[]; headerMetrics: HeaderMetricConfig[]; } export function createReviewPageConfig(params: { searchTerm: string; setSearchTerm: (value: string) => void; statusFilter: string; setStatusFilter: (value: string) => void; setCurrentPage: (page: number) => void; activeSector: { id: number; name: string } | null; }): ReviewPageConfig { const showSectorColumn = !params.activeSector; const columns: ColumnConfig[] = [ // Title first, then categories and tags (moved after title per change request) { key: 'title', label: 'Title', sortable: true, sortField: 'title', toggleable: true, toggleContentKey: 'content_html', toggleContentLabel: 'Generated Content', render: (value: string, row: Content) => (
{value || `Content #${row.id}`}
), }, { key: 'categories', label: 'Categories', sortable: false, width: '180px', render: (_value: any, row: Content) => { const categories = row.taxonomy_terms_data?.filter((t: any) => t.taxonomy_type === 'category') || []; if (!categories.length) return -; return (
{categories.map((cat: any) => ( {cat.name} ))}
); }, toggleable: false, defaultVisible: true, }, { key: 'tags', label: 'Tags', sortable: false, width: '180px', render: (_value: any, row: Content) => { const tags = row.taxonomy_terms_data?.filter((t: any) => t.taxonomy_type === 'tag') || []; if (!tags.length) return -; return (
{tags.map((tag: any) => ( {tag.name} ))}
); }, toggleable: false, defaultVisible: true, }, { key: 'title', label: 'Title', sortable: true, sortField: 'title', toggleable: true, toggleContentKey: 'content_html', toggleContentLabel: 'Generated Content', render: (value: string, row: Content) => (
{value || `Content #${row.id}`}
), }, { key: 'content_type', label: 'Type', sortable: true, sortField: 'content_type', width: '120px', render: (value: string) => ( {TYPE_LABELS[value] || value || '-'} ), }, { key: 'content_structure', label: 'Structure', sortable: true, sortField: 'content_structure', width: '150px', render: (value: string) => ( {STRUCTURE_LABELS[value] || value || '-'} ), }, { key: 'cluster_name', label: 'Cluster', sortable: false, width: '150px', render: (_value: any, row: Content) => { const clusterName = row.cluster_name; if (!clusterName) { return -; } return ( {clusterName} ); }, }, { key: 'word_count', label: 'Words', sortable: true, sortField: 'word_count', numeric: true, width: '100px', render: (value: number) => ( {value?.toLocaleString() || 0} ), }, { key: 'created_at', label: 'Created', sortable: true, sortField: 'created_at', date: true, align: 'right', render: (value: string) => ( {formatRelativeDate(value)} ), }, ]; if (showSectorColumn) { columns.splice(4, 0, { key: 'sector_name', label: 'Sector', sortable: false, width: '120px', render: (value: string, row: Content) => ( {row.sector_name || '-'} ), }); } return { columns, filters: [ { key: 'search', label: 'Search', type: 'text', placeholder: 'Search content...', }, ], headerMetrics: [ { label: 'Total Ready', accentColor: 'blue', calculate: ({ totalCount }) => totalCount, }, { label: 'Has Images', accentColor: 'green', calculate: ({ content }) => content.filter(c => c.has_generated_images).length, }, { label: 'Optimized', accentColor: 'purple', calculate: ({ content }) => content.filter(c => (c as any).optimization_score >= 80).length, }, ], }; }