Styles styels styles
This commit is contained in:
@@ -62,17 +62,18 @@ export function createApprovedPageConfig(params: {
|
||||
label: 'Content Idea Title',
|
||||
sortable: true,
|
||||
sortField: 'title',
|
||||
width: '400px',
|
||||
render: (value: string, row: Content) => (
|
||||
<div className="flex items-center gap-2">
|
||||
{params.onRowClick ? (
|
||||
<button
|
||||
onClick={() => params.onRowClick!(row)}
|
||||
className="text-base font-light text-brand-500 hover:text-brand-600 hover:underline text-left transition-colors"
|
||||
className="text-sm text-brand-500 hover:text-brand-600 hover:underline text-left transition-colors"
|
||||
>
|
||||
{value || `Content #${row.id}`}
|
||||
</button>
|
||||
) : (
|
||||
<span className="text-base font-light text-gray-900 dark:text-white">
|
||||
<span className="text-sm text-gray-900 dark:text-white">
|
||||
{value || `Content #${row.id}`}
|
||||
</span>
|
||||
)}
|
||||
@@ -95,7 +96,6 @@ export function createApprovedPageConfig(params: {
|
||||
label: 'Status',
|
||||
sortable: true,
|
||||
sortField: 'status',
|
||||
width: '130px',
|
||||
render: (value: string, row: Content) => {
|
||||
// Map internal status to user-friendly labels
|
||||
const statusConfig: Record<string, { color: 'success' | 'blue' | 'amber' | 'gray'; label: string }> = {
|
||||
@@ -251,8 +251,8 @@ export function createApprovedPageConfig(params: {
|
||||
sortable: false, // Backend doesn't support sorting by word_count
|
||||
sortField: 'word_count',
|
||||
numeric: true,
|
||||
width: '100px',
|
||||
align: 'right' as const,
|
||||
align: 'center' as const,
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: number) => (
|
||||
<span className="text-gray-900 dark:text-white">
|
||||
{value ? value.toLocaleString() : '-'}
|
||||
@@ -265,7 +265,8 @@ export function createApprovedPageConfig(params: {
|
||||
sortable: true,
|
||||
sortField: 'created_at',
|
||||
date: true,
|
||||
width: '140px',
|
||||
width: '130px',
|
||||
hasActions: true,
|
||||
render: (value: string) => (
|
||||
<span className="text-gray-600 dark:text-gray-400">
|
||||
{formatRelativeDate(value)}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
sectorColumn,
|
||||
difficultyColumn,
|
||||
statusColumn,
|
||||
createdColumn,
|
||||
createdWithActionsColumn,
|
||||
} from '../snippets/columns.snippets';
|
||||
import Badge from '../../components/ui/badge/Badge';
|
||||
import { formatRelativeDate } from '../../utils/date';
|
||||
@@ -107,6 +107,7 @@ export const createClustersPageConfig = (
|
||||
label: 'Cluster Name',
|
||||
sortable: true,
|
||||
sortField: 'name',
|
||||
width: '400px',
|
||||
render: (value: string, row: Cluster) => (
|
||||
<Link
|
||||
to={`/planner/clusters/${row.id}`}
|
||||
@@ -130,17 +131,8 @@ export const createClustersPageConfig = (
|
||||
label: 'KW Count',
|
||||
sortable: true,
|
||||
sortField: 'keywords_count',
|
||||
width: '120px',
|
||||
align: 'center' as const,
|
||||
render: (value: number) => value.toLocaleString(),
|
||||
},
|
||||
{
|
||||
key: 'ideas_count',
|
||||
label: 'Ideas',
|
||||
sortable: false, // Backend doesn't support sorting by ideas_count
|
||||
sortField: 'ideas_count',
|
||||
width: '120px',
|
||||
align: 'center' as const,
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: number) => value.toLocaleString(),
|
||||
},
|
||||
{
|
||||
@@ -148,8 +140,8 @@ export const createClustersPageConfig = (
|
||||
label: 'Volume',
|
||||
sortable: true,
|
||||
sortField: 'volume',
|
||||
width: '120px',
|
||||
align: 'center' as const,
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: number) => value.toLocaleString(),
|
||||
},
|
||||
{
|
||||
@@ -159,6 +151,7 @@ export const createClustersPageConfig = (
|
||||
sortable: true,
|
||||
sortField: 'difficulty',
|
||||
align: 'center' as const,
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: number) => {
|
||||
const difficultyNum = getDifficultyNumber(value);
|
||||
const difficultyBadgeColor =
|
||||
@@ -187,8 +180,8 @@ export const createClustersPageConfig = (
|
||||
label: 'Content',
|
||||
sortable: false, // Backend doesn't support sorting by content_count
|
||||
sortField: 'content_count',
|
||||
width: '120px',
|
||||
align: 'center' as const,
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: number) => value.toLocaleString(),
|
||||
},
|
||||
{
|
||||
@@ -205,9 +198,44 @@ export const createClustersPageConfig = (
|
||||
},
|
||||
},
|
||||
{
|
||||
...createdColumn,
|
||||
key: 'ideas_count',
|
||||
label: 'Ideas',
|
||||
sortable: false, // Backend doesn't support sorting by ideas_count
|
||||
sortField: 'ideas_count',
|
||||
align: 'center' as const,
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: number) => value.toLocaleString(),
|
||||
},
|
||||
// Generate Ideas action column - only shows button for status = 'new'
|
||||
{
|
||||
key: 'generate_action',
|
||||
label: 'Generate Ideas',
|
||||
sortable: false,
|
||||
render: (_value: any, row: Cluster) => {
|
||||
// Only show generate button for clusters with status 'new'
|
||||
if (row.status === 'new' && handlers.onGenerateIdeas) {
|
||||
return (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handlers.onGenerateIdeas!(row.id);
|
||||
}}
|
||||
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 Ideas"
|
||||
>
|
||||
<BoltIcon className="w-4 h-4" />
|
||||
Generate
|
||||
</button>
|
||||
);
|
||||
}
|
||||
return <span className="text-gray-400 dark:text-gray-500 text-sm">-</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
...createdWithActionsColumn,
|
||||
sortable: true,
|
||||
sortField: 'created_at',
|
||||
width: '130px',
|
||||
render: (value: string) => formatRelativeDate(value),
|
||||
},
|
||||
// Optional columns - hidden by default
|
||||
@@ -240,32 +268,6 @@ export const createClustersPageConfig = (
|
||||
defaultVisible: false,
|
||||
render: (value: string) => formatRelativeDate(value),
|
||||
},
|
||||
// Generate Ideas action column - only shows button for status = 'new'
|
||||
{
|
||||
key: 'generate_action',
|
||||
label: 'Generate Ideas',
|
||||
sortable: false,
|
||||
width: '120px',
|
||||
render: (_value: any, row: Cluster) => {
|
||||
// Only show generate button for clusters with status 'new'
|
||||
if (row.status === 'new' && handlers.onGenerateIdeas) {
|
||||
return (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handlers.onGenerateIdeas!(row.id);
|
||||
}}
|
||||
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 Ideas"
|
||||
>
|
||||
<BoltIcon className="w-4 h-4" />
|
||||
Generate
|
||||
</button>
|
||||
);
|
||||
}
|
||||
return <span className="text-gray-400 dark:text-gray-500 text-sm">-</span>;
|
||||
},
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ import React from 'react';
|
||||
import {
|
||||
titleColumn,
|
||||
statusColumn,
|
||||
createdColumn,
|
||||
createdWithActionsColumn,
|
||||
wordCountColumn,
|
||||
sectorColumn,
|
||||
} from '../snippets/columns.snippets';
|
||||
@@ -100,17 +100,18 @@ export const createContentPageConfig = (
|
||||
label: 'Content Idea Title',
|
||||
sortable: true,
|
||||
sortField: 'title',
|
||||
width: '400px',
|
||||
render: (value: string, row: Content) => (
|
||||
<div>
|
||||
{handlers.onRowClick ? (
|
||||
<button
|
||||
onClick={() => handlers.onRowClick!(row)}
|
||||
className="text-base font-light text-brand-500 hover:text-brand-600 hover:underline text-left transition-colors"
|
||||
className="text-sm text-brand-500 hover:text-brand-600 hover:underline text-left transition-colors"
|
||||
>
|
||||
{row.title || `Content #${row.id}`}
|
||||
</button>
|
||||
) : (
|
||||
<div className="text-base font-light text-gray-900 dark:text-white">
|
||||
<div className="text-sm text-gray-900 dark:text-white">
|
||||
{row.title || `Content #${row.id}`}
|
||||
</div>
|
||||
)}
|
||||
@@ -130,7 +131,6 @@ export const createContentPageConfig = (
|
||||
label: 'Type',
|
||||
sortable: true,
|
||||
sortField: 'content_type',
|
||||
width: '110px',
|
||||
render: (value: string) => {
|
||||
const label = TYPE_LABELS[value] || value || '-';
|
||||
// Proper case: capitalize first letter only
|
||||
@@ -147,7 +147,6 @@ export const createContentPageConfig = (
|
||||
label: 'Structure',
|
||||
sortable: true,
|
||||
sortField: 'content_structure',
|
||||
width: '130px',
|
||||
render: (value: string) => {
|
||||
const label = STRUCTURE_LABELS[value] || value || '-';
|
||||
// Proper case: capitalize first letter of each word
|
||||
@@ -165,16 +164,15 @@ export const createContentPageConfig = (
|
||||
key: 'cluster_name',
|
||||
label: 'Cluster',
|
||||
sortable: false,
|
||||
width: '130px',
|
||||
render: (_value: any, row: Content) => {
|
||||
const clusterName = row.cluster_name;
|
||||
if (!clusterName) {
|
||||
return <span className="text-gray-400 dark:text-gray-500 text-[11px]">-</span>;
|
||||
return <span className="text-gray-400 dark:text-gray-500">-</span>;
|
||||
}
|
||||
return (
|
||||
<Badge color="indigo" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{clusterName}</span>
|
||||
</Badge>
|
||||
<span className="text-gray-800 dark:text-white">
|
||||
{clusterName}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -305,11 +303,11 @@ export const createContentPageConfig = (
|
||||
),
|
||||
},
|
||||
{
|
||||
...createdColumn,
|
||||
...createdWithActionsColumn,
|
||||
sortable: true,
|
||||
sortField: 'created_at',
|
||||
label: 'Created',
|
||||
align: 'right',
|
||||
width: '130px',
|
||||
render: (value: string, row: Content) => {
|
||||
// Prompt icon logic (unchanged)
|
||||
const hasPrompts = row.has_image_prompts || false;
|
||||
@@ -335,7 +333,7 @@ export const createContentPageConfig = (
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-end gap-3 pr-10">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-gray-700 dark:text-gray-300 whitespace-nowrap">
|
||||
{formatRelativeDate(value)}
|
||||
</span>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
titleColumn,
|
||||
sectorColumn,
|
||||
statusColumn,
|
||||
createdColumn,
|
||||
createdWithActionsColumn,
|
||||
} from '../snippets/columns.snippets';
|
||||
import Badge from '../../components/ui/badge/Badge';
|
||||
import { formatRelativeDate } from '../../utils/date';
|
||||
@@ -98,6 +98,7 @@ export const createIdeasPageConfig = (
|
||||
label: 'Content Idea Title',
|
||||
sortable: true,
|
||||
sortField: 'idea_title',
|
||||
width: '400px',
|
||||
toggleable: true, // Enable toggle for this column
|
||||
toggleContentKey: 'description', // Use description field for toggle content
|
||||
toggleContentLabel: 'Content Outline', // Label for expanded content
|
||||
@@ -119,7 +120,6 @@ export const createIdeasPageConfig = (
|
||||
label: 'Structure',
|
||||
sortable: false, // Backend doesn't support sorting by content_structure
|
||||
sortField: 'content_structure',
|
||||
width: '130px',
|
||||
render: (value: string) => {
|
||||
const label = value?.replace('_', ' ') || '-';
|
||||
const properCase = label.split(/[_\s]+/).map(word =>
|
||||
@@ -137,7 +137,6 @@ export const createIdeasPageConfig = (
|
||||
label: 'Type',
|
||||
sortable: false, // Backend doesn't support sorting by content_type
|
||||
sortField: 'content_type',
|
||||
width: '110px',
|
||||
render: (value: string) => {
|
||||
const label = value?.replace('_', ' ') || '-';
|
||||
const properCase = label.charAt(0).toUpperCase() + label.slice(1);
|
||||
@@ -152,9 +151,8 @@ export const createIdeasPageConfig = (
|
||||
key: 'target_keywords',
|
||||
label: 'Keywords',
|
||||
sortable: false,
|
||||
width: '250px',
|
||||
render: (value: string) => (
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400 truncate block max-w-[250px]">
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">
|
||||
{value || '-'}
|
||||
</span>
|
||||
),
|
||||
@@ -164,7 +162,6 @@ export const createIdeasPageConfig = (
|
||||
label: 'Cluster',
|
||||
sortable: false, // Backend doesn't support sorting by keyword_cluster_id
|
||||
sortField: 'keyword_cluster_id',
|
||||
width: '200px',
|
||||
render: (_value: string, row: ContentIdea) => {
|
||||
if (row.keyword_cluster_id && row.keyword_cluster_name) {
|
||||
return (
|
||||
@@ -202,14 +199,15 @@ export const createIdeasPageConfig = (
|
||||
label: 'Words',
|
||||
sortable: true,
|
||||
sortField: 'estimated_word_count',
|
||||
width: '100px',
|
||||
align: 'center' as const,
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: number) => value.toLocaleString(),
|
||||
},
|
||||
{
|
||||
...createdColumn,
|
||||
...createdWithActionsColumn,
|
||||
sortable: true,
|
||||
sortField: 'created_at',
|
||||
width: '130px',
|
||||
render: (value: string) => formatRelativeDate(value),
|
||||
},
|
||||
// Optional columns - hidden by default
|
||||
|
||||
@@ -64,7 +64,7 @@ export const createImagesPageConfig = (
|
||||
label: 'Content Title',
|
||||
sortable: true,
|
||||
sortField: 'content_title',
|
||||
width: '250px',
|
||||
width: '400px',
|
||||
render: (_value: string, row: ContentImagesGroup) => {
|
||||
const statusColors: Record<string, 'warning' | 'info' | 'success'> = {
|
||||
draft: 'warning',
|
||||
@@ -130,13 +130,14 @@ export const createImagesPageConfig = (
|
||||
});
|
||||
}
|
||||
|
||||
// Add status column (separate from generate button)
|
||||
// Add status column with actions (3-dot dropdown rendered by TablePageTemplate)
|
||||
columns.push({
|
||||
key: 'overall_status',
|
||||
label: 'Status',
|
||||
sortable: true,
|
||||
sortField: 'overall_status',
|
||||
width: '120px',
|
||||
width: '130px',
|
||||
hasActions: true,
|
||||
render: (value: string, row: ContentImagesGroup) => {
|
||||
const statusColors: Record<string, 'success' | 'warning' | 'error' | 'info'> = {
|
||||
'complete': 'success',
|
||||
@@ -159,40 +160,6 @@ export const createImagesPageConfig = (
|
||||
},
|
||||
});
|
||||
|
||||
// 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 (
|
||||
<>
|
||||
{hasPendingImages && handlers.onGenerateImages ? (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handlers.onGenerateImages!(row.content_id);
|
||||
}}
|
||||
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-4 h-4" />
|
||||
Generate
|
||||
</button>
|
||||
) : (
|
||||
<span className="text-gray-400 dark:text-gray-500 text-sm">-</span>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
columns,
|
||||
filters: [
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
clusterColumn,
|
||||
sectorColumn,
|
||||
statusColumn,
|
||||
createdColumn,
|
||||
createdWithActionsColumn,
|
||||
} from '../snippets/columns.snippets';
|
||||
// Icons removed - bulkActions and rowActions are now in table-actions.config.tsx
|
||||
import Badge from '../../components/ui/badge/Badge';
|
||||
@@ -146,13 +146,19 @@ export const createKeywordsPageConfig = (
|
||||
...keywordColumn,
|
||||
sortable: false, // Backend doesn't support sorting by keyword field
|
||||
sortField: 'seed_keyword__keyword',
|
||||
width: '300px',
|
||||
render: (value: string) => (
|
||||
<span>
|
||||
{value || '-'}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
// Sector column - only show when viewing all sectors
|
||||
...(showSectorColumn ? [{
|
||||
...sectorColumn,
|
||||
render: (value: string, row: Keyword) => (
|
||||
<Badge color="info" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{row.sector_name || '-'}</span>
|
||||
{row.sector_name || '-'}
|
||||
</Badge>
|
||||
),
|
||||
}] : []),
|
||||
@@ -161,16 +167,18 @@ export const createKeywordsPageConfig = (
|
||||
sortable: true,
|
||||
sortField: 'seed_keyword__volume', // Backend expects seed_keyword__volume
|
||||
align: 'center' as const,
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: number) => value.toLocaleString(),
|
||||
},
|
||||
{
|
||||
...clusterColumn,
|
||||
sortable: false, // Backend doesn't support sorting by cluster_id
|
||||
sortField: 'cluster_id',
|
||||
width: '300px',
|
||||
render: (_value: string, row: Keyword) => row.cluster_name ? (
|
||||
<Badge color="info" size="xs" variant="outline">
|
||||
<span className="text-[11px] font-normal">{row.cluster_name}</span>
|
||||
</Badge>
|
||||
<span className="text-gray-800 dark:text-white">
|
||||
{row.cluster_name}
|
||||
</span>
|
||||
) : <span className="text-gray-400">-</span>,
|
||||
},
|
||||
{
|
||||
@@ -178,6 +186,7 @@ export const createKeywordsPageConfig = (
|
||||
sortable: true,
|
||||
sortField: 'seed_keyword__difficulty', // Backend expects seed_keyword__difficulty
|
||||
align: 'center' as const,
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: number) => {
|
||||
const difficultyNum = getDifficultyNumber(value);
|
||||
const difficultyBadgeColor =
|
||||
@@ -194,7 +203,7 @@ export const createKeywordsPageConfig = (
|
||||
: 'gray';
|
||||
return typeof difficultyNum === 'number' ? (
|
||||
<Badge color={difficultyBadgeColor} variant="soft" size="xs">
|
||||
<span className="text-[11px] font-normal">{difficultyNum}</span>
|
||||
{difficultyNum}
|
||||
</Badge>
|
||||
) : (
|
||||
difficultyNum
|
||||
@@ -206,23 +215,12 @@ export const createKeywordsPageConfig = (
|
||||
sortable: false, // Backend doesn't support sorting by country
|
||||
sortField: 'seed_keyword__country',
|
||||
align: 'center' as const,
|
||||
render: (value: string) => {
|
||||
const countryNames: Record<string, string> = {
|
||||
'US': 'United States',
|
||||
'CA': 'Canada',
|
||||
'GB': 'United Kingdom',
|
||||
'AE': 'United Arab Emirates',
|
||||
'AU': 'Australia',
|
||||
'IN': 'India',
|
||||
'PK': 'Pakistan',
|
||||
};
|
||||
const displayName = countryNames[value] || value || '-';
|
||||
return (
|
||||
<Badge color="info" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{value || '-'}</span>
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: string) => (
|
||||
<Badge color="info" size="xs" variant="soft">
|
||||
{value || '-'}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
...statusColumn,
|
||||
@@ -242,16 +240,17 @@ export const createKeywordsPageConfig = (
|
||||
size="xs"
|
||||
variant="soft"
|
||||
>
|
||||
<span className="text-[11px] font-normal">{properCase}</span>
|
||||
{properCase}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
...createdColumn,
|
||||
...createdWithActionsColumn,
|
||||
label: 'Added',
|
||||
sortable: true,
|
||||
sortField: 'created_at',
|
||||
width: '130px',
|
||||
render: (value: string) => formatRelativeDate(value),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -62,17 +62,18 @@ export function createReviewPageConfig(params: {
|
||||
label: 'Content Idea Title',
|
||||
sortable: true,
|
||||
sortField: 'title',
|
||||
width: '400px',
|
||||
render: (value: string, row: Content) => (
|
||||
<div className="flex items-center gap-2">
|
||||
{params.onRowClick ? (
|
||||
<button
|
||||
onClick={() => params.onRowClick!(row)}
|
||||
className="text-base font-light text-brand-500 hover:text-brand-600 hover:underline text-left transition-colors"
|
||||
className="text-sm text-brand-500 hover:text-brand-600 hover:underline text-left transition-colors"
|
||||
>
|
||||
{value || `Content #${row.id}`}
|
||||
</button>
|
||||
) : (
|
||||
<span className="text-base font-light text-gray-900 dark:text-white">
|
||||
<span className="text-sm text-gray-900 dark:text-white">
|
||||
{value || `Content #${row.id}`}
|
||||
</span>
|
||||
)}
|
||||
@@ -83,7 +84,6 @@ export function createReviewPageConfig(params: {
|
||||
key: 'categories',
|
||||
label: 'Categories',
|
||||
sortable: false,
|
||||
width: '180px',
|
||||
render: (_value: any, row: Content) => {
|
||||
const categories = row.categories || [];
|
||||
if (!categories || categories.length === 0) {
|
||||
@@ -107,7 +107,6 @@ export function createReviewPageConfig(params: {
|
||||
key: 'tags',
|
||||
label: 'Tags',
|
||||
sortable: false,
|
||||
width: '180px',
|
||||
render: (_value: any, row: Content) => {
|
||||
const tags = row.tags || [];
|
||||
if (!tags || tags.length === 0) {
|
||||
@@ -132,7 +131,6 @@ export function createReviewPageConfig(params: {
|
||||
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);
|
||||
@@ -148,7 +146,6 @@ export function createReviewPageConfig(params: {
|
||||
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 =>
|
||||
@@ -165,16 +162,15 @@ export function createReviewPageConfig(params: {
|
||||
key: 'cluster_name',
|
||||
label: 'Cluster',
|
||||
sortable: false,
|
||||
width: '150px',
|
||||
render: (_value: any, row: Content) => {
|
||||
const clusterName = row.cluster_name;
|
||||
if (!clusterName) {
|
||||
return <span className="text-gray-400 dark:text-gray-500">-</span>;
|
||||
}
|
||||
return (
|
||||
<Badge color="indigo" size="xs" variant="soft">
|
||||
<span className="text-[11px] font-normal">{clusterName}</span>
|
||||
</Badge>
|
||||
<span className="text-gray-800 dark:text-white">
|
||||
{clusterName}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -183,7 +179,6 @@ export function createReviewPageConfig(params: {
|
||||
label: 'Status',
|
||||
sortable: true,
|
||||
sortField: 'status',
|
||||
width: '120px',
|
||||
render: (value: string, row: Content) => {
|
||||
const status = value || 'draft';
|
||||
const statusColors: Record<string, 'gray' | 'blue' | 'green' | 'amber' | 'red'> = {
|
||||
@@ -214,7 +209,8 @@ export function createReviewPageConfig(params: {
|
||||
sortable: true,
|
||||
sortField: 'word_count',
|
||||
numeric: true,
|
||||
width: '100px',
|
||||
align: 'center' as const,
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: number) => (
|
||||
<span className="font-mono text-sm text-gray-700 dark:text-gray-300">
|
||||
{value?.toLocaleString() || 0}
|
||||
@@ -227,7 +223,8 @@ export function createReviewPageConfig(params: {
|
||||
sortable: true,
|
||||
sortField: 'created_at',
|
||||
date: true,
|
||||
align: 'right',
|
||||
width: '130px',
|
||||
hasActions: true,
|
||||
render: (value: string) => (
|
||||
<span className="text-gray-700 dark:text-gray-300 whitespace-nowrap">
|
||||
{formatRelativeDate(value)}
|
||||
|
||||
@@ -7,7 +7,7 @@ import React from 'react';
|
||||
import {
|
||||
titleColumn,
|
||||
statusColumn,
|
||||
createdColumn,
|
||||
createdWithActionsColumn,
|
||||
wordCountColumn,
|
||||
sectorColumn,
|
||||
} from '../snippets/columns.snippets';
|
||||
@@ -104,6 +104,7 @@ export const createTasksPageConfig = (
|
||||
label: 'Content Idea Title',
|
||||
sortable: true,
|
||||
sortField: 'title',
|
||||
width: '400px',
|
||||
toggleable: true,
|
||||
toggleContentKey: 'description',
|
||||
toggleContentLabel: 'Idea & Content Outline',
|
||||
@@ -133,8 +134,11 @@ export const createTasksPageConfig = (
|
||||
label: 'Cluster',
|
||||
sortable: false, // Backend doesn't support sorting by cluster_id
|
||||
sortField: 'cluster_id',
|
||||
width: '200px',
|
||||
render: (_value: string, row: Task) => row.cluster_name || '-',
|
||||
render: (_value: string, row: Task) => (
|
||||
<span className="text-gray-800 dark:text-white">
|
||||
{row.cluster_name || '-'}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'taxonomy_name',
|
||||
@@ -159,7 +163,6 @@ export const createTasksPageConfig = (
|
||||
label: 'Type',
|
||||
sortable: false, // Backend doesn't support sorting by content_type
|
||||
sortField: 'content_type',
|
||||
width: '110px',
|
||||
render: (value: string) => {
|
||||
const label = TYPE_LABELS[value] || value || '-';
|
||||
const properCase = label.charAt(0).toUpperCase() + label.slice(1);
|
||||
@@ -175,7 +178,6 @@ export const createTasksPageConfig = (
|
||||
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 =>
|
||||
@@ -211,12 +213,14 @@ export const createTasksPageConfig = (
|
||||
sortable: true,
|
||||
sortField: 'word_count',
|
||||
align: 'center' as const,
|
||||
headingAlign: 'center' as const,
|
||||
render: (value: number | null | undefined) => (value != null ? value.toLocaleString() : '-'),
|
||||
},
|
||||
{
|
||||
...createdColumn,
|
||||
...createdWithActionsColumn,
|
||||
sortable: true,
|
||||
sortField: 'created_at',
|
||||
width: '130px',
|
||||
render: (value: string) => formatRelativeDate(value),
|
||||
},
|
||||
// Optional columns - hidden by default
|
||||
|
||||
@@ -7,14 +7,12 @@ export const titleColumn = {
|
||||
key: 'title',
|
||||
label: 'Title',
|
||||
sortable: true,
|
||||
width: 'auto',
|
||||
};
|
||||
|
||||
export const keywordColumn = {
|
||||
key: 'keyword',
|
||||
label: 'Keyword',
|
||||
sortable: true,
|
||||
width: 'auto',
|
||||
};
|
||||
|
||||
export const statusColumn = {
|
||||
@@ -22,7 +20,6 @@ export const statusColumn = {
|
||||
label: 'Status',
|
||||
sortable: true,
|
||||
badge: true,
|
||||
width: '120px',
|
||||
};
|
||||
|
||||
export const volumeColumn = {
|
||||
@@ -30,7 +27,7 @@ export const volumeColumn = {
|
||||
label: 'Volume',
|
||||
sortable: true,
|
||||
numeric: true,
|
||||
width: '100px',
|
||||
align: 'center' as const,
|
||||
};
|
||||
|
||||
export const difficultyColumn = {
|
||||
@@ -38,7 +35,7 @@ export const difficultyColumn = {
|
||||
label: 'Difficulty',
|
||||
sortable: true,
|
||||
badge: true,
|
||||
width: '120px',
|
||||
align: 'center' as const,
|
||||
};
|
||||
|
||||
export const countryColumn = {
|
||||
@@ -46,14 +43,12 @@ export const countryColumn = {
|
||||
label: 'Country',
|
||||
sortable: true,
|
||||
badge: true,
|
||||
width: '120px',
|
||||
};
|
||||
|
||||
export const clusterColumn = {
|
||||
key: 'cluster',
|
||||
label: 'Cluster',
|
||||
sortable: true,
|
||||
width: '200px',
|
||||
};
|
||||
|
||||
export const createdColumn = {
|
||||
@@ -61,7 +56,16 @@ export const createdColumn = {
|
||||
label: 'Created',
|
||||
sortable: true,
|
||||
date: true,
|
||||
width: '100px',
|
||||
};
|
||||
|
||||
// Combined column with date and actions in one cell
|
||||
export const createdWithActionsColumn = {
|
||||
key: 'created_at',
|
||||
label: 'Created',
|
||||
sortable: true,
|
||||
date: true,
|
||||
align: 'left' as const,
|
||||
hasActions: true, // Special flag to indicate this column has row actions
|
||||
};
|
||||
|
||||
export const updatedColumn = {
|
||||
@@ -69,14 +73,12 @@ export const updatedColumn = {
|
||||
label: 'Modified',
|
||||
sortable: true,
|
||||
date: true,
|
||||
width: '150px',
|
||||
};
|
||||
|
||||
export const actionsColumn = {
|
||||
key: 'actions',
|
||||
label: 'Actions',
|
||||
sortable: false,
|
||||
width: '100px',
|
||||
fixed: true,
|
||||
};
|
||||
|
||||
@@ -85,13 +87,12 @@ export const wordCountColumn = {
|
||||
label: 'Words',
|
||||
sortable: true,
|
||||
numeric: true,
|
||||
width: '120px',
|
||||
align: 'center' as const,
|
||||
};
|
||||
|
||||
export const sectorColumn = {
|
||||
key: 'sector_name',
|
||||
label: 'Sector',
|
||||
sortable: false,
|
||||
width: '150px',
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user