123
This commit is contained in:
@@ -118,8 +118,8 @@ export const createClustersPageConfig = (
|
||||
...(showSectorColumn ? [{
|
||||
...sectorColumn,
|
||||
render: (value: string, row: Cluster) => (
|
||||
<Badge color="info" size="sm" variant="light">
|
||||
{row.sector_name || '-'}
|
||||
<Badge color="info" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{row.sector_name || '-'}</span>
|
||||
</Badge>
|
||||
),
|
||||
}] : []),
|
||||
@@ -156,7 +156,6 @@ export const createClustersPageConfig = (
|
||||
align: 'center' as const,
|
||||
render: (value: number) => {
|
||||
const difficultyNum = getDifficultyNumber(value);
|
||||
const difficultyBadgeVariant = 'light';
|
||||
const difficultyBadgeColor =
|
||||
typeof difficultyNum === 'number' && difficultyNum === 1
|
||||
? 'success'
|
||||
@@ -170,12 +169,8 @@ export const createClustersPageConfig = (
|
||||
? 'error'
|
||||
: 'light';
|
||||
return typeof difficultyNum === 'number' ? (
|
||||
<Badge
|
||||
color={difficultyBadgeColor}
|
||||
variant={difficultyBadgeVariant}
|
||||
size="sm"
|
||||
>
|
||||
{difficultyNum}
|
||||
<Badge color={difficultyBadgeColor} variant="soft" size="xs">
|
||||
<span className="text-[11px] font-normal">{difficultyNum}</span>
|
||||
</Badge>
|
||||
) : (
|
||||
difficultyNum
|
||||
@@ -194,14 +189,14 @@ export const createClustersPageConfig = (
|
||||
...statusColumn,
|
||||
sortable: true,
|
||||
sortField: 'status',
|
||||
render: (value: string) => (
|
||||
<Badge
|
||||
color={value === 'active' ? 'success' : 'warning'}
|
||||
size="sm"
|
||||
>
|
||||
{value}
|
||||
</Badge>
|
||||
),
|
||||
render: (value: string) => {
|
||||
const properCase = value ? value.charAt(0).toUpperCase() + value.slice(1) : '-';
|
||||
return (
|
||||
<Badge color={value === 'active' ? 'success' : 'warning'} size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
...createdColumn,
|
||||
|
||||
@@ -82,7 +82,7 @@ export const createContentPageConfig = (
|
||||
statusFilter: string;
|
||||
setStatusFilter: (value: string) => void;
|
||||
setCurrentPage: (page: number) => void;
|
||||
onViewContent?: (row: Content) => void;
|
||||
onRowClick?: (row: Content) => void;
|
||||
}
|
||||
): ContentPageConfig => {
|
||||
const showSectorColumn = !handlers.activeSector;
|
||||
@@ -99,14 +99,11 @@ export const createContentPageConfig = (
|
||||
...titleColumn,
|
||||
sortable: true,
|
||||
sortField: 'title',
|
||||
toggleable: true,
|
||||
toggleContentKey: 'content_html',
|
||||
toggleContentLabel: 'Generated Content',
|
||||
render: (value: string, row: Content) => (
|
||||
<div>
|
||||
{handlers.onViewContent ? (
|
||||
{handlers.onRowClick ? (
|
||||
<button
|
||||
onClick={() => handlers.onViewContent!(row)}
|
||||
onClick={() => handlers.onRowClick!(row)}
|
||||
className="font-medium text-blue-500 hover:text-blue-600 hover:underline text-left transition-colors"
|
||||
>
|
||||
{row.title || `Content #${row.id}`}
|
||||
@@ -122,69 +119,84 @@ export const createContentPageConfig = (
|
||||
...(showSectorColumn ? [{
|
||||
...sectorColumn,
|
||||
render: (value: string, row: Content) => (
|
||||
<Badge color="info" size="sm" variant="light">
|
||||
{row.sector_name || '-'}
|
||||
<Badge color="info" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{row.sector_name || '-'}</span>
|
||||
</Badge>
|
||||
),
|
||||
}] : []),
|
||||
{
|
||||
key: 'content_type',
|
||||
label: '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>
|
||||
),
|
||||
width: '110px',
|
||||
render: (value: string) => {
|
||||
const label = TYPE_LABELS[value] || value || '-';
|
||||
// Proper case: capitalize first letter only
|
||||
const properCase = label.charAt(0).toUpperCase() + label.slice(1);
|
||||
return (
|
||||
<Badge color="blue" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</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>
|
||||
),
|
||||
width: '130px',
|
||||
render: (value: string) => {
|
||||
const label = STRUCTURE_LABELS[value] || value || '-';
|
||||
// Proper case: capitalize first letter of each word
|
||||
const properCase = label.split(/[_\s]+/).map(word =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1)
|
||||
).join(' ');
|
||||
return (
|
||||
<Badge color="purple" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'cluster_name',
|
||||
label: 'Cluster',
|
||||
sortable: false,
|
||||
width: '150px',
|
||||
width: '130px',
|
||||
render: (_value: any, row: Content) => {
|
||||
const clusterName = row.cluster_name;
|
||||
if (!clusterName) {
|
||||
return <span className="text-gray-400 dark:text-gray-500">-</span>;
|
||||
return <span className="text-gray-400 dark:text-gray-500 text-[11px]">-</span>;
|
||||
}
|
||||
return (
|
||||
<Badge color="primary" size="sm" variant="light">
|
||||
{clusterName}
|
||||
<Badge color="indigo" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{clusterName}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'taxonomy_terms',
|
||||
label: 'Taxonomy',
|
||||
label: 'Tags',
|
||||
sortable: false,
|
||||
width: '180px',
|
||||
width: '150px',
|
||||
render: (_value: any, row: Content) => {
|
||||
const taxonomyTerms = row.taxonomy_terms;
|
||||
if (!taxonomyTerms || taxonomyTerms.length === 0) {
|
||||
return <span className="text-gray-400 dark:text-gray-500">-</span>;
|
||||
return <span className="text-gray-400 dark:text-gray-500 text-[11px]">-</span>;
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{taxonomyTerms.map((term) => (
|
||||
<Badge key={term.id} color="purple" size="sm" variant="light">
|
||||
{term.name}
|
||||
{taxonomyTerms.slice(0, 2).map((term) => (
|
||||
<Badge key={term.id} color="pink" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{term.name}</span>
|
||||
</Badge>
|
||||
))}
|
||||
{taxonomyTerms.length > 2 && (
|
||||
<span className="text-[11px] text-gray-500">+{taxonomyTerms.length - 2}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@@ -194,15 +206,16 @@ export const createContentPageConfig = (
|
||||
sortable: true,
|
||||
sortField: 'status',
|
||||
render: (value: string) => {
|
||||
const statusColors: Record<string, 'warning' | 'success'> = {
|
||||
draft: 'warning',
|
||||
const statusColors: Record<string, 'success' | 'amber'> = {
|
||||
draft: 'amber',
|
||||
published: 'success',
|
||||
};
|
||||
const color = statusColors[value] || 'warning';
|
||||
const label = value === 'published' ? 'Published' : 'Draft';
|
||||
const color = statusColors[value] || 'amber';
|
||||
// Proper case
|
||||
const label = value ? value.charAt(0).toUpperCase() + value.slice(1) : 'Draft';
|
||||
return (
|
||||
<Badge color={color} size="sm" variant="light">
|
||||
{label}
|
||||
<Badge color={color} size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{label}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
@@ -213,20 +226,20 @@ export const createContentPageConfig = (
|
||||
label: 'Source',
|
||||
sortable: true,
|
||||
sortField: 'source',
|
||||
width: '120px',
|
||||
width: '90px',
|
||||
render: (value: any, row: Content) => {
|
||||
const source = value || row.source || 'igny8';
|
||||
const sourceColors: Record<string, 'primary' | 'info'> = {
|
||||
igny8: 'primary',
|
||||
wordpress: 'info',
|
||||
const sourceColors: Record<string, 'teal' | 'cyan'> = {
|
||||
igny8: 'teal',
|
||||
wordpress: 'cyan',
|
||||
};
|
||||
const sourceLabels: Record<string, string> = {
|
||||
igny8: 'IGNY8',
|
||||
wordpress: 'WordPress',
|
||||
igny8: 'Igny8',
|
||||
wordpress: 'Wp',
|
||||
};
|
||||
return (
|
||||
<Badge color={sourceColors[source] || 'primary'} size="sm" variant="light">
|
||||
{sourceLabels[source] || source}
|
||||
<Badge color={sourceColors[source] || 'teal'} size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{sourceLabels[source] || source}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -108,8 +108,8 @@ export const createIdeasPageConfig = (
|
||||
...(showSectorColumn ? [{
|
||||
...sectorColumn,
|
||||
render: (value: string, row: ContentIdea) => (
|
||||
<Badge color="info" size="sm" variant="light">
|
||||
{row.sector_name || '-'}
|
||||
<Badge color="info" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{row.sector_name || '-'}</span>
|
||||
</Badge>
|
||||
),
|
||||
}] : []),
|
||||
@@ -118,24 +118,34 @@ export const createIdeasPageConfig = (
|
||||
label: 'Structure',
|
||||
sortable: true,
|
||||
sortField: 'content_structure',
|
||||
width: '150px',
|
||||
render: (value: string) => (
|
||||
<Badge color="info" size="sm" variant="light">
|
||||
{value?.replace('_', ' ') || '-'}
|
||||
</Badge>
|
||||
),
|
||||
width: '130px',
|
||||
render: (value: string) => {
|
||||
const label = value?.replace('_', ' ') || '-';
|
||||
const properCase = label.split(/[_\s]+/).map(word =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1)
|
||||
).join(' ');
|
||||
return (
|
||||
<Badge color="purple" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'content_type',
|
||||
label: 'Type',
|
||||
sortable: true,
|
||||
sortField: 'content_type',
|
||||
width: '120px',
|
||||
render: (value: string) => (
|
||||
<Badge color="info" size="sm" variant="light">
|
||||
{value?.replace('_', ' ') || '-'}
|
||||
</Badge>
|
||||
),
|
||||
width: '110px',
|
||||
render: (value: string) => {
|
||||
const label = value?.replace('_', ' ') || '-';
|
||||
const properCase = label.charAt(0).toUpperCase() + label.slice(1);
|
||||
return (
|
||||
<Badge color="blue" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'target_keywords',
|
||||
@@ -161,17 +171,15 @@ export const createIdeasPageConfig = (
|
||||
sortable: true,
|
||||
sortField: 'status',
|
||||
render: (value: string) => {
|
||||
const statusColors: Record<string, 'success' | 'warning' | 'error'> = {
|
||||
'new': 'warning',
|
||||
const statusColors: Record<string, 'success' | 'amber' | 'info'> = {
|
||||
'new': 'amber',
|
||||
'scheduled': 'info',
|
||||
'published': 'success',
|
||||
};
|
||||
const properCase = value ? value.charAt(0).toUpperCase() + value.slice(1) : '-';
|
||||
return (
|
||||
<Badge
|
||||
color={statusColors[value] || 'warning'}
|
||||
size="sm"
|
||||
>
|
||||
{value}
|
||||
<Badge color={statusColors[value] || 'amber'} size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -83,10 +83,11 @@ export const createImagesPageConfig = (
|
||||
key: 'featured_image',
|
||||
label: 'Featured Image',
|
||||
sortable: false,
|
||||
width: '200px',
|
||||
width: '150px',
|
||||
render: (_value: any, row: ContentImagesGroup) => (
|
||||
<ContentImageCell
|
||||
image={row.featured_image}
|
||||
showPrompt={false}
|
||||
onImageClick={handlers.onImageClick ? () => handlers.onImageClick!(row.content_id, 'featured') : undefined}
|
||||
/>
|
||||
),
|
||||
@@ -99,12 +100,13 @@ export const createImagesPageConfig = (
|
||||
key: `in_article_${i}`,
|
||||
label: `In-Article ${i}`,
|
||||
sortable: false,
|
||||
width: '200px',
|
||||
width: '150px',
|
||||
render: (_value: any, row: ContentImagesGroup) => {
|
||||
const image = row.in_article_images.find(img => img.position === i);
|
||||
return (
|
||||
<ContentImageCell
|
||||
image={image || null}
|
||||
showPrompt={false}
|
||||
onImageClick={handlers.onImageClick && image ? () => handlers.onImageClick!(row.content_id, 'in_article', i) : undefined}
|
||||
/>
|
||||
);
|
||||
@@ -112,13 +114,13 @@ export const createImagesPageConfig = (
|
||||
});
|
||||
}
|
||||
|
||||
// Add overall status column with Generate Images button
|
||||
// Add status column (separate from generate button)
|
||||
columns.push({
|
||||
key: 'overall_status',
|
||||
label: 'Status',
|
||||
sortable: true,
|
||||
sortField: 'overall_status',
|
||||
width: '180px',
|
||||
width: '120px',
|
||||
render: (value: string, row: ContentImagesGroup) => {
|
||||
const statusColors: Record<string, 'success' | 'warning' | 'error' | 'info'> = {
|
||||
'complete': 'success',
|
||||
@@ -133,33 +135,44 @@ export const createImagesPageConfig = (
|
||||
'failed': 'Failed',
|
||||
};
|
||||
|
||||
return (
|
||||
<Badge color={statusColors[value] || 'amber'} size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{labels[value] || value}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// Add generate button column (separate from status)
|
||||
columns.push({
|
||||
key: 'generate_action',
|
||||
label: 'Actions',
|
||||
sortable: false,
|
||||
width: '120px',
|
||||
render: (_value: any, row: ContentImagesGroup) => {
|
||||
// Check if there are any pending images with prompts
|
||||
const hasPendingImages =
|
||||
(row.featured_image?.status === 'pending' && row.featured_image?.prompt) ||
|
||||
row.in_article_images.some(img => img.status === 'pending' && img.prompt);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge
|
||||
color={statusColors[value] || 'warning'}
|
||||
size="sm"
|
||||
>
|
||||
{labels[value] || value}
|
||||
</Badge>
|
||||
{hasPendingImages && handlers.onGenerateImages && (
|
||||
<>
|
||||
{hasPendingImages && handlers.onGenerateImages ? (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handlers.onGenerateImages!(row.content_id);
|
||||
}}
|
||||
className="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium text-white bg-brand-500 hover:bg-brand-600 rounded transition-colors"
|
||||
className="inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium text-white bg-brand-500 hover:bg-brand-600 rounded transition-colors"
|
||||
title="Generate Images"
|
||||
>
|
||||
<BoltIcon className="w-3 h-3" />
|
||||
<BoltIcon className="w-4 h-4" />
|
||||
Generate
|
||||
</button>
|
||||
) : (
|
||||
<span className="text-gray-400 dark:text-gray-500 text-sm">-</span>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -151,8 +151,8 @@ export const createKeywordsPageConfig = (
|
||||
...(showSectorColumn ? [{
|
||||
...sectorColumn,
|
||||
render: (value: string, row: Keyword) => (
|
||||
<Badge color="info" size="sm" variant="light">
|
||||
{row.sector_name || '-'}
|
||||
<Badge color="info" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{row.sector_name || '-'}</span>
|
||||
</Badge>
|
||||
),
|
||||
}] : []),
|
||||
@@ -175,26 +175,21 @@ export const createKeywordsPageConfig = (
|
||||
align: 'center' as const,
|
||||
render: (value: number) => {
|
||||
const difficultyNum = getDifficultyNumber(value);
|
||||
const difficultyBadgeVariant = 'light';
|
||||
const difficultyBadgeColor =
|
||||
typeof difficultyNum === 'number' && difficultyNum === 1
|
||||
? 'success'
|
||||
: typeof difficultyNum === 'number' && difficultyNum === 2
|
||||
? 'success'
|
||||
: typeof difficultyNum === 'number' && difficultyNum === 3
|
||||
? 'warning'
|
||||
? 'amber'
|
||||
: typeof difficultyNum === 'number' && difficultyNum === 4
|
||||
? 'error'
|
||||
: typeof difficultyNum === 'number' && difficultyNum === 5
|
||||
? 'error'
|
||||
: 'light';
|
||||
: 'gray';
|
||||
return typeof difficultyNum === 'number' ? (
|
||||
<Badge
|
||||
color={difficultyBadgeColor}
|
||||
variant={difficultyBadgeVariant}
|
||||
size="sm"
|
||||
>
|
||||
{difficultyNum}
|
||||
<Badge color={difficultyBadgeColor} variant="soft" size="xs">
|
||||
<span className="text-[11px] font-normal">{difficultyNum}</span>
|
||||
</Badge>
|
||||
) : (
|
||||
difficultyNum
|
||||
@@ -220,13 +215,10 @@ export const createKeywordsPageConfig = (
|
||||
return 'info'; // Blue for informational or default
|
||||
};
|
||||
|
||||
const properCase = value ? value.charAt(0).toUpperCase() + value.slice(1) : '-';
|
||||
return (
|
||||
<Badge
|
||||
color={getIntentColor(value)}
|
||||
size="sm"
|
||||
variant={value?.toLowerCase() === 'informational' ? 'light' : undefined}
|
||||
>
|
||||
{value}
|
||||
<Badge color={getIntentColor(value)} size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
@@ -236,18 +228,20 @@ export const createKeywordsPageConfig = (
|
||||
sortable: true,
|
||||
sortField: 'status',
|
||||
render: (value: string) => {
|
||||
const properCase = value ? value.charAt(0).toUpperCase() + value.slice(1) : '-';
|
||||
return (
|
||||
<Badge
|
||||
color={
|
||||
value === 'active'
|
||||
? 'success'
|
||||
: value === 'pending'
|
||||
? 'warning'
|
||||
? 'amber'
|
||||
: 'error'
|
||||
}
|
||||
size="sm"
|
||||
size="xs"
|
||||
variant="soft"
|
||||
>
|
||||
{value}
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -92,14 +92,14 @@ export function createPublishedPageConfig(params: {
|
||||
sortField: 'status',
|
||||
width: '120px',
|
||||
render: (value: string) => {
|
||||
const statusConfig: Record<string, { color: 'warning' | 'success'; label: string }> = {
|
||||
draft: { color: 'warning', label: 'Draft' },
|
||||
const statusConfig: Record<string, { color: 'amber' | 'success'; label: string }> = {
|
||||
draft: { color: 'amber', label: 'Draft' },
|
||||
published: { color: 'success', label: 'Published' },
|
||||
};
|
||||
const config = statusConfig[value] || { color: 'warning' as const, label: value };
|
||||
const config = statusConfig[value] || { color: 'amber' as const, label: value };
|
||||
return (
|
||||
<Badge color={config.color} size="sm" variant="light">
|
||||
{config.label}
|
||||
<Badge color={config.color} size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{config.label}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
@@ -112,15 +112,15 @@ export function createPublishedPageConfig(params: {
|
||||
render: (_value: any, row: Content) => {
|
||||
if (row.external_id && row.external_url) {
|
||||
return (
|
||||
<Badge color="success" size="sm" variant="light">
|
||||
<Badge color="success" size="xs" variant="soft">
|
||||
<CheckCircleIcon className="w-3 h-3 mr-1" />
|
||||
Published
|
||||
<span className="text-[11px] font-normal">Published</span>
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Badge color="warning" size="sm" variant="light">
|
||||
Not Published
|
||||
<Badge color="amber" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">Not Published</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
@@ -130,24 +130,34 @@ export function createPublishedPageConfig(params: {
|
||||
label: 'Type',
|
||||
sortable: true,
|
||||
sortField: 'content_type',
|
||||
width: '120px',
|
||||
render: (value: string) => (
|
||||
<Badge color="primary" size="sm" variant="light">
|
||||
{TYPE_LABELS[value] || value || '-'}
|
||||
</Badge>
|
||||
),
|
||||
width: '110px',
|
||||
render: (value: string) => {
|
||||
const label = TYPE_LABELS[value] || value || '-';
|
||||
const properCase = label.charAt(0).toUpperCase() + label.slice(1);
|
||||
return (
|
||||
<Badge color="blue" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</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>
|
||||
),
|
||||
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 (
|
||||
<Badge color="purple" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'word_count',
|
||||
|
||||
@@ -52,87 +52,104 @@ export function createReviewPageConfig(params: {
|
||||
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: '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 <span className="text-gray-400 dark:text-gray-500">-</span>;
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{categories.map((cat: any) => (
|
||||
<span key={cat.id} className="px-2 py-0.5 bg-purple-50 dark:bg-purple-900/20 text-purple-700 dark:text-purple-300 rounded-full text-xs font-medium">{cat.name}</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
toggleable: true,
|
||||
defaultVisible: false,
|
||||
},
|
||||
{
|
||||
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 <span className="text-gray-400 dark:text-gray-500">-</span>;
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{tags.map((tag: any) => (
|
||||
<span key={tag.id} className="px-2 py-0.5 bg-brand-50 dark:bg-brand-900/20 text-brand-700 dark:text-brand-300 rounded-full text-xs font-medium">{tag.name}</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
toggleable: true,
|
||||
defaultVisible: false,
|
||||
},
|
||||
{
|
||||
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>
|
||||
{params.onRowClick ? (
|
||||
<button
|
||||
onClick={() => params.onRowClick!(row)}
|
||||
className="font-medium text-blue-500 hover:text-blue-600 hover:underline text-left transition-colors"
|
||||
>
|
||||
{value || `Content #${row.id}`}
|
||||
</button>
|
||||
) : (
|
||||
<span className="font-medium text-gray-900 dark:text-white">
|
||||
{value || `Content #${row.id}`}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
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 <span className="text-gray-400 dark:text-gray-500">-</span>;
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{categories.map((cat: any) => (
|
||||
<span key={cat.id} className="px-2 py-0.5 bg-purple-50 dark:bg-purple-900/20 text-purple-700 dark:text-purple-300 rounded-full text-xs font-medium">{cat.name}</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
toggleable: true,
|
||||
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 <span className="text-gray-400 dark:text-gray-500">-</span>;
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{tags.map((tag: any) => (
|
||||
<span key={tag.id} className="px-2 py-0.5 bg-brand-50 dark:bg-brand-900/20 text-brand-700 dark:text-brand-300 rounded-full text-xs font-medium">{tag.name}</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
toggleable: true,
|
||||
defaultVisible: true,
|
||||
},
|
||||
{
|
||||
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>
|
||||
),
|
||||
width: '110px',
|
||||
render: (value: string) => {
|
||||
const label = TYPE_LABELS[value] || value || '-';
|
||||
const properCase = label.charAt(0).toUpperCase() + label.slice(1);
|
||||
return (
|
||||
<Badge color="blue" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</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>
|
||||
),
|
||||
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 (
|
||||
<Badge color="purple" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'cluster_name',
|
||||
@@ -145,8 +162,8 @@ export function createReviewPageConfig(params: {
|
||||
return <span className="text-gray-400 dark:text-gray-500">-</span>;
|
||||
}
|
||||
return (
|
||||
<Badge color="primary" size="sm" variant="light">
|
||||
{clusterName}
|
||||
<Badge color="indigo" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{clusterName}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
@@ -186,8 +203,8 @@ export function createReviewPageConfig(params: {
|
||||
sortable: false,
|
||||
width: '120px',
|
||||
render: (value: string, row: Content) => (
|
||||
<Badge color="info" size="sm" variant="light">
|
||||
{row.sector_name || '-'}
|
||||
<Badge color="info" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{row.sector_name || '-'}</span>
|
||||
</Badge>
|
||||
),
|
||||
});
|
||||
|
||||
@@ -116,8 +116,8 @@ export const createTasksPageConfig = (
|
||||
{displayTitle}
|
||||
</span>
|
||||
{isSiteBuilder && (
|
||||
<Badge color="purple" size="sm" variant="light">
|
||||
Site Builder
|
||||
<Badge color="purple" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">Site Builder</span>
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
@@ -128,8 +128,8 @@ export const createTasksPageConfig = (
|
||||
...(showSectorColumn ? [{
|
||||
...sectorColumn,
|
||||
render: (value: string, row: Task) => (
|
||||
<Badge color="info" size="sm" variant="light">
|
||||
{row.sector_name || '-'}
|
||||
<Badge color="info" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{row.sector_name || '-'}</span>
|
||||
</Badge>
|
||||
),
|
||||
}] : []),
|
||||
@@ -153,53 +153,60 @@ export const createTasksPageConfig = (
|
||||
return <span className="text-gray-400 dark:text-gray-500">-</span>;
|
||||
}
|
||||
return (
|
||||
<Badge color="purple" size="sm" variant="light">
|
||||
{taxonomyName}
|
||||
<Badge color="purple" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{taxonomyName}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'content_type',
|
||||
label: '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>
|
||||
),
|
||||
width: '110px',
|
||||
render: (value: string) => {
|
||||
const label = TYPE_LABELS[value] || value || '-';
|
||||
const properCase = label.charAt(0).toUpperCase() + label.slice(1);
|
||||
return (
|
||||
<Badge color="blue" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</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>
|
||||
),
|
||||
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 (
|
||||
<Badge color="purple" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
...statusColumn,
|
||||
sortable: true,
|
||||
sortField: 'status',
|
||||
render: (value: string) => {
|
||||
const statusColors: Record<string, 'success' | 'warning'> = {
|
||||
queued: 'warning',
|
||||
const statusColors: Record<string, 'success' | 'amber'> = {
|
||||
queued: 'amber',
|
||||
completed: 'success',
|
||||
};
|
||||
const label = value ? value.replace('_', ' ') : '';
|
||||
const formatted = label ? label.charAt(0).toUpperCase() + label.slice(1) : '';
|
||||
return (
|
||||
<Badge
|
||||
color={statusColors[value] || 'warning'}
|
||||
size="sm"
|
||||
>
|
||||
{formatted}
|
||||
<Badge color={statusColors[value] || 'amber'} size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{formatted}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user