/** * Ideas Page Configuration * Centralized config for Ideas page table, filters, and actions */ import React from 'react'; import { titleColumn, sectorColumn, statusColumn, createdColumn, } from '../snippets/columns.snippets'; import Badge from '../../components/ui/badge/Badge'; import { formatRelativeDate } from '../../utils/date'; import { ContentIdea, Cluster } from '../../services/api'; export interface ColumnConfig { key: string; label: string; sortable?: boolean; sortField?: string; align?: 'left' | 'center' | 'right'; width?: string; render?: (value: any, row: any) => React.ReactNode; defaultVisible?: boolean; // Whether column is visible by default (default: true) } export interface FormFieldConfig { key: string; label: string; type: 'text' | 'number' | 'select' | 'textarea'; placeholder?: string; required?: boolean; value: any; onChange: (value: any) => void; options?: Array<{ value: string; label: string }>; } export interface FilterConfig { key: string; label: string; type: 'text' | 'select'; placeholder?: string; options?: Array<{ value: string; label: string }>; dynamicOptions?: string; } export interface HeaderMetricConfig { label: string; value: number; accentColor: 'blue' | 'green' | 'amber' | 'purple'; calculate: (data: { ideas: any[]; totalCount: number }) => number; } export interface IdeasPageConfig { columns: ColumnConfig[]; filters: FilterConfig[]; formFields: (clusters: Array<{ id: number; name: string }>) => FormFieldConfig[]; headerMetrics: HeaderMetricConfig[]; } export const createIdeasPageConfig = ( handlers: { clusters: Array<{ id: number; name: string }>; activeSector: { id: number; name: string } | null; formData: { idea_title: string; description?: string | null; content_structure: string; content_type: string; target_keywords?: string | null; keyword_cluster_id?: number | null; status: string; estimated_word_count?: number; }; setFormData: React.Dispatch>; searchTerm: string; setSearchTerm: (value: string) => void; statusFilter: string; setStatusFilter: (value: string) => void; clusterFilter: string; setClusterFilter: (value: string) => void; structureFilter: string; setStructureFilter: (value: string) => void; typeFilter: string; setTypeFilter: (value: string) => void; setCurrentPage: (page: number) => void; } ): IdeasPageConfig => { const showSectorColumn = !handlers.activeSector; // Show when viewing all sectors return { columns: [ { ...titleColumn, key: 'idea_title', label: 'Title', sortable: true, sortField: 'idea_title', toggleable: true, // Enable toggle for this column toggleContentKey: 'description', // Use description field for toggle content toggleContentLabel: 'Content Outline', // Label for expanded content render: (value: string) => ( {value} ), }, // Sector column - only show when viewing all sectors ...(showSectorColumn ? [{ ...sectorColumn, render: (value: string, row: ContentIdea) => ( {row.sector_name || '-'} ), }] : []), { key: 'content_structure', label: 'Structure', sortable: true, sortField: 'content_structure', width: '150px', render: (value: string) => ( {value?.replace('_', ' ') || '-'} ), }, { key: 'content_type', label: 'Type', sortable: true, sortField: 'content_type', width: '120px', render: (value: string) => ( {value?.replace('_', ' ') || '-'} ), }, { key: 'target_keywords', label: 'Target Keywords', sortable: false, width: '250px', render: (value: string) => ( {value || '-'} ), }, { key: 'keyword_cluster_name', label: 'Cluster', sortable: true, sortField: 'keyword_cluster_id', width: '200px', render: (_value: string, row: ContentIdea) => row.keyword_cluster_name || '-', }, { ...statusColumn, sortable: true, sortField: 'status', render: (value: string) => { const statusColors: Record = { 'new': 'warning', 'scheduled': 'info', 'published': 'success', }; return ( {value} ); }, }, { key: 'estimated_word_count', label: 'Words', sortable: true, sortField: 'estimated_word_count', width: '100px', render: (value: number) => value.toLocaleString(), }, { ...createdColumn, sortable: true, sortField: 'created_at', render: (value: string) => formatRelativeDate(value), }, // Optional columns - hidden by default { key: 'updated_at', label: 'Updated', sortable: true, sortField: 'updated_at', defaultVisible: false, render: (value: string) => formatRelativeDate(value), }, ], filters: [ { key: 'search', label: 'Search', type: 'text', placeholder: 'Search ideas...', }, { key: 'status', label: 'Status', type: 'select', options: [ { value: '', label: 'All Status' }, { value: 'new', label: 'New' }, { value: 'scheduled', label: 'Scheduled' }, { value: 'published', label: 'Published' }, ], }, { key: 'content_structure', label: 'Structure', type: 'select', options: [ { value: '', label: 'All Structures' }, { value: 'cluster_hub', label: 'Cluster Hub' }, { value: 'landing_page', label: 'Landing Page' }, { value: 'pillar_page', label: 'Pillar Page' }, { value: 'supporting_page', label: 'Supporting Page' }, ], }, { key: 'content_type', label: 'Type', type: 'select', options: [ { value: '', label: 'All Types' }, { value: 'blog_post', label: 'Blog Post' }, { value: 'article', label: 'Article' }, { value: 'guide', label: 'Guide' }, { value: 'tutorial', label: 'Tutorial' }, ], }, { key: 'keyword_cluster_id', label: 'Cluster', type: 'select', options: (() => { return [ { value: '', label: 'All Clusters' }, ...handlers.clusters.map((c) => ({ value: c.id.toString(), label: c.name })), ]; })(), dynamicOptions: 'clusters', }, ], formFields: (clusters: Array<{ id: number; name: string }>) => [ { key: 'idea_title', label: 'Title', type: 'text', placeholder: 'Enter idea title', required: true, value: handlers.formData.idea_title || '', onChange: (value: any) => handlers.setFormData({ ...handlers.formData, idea_title: value }), }, { key: 'description', label: 'Description', type: 'textarea', placeholder: 'Enter description', value: handlers.formData.description || '', onChange: (value: any) => handlers.setFormData({ ...handlers.formData, description: value }), }, { key: 'content_structure', label: 'Content Structure', type: 'select', value: handlers.formData.content_structure || 'blog_post', onChange: (value: any) => handlers.setFormData({ ...handlers.formData, content_structure: value }), options: [ { value: 'cluster_hub', label: 'Cluster Hub' }, { value: 'landing_page', label: 'Landing Page' }, { value: 'pillar_page', label: 'Pillar Page' }, { value: 'supporting_page', label: 'Supporting Page' }, ], }, { key: 'content_type', label: 'Content Type', type: 'select', value: handlers.formData.content_type || 'blog_post', onChange: (value: any) => handlers.setFormData({ ...handlers.formData, content_type: value }), options: [ { value: 'blog_post', label: 'Blog Post' }, { value: 'article', label: 'Article' }, { value: 'guide', label: 'Guide' }, { value: 'tutorial', label: 'Tutorial' }, ], }, { key: 'target_keywords', label: 'Target Keywords', type: 'text', placeholder: 'Enter keywords (comma-separated)', value: handlers.formData.target_keywords || '', onChange: (value: any) => handlers.setFormData({ ...handlers.formData, target_keywords: value }), }, { key: 'keyword_cluster_id', label: 'Cluster', type: 'select', value: handlers.formData.keyword_cluster_id?.toString() || '', onChange: (value: any) => handlers.setFormData({ ...handlers.formData, keyword_cluster_id: value ? parseInt(value) : null, }), options: [ { value: '', label: 'No Cluster' }, ...clusters.map((c) => ({ value: c.id.toString(), label: c.name })), ], }, { key: 'estimated_word_count', label: 'Estimated Word Count', type: 'number', value: handlers.formData.estimated_word_count || 1000, onChange: (value: any) => handlers.setFormData({ ...handlers.formData, estimated_word_count: value ? parseInt(value) : 1000 }), }, { key: 'status', label: 'Status', type: 'select', value: handlers.formData.status || 'new', onChange: (value: any) => handlers.setFormData({ ...handlers.formData, status: value }), options: [ { value: 'new', label: 'New' }, { value: 'scheduled', label: 'Scheduled' }, { value: 'published', label: 'Published' }, ], }, ], headerMetrics: [ { label: 'Total Ideas', value: 0, accentColor: 'blue' as const, calculate: (data) => data.totalCount || 0, }, { label: 'New', value: 0, accentColor: 'amber' as const, calculate: (data) => data.ideas.filter((i: ContentIdea) => i.status === 'new').length, }, { label: 'Scheduled', value: 0, accentColor: 'blue' as const, calculate: (data) => data.ideas.filter((i: ContentIdea) => i.status === 'scheduled').length, }, { label: 'Published', value: 0, accentColor: 'green' as const, calculate: (data) => data.ideas.filter((i: ContentIdea) => i.status === 'published').length, }, ], }; };