tasks to published refactor
This commit is contained in:
@@ -82,6 +82,7 @@ export const createContentPageConfig = (
|
||||
statusFilter: string;
|
||||
setStatusFilter: (value: string) => void;
|
||||
setCurrentPage: (page: number) => void;
|
||||
onViewContent?: (row: Content) => void;
|
||||
}
|
||||
): ContentPageConfig => {
|
||||
const showSectorColumn = !handlers.activeSector;
|
||||
@@ -103,9 +104,18 @@ export const createContentPageConfig = (
|
||||
toggleContentLabel: 'Generated Content',
|
||||
render: (value: string, row: Content) => (
|
||||
<div>
|
||||
<div className="font-medium text-gray-900 dark:text-white">
|
||||
{row.title || `Content #${row.id}`}
|
||||
</div>
|
||||
{handlers.onViewContent ? (
|
||||
<button
|
||||
onClick={() => handlers.onViewContent!(row)}
|
||||
className="font-medium text-blue-500 hover:text-blue-600 hover:underline text-left transition-colors"
|
||||
>
|
||||
{row.title || `Content #${row.id}`}
|
||||
</button>
|
||||
) : (
|
||||
<div className="font-medium text-gray-900 dark:text-white">
|
||||
{row.title || `Content #${row.id}`}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@@ -197,6 +207,52 @@ export const createContentPageConfig = (
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'prompts_status',
|
||||
label: 'Prompts',
|
||||
sortable: false,
|
||||
width: '110px',
|
||||
render: (_value: any, row: Content) => {
|
||||
const hasPrompts = row.has_image_prompts;
|
||||
return (
|
||||
<Badge color={hasPrompts ? 'success' : 'warning'} size="sm" variant="light">
|
||||
{hasPrompts ? (
|
||||
<span className="flex items-center gap-1">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
Ready
|
||||
</span>
|
||||
) : (
|
||||
'No Prompts'
|
||||
)}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'images_status',
|
||||
label: 'Images',
|
||||
sortable: false,
|
||||
width: '110px',
|
||||
render: (_value: any, row: Content) => {
|
||||
const hasImages = row.has_generated_images;
|
||||
return (
|
||||
<Badge color={hasImages ? 'success' : 'warning'} size="sm" variant="light">
|
||||
{hasImages ? (
|
||||
<span className="flex items-center gap-1">
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
Generated
|
||||
</span>
|
||||
) : (
|
||||
'No Images'
|
||||
)}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'source',
|
||||
label: 'Source',
|
||||
|
||||
229
frontend/src/config/pages/published.config.tsx
Normal file
229
frontend/src/config/pages/published.config.tsx
Normal file
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* Published Page Configuration
|
||||
* Centralized config for Published page table, filters, and actions
|
||||
*/
|
||||
|
||||
import { Content } from '../../services/api';
|
||||
import Badge from '../../components/ui/badge/Badge';
|
||||
import { formatRelativeDate } from '../../utils/date';
|
||||
import { CheckCircleIcon, ArrowRightIcon } 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 PublishedPageConfig {
|
||||
columns: ColumnConfig[];
|
||||
filters: FilterConfig[];
|
||||
headerMetrics: HeaderMetricConfig[];
|
||||
}
|
||||
|
||||
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;
|
||||
activeSector: { id: number; name: string } | null;
|
||||
}): PublishedPageConfig {
|
||||
const showSectorColumn = !params.activeSector;
|
||||
|
||||
const columns: ColumnConfig[] = [
|
||||
{
|
||||
key: 'title',
|
||||
label: 'Title',
|
||||
sortable: true,
|
||||
sortField: 'title',
|
||||
toggleable: true,
|
||||
toggleContentKey: 'content_html',
|
||||
toggleContentLabel: 'Generated Content',
|
||||
render: (value: string, row: Content) => (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-gray-900 dark:text-white">
|
||||
{value || `Content #${row.id}`}
|
||||
</span>
|
||||
{row.external_url && (
|
||||
<a
|
||||
href={row.external_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-500 hover:text-blue-600 transition-colors"
|
||||
title="View on WordPress"
|
||||
>
|
||||
<ArrowRightIcon className="w-4 h-4" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
label: 'Content Status',
|
||||
sortable: true,
|
||||
sortField: 'status',
|
||||
width: '120px',
|
||||
render: (value: string) => {
|
||||
const statusConfig: Record<string, { color: 'warning' | 'success'; label: string }> = {
|
||||
draft: { color: 'warning', label: 'Draft' },
|
||||
published: { color: 'success', label: 'Published' },
|
||||
};
|
||||
const config = statusConfig[value] || { color: 'warning' as const, label: value };
|
||||
return (
|
||||
<Badge color={config.color} size="sm" variant="light">
|
||||
{config.label}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'wordpress_status',
|
||||
label: 'WordPress',
|
||||
sortable: false,
|
||||
width: '140px',
|
||||
render: (_value: any, row: Content) => {
|
||||
if (row.external_id && row.external_url) {
|
||||
return (
|
||||
<Badge color="success" size="sm" variant="light">
|
||||
<CheckCircleIcon className="w-3 h-3 mr-1" />
|
||||
Published
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Badge color="warning" size="sm" variant="light">
|
||||
Not Published
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'content_type',
|
||||
label: 'Type',
|
||||
sortable: true,
|
||||
sortField: 'content_type',
|
||||
width: '120px',
|
||||
render: (value: string) => (
|
||||
<Badge color="primary" size="sm" variant="light">
|
||||
{TYPE_LABELS[value] || value || '-'}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'content_structure',
|
||||
label: 'Structure',
|
||||
sortable: true,
|
||||
sortField: 'content_structure',
|
||||
width: '150px',
|
||||
render: (value: string) => (
|
||||
<Badge color="info" size="sm" variant="light">
|
||||
{STRUCTURE_LABELS[value] || value || '-'}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'word_count',
|
||||
label: 'Words',
|
||||
sortable: true,
|
||||
sortField: 'word_count',
|
||||
numeric: true,
|
||||
width: '100px',
|
||||
align: 'right' as const,
|
||||
render: (value: number) => (
|
||||
<span className="text-gray-900 dark:text-white">
|
||||
{value ? value.toLocaleString() : '-'}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'created_at',
|
||||
label: 'Created',
|
||||
sortable: true,
|
||||
sortField: 'created_at',
|
||||
date: true,
|
||||
width: '140px',
|
||||
render: (value: string) => (
|
||||
<span className="text-gray-600 dark:text-gray-400">
|
||||
{formatRelativeDate(value)}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const filters: FilterConfig[] = [
|
||||
{
|
||||
key: 'search',
|
||||
label: 'Search',
|
||||
type: 'text',
|
||||
placeholder: 'Search published content...',
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
label: 'Content Status',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Statuses' },
|
||||
{ value: 'draft', label: 'Draft' },
|
||||
{ value: 'published', label: 'Published' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'publishStatus',
|
||||
label: 'WordPress Status',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All' },
|
||||
{ value: 'published', label: 'Published to WP' },
|
||||
{ value: 'not_published', label: 'Not Published' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const headerMetrics: HeaderMetricConfig[] = [
|
||||
{
|
||||
label: 'Total Published',
|
||||
accentColor: 'green',
|
||||
calculate: (data: { totalCount: number }) => data.totalCount,
|
||||
},
|
||||
{
|
||||
label: 'On WordPress',
|
||||
accentColor: 'blue',
|
||||
calculate: (data: { content: Content[] }) =>
|
||||
data.content.filter(c => c.external_id).length,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
columns,
|
||||
filters,
|
||||
headerMetrics,
|
||||
};
|
||||
}
|
||||
@@ -299,12 +299,32 @@ const tableActionsConfigs: Record<string, TableActionsConfig> = {
|
||||
rowActions: [
|
||||
{
|
||||
key: 'edit',
|
||||
label: '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: <CheckCircleIcon className="w-5 h-5 text-blue-500" />,
|
||||
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',
|
||||
|
||||
Reference in New Issue
Block a user