/** * Review Page Configuration * Centralized config for Review page table, filters, and actions */ import { Content } from '../../services/api'; 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; onRowClick?: (row: Content) => void; }): ReviewPageConfig { const showSectorColumn = !params.activeSector; const columns: ColumnConfig[] = [ { key: 'title', label: 'Title', sortable: true, sortField: 'title', render: (value: string, row: Content) => (
{params.onRowClick ? ( ) : ( {value || `Content #${row.id}`} )}
), }, { key: 'categories', label: 'Categories', sortable: false, width: '180px', render: (_value: any, row: Content) => { const categories = row.categories || []; if (!categories || categories.length === 0) { return -; } return (
{categories.slice(0, 2).map((category, index) => ( {category} ))} {categories.length > 2 && ( +{categories.length - 2} )}
); }, }, { key: 'tags', label: 'Tags', sortable: false, width: '180px', render: (_value: any, row: Content) => { const tags = row.tags || []; if (!tags || tags.length === 0) { return -; } return (
{tags.slice(0, 2).map((tag, index) => ( {tag} ))} {tags.length > 2 && ( +{tags.length - 2} )}
); }, }, { key: 'content_type', label: 'Type', sortable: true, sortField: 'content_type', width: '110px', render: (value: string) => { const label = TYPE_LABELS[value] || value || '-'; const properCase = label.charAt(0).toUpperCase() + label.slice(1); return ( {properCase} ); }, }, { key: 'content_structure', label: 'Structure', sortable: true, sortField: 'content_structure', width: '130px', render: (value: string) => { const label = STRUCTURE_LABELS[value] || value || '-'; const properCase = label.split(/[_\s]+/).map(word => word.charAt(0).toUpperCase() + word.slice(1) ).join(' '); return ( {properCase} ); }, }, { 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: 'status', label: 'Status', sortable: true, sortField: 'status', width: '120px', render: (value: string, row: Content) => { const status = value || 'draft'; const statusColors: Record = { draft: 'gray', review: 'blue', published: 'green', scheduled: 'amber', archived: 'red', }; const color = statusColors[status] || 'gray'; const label = status.charAt(0).toUpperCase() + status.slice(1); return (
{label} {row.external_id && ( )}
); }, }, { 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: 'Ready', accentColor: 'blue', calculate: ({ totalCount }) => totalCount, tooltip: 'Content ready for final review. Review quality, SEO, and images before publishing.', }, { label: 'Images', accentColor: 'green', calculate: ({ content }) => content.filter(c => c.has_generated_images).length, tooltip: 'Content with generated images. Visual assets complete and ready for review.', }, { label: 'Optimized', accentColor: 'purple', calculate: ({ content }) => content.filter(c => c.optimization_scores && c.optimization_scores.overall_score >= 80).length, tooltip: 'Content with high SEO optimization scores (80%+). Well-optimized for search engines.', }, { label: 'Sync Ready', accentColor: 'amber', calculate: ({ content }) => content.filter(c => c.has_generated_images && c.optimization_scores && c.optimization_scores.overall_score >= 70).length, tooltip: 'Content ready for WordPress sync. Has images and good optimization score.', }, ], }; }