= ({
diff --git a/frontend/src/components/ui/table/index.tsx b/frontend/src/components/ui/table/index.tsx
index 18dfd599..fa860e31 100644
--- a/frontend/src/components/ui/table/index.tsx
+++ b/frontend/src/components/ui/table/index.tsx
@@ -35,11 +35,12 @@ interface TableCellProps {
children: ReactNode; // Cell content
isHeader?: boolean; // If true, renders as | , otherwise |
className?: string; // Optional className for styling
+ style?: React.CSSProperties; // Optional inline styles for width, fontSize, etc.
}
// Table Component
const Table: React.FC = ({ children, className }) => {
- return ;
+ return ;
};
// TableHeader Component
@@ -62,9 +63,10 @@ const TableCell: React.FC = ({
children,
isHeader = false,
className,
+ style,
}) => {
const CellTag = isHeader ? "th" : "td";
- return {children};
+ return {children};
};
export { Table, TableHeader, TableBody, TableRow, TableCell };
diff --git a/frontend/src/config/pages/approved.config.tsx b/frontend/src/config/pages/approved.config.tsx
index 304a3534..eae40241 100644
--- a/frontend/src/config/pages/approved.config.tsx
+++ b/frontend/src/config/pages/approved.config.tsx
@@ -62,17 +62,18 @@ export function createApprovedPageConfig(params: {
label: 'Content Idea Title',
sortable: true,
sortField: 'title',
+ width: '400px',
render: (value: string, row: Content) => (
{params.onRowClick ? (
) : (
-
+
{value || `Content #${row.id}`}
)}
@@ -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 = {
@@ -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) => (
{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) => (
{formatRelativeDate(value)}
diff --git a/frontend/src/config/pages/clusters.config.tsx b/frontend/src/config/pages/clusters.config.tsx
index 45b9fe22..4ac0e028 100644
--- a/frontend/src/config/pages/clusters.config.tsx
+++ b/frontend/src/config/pages/clusters.config.tsx
@@ -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) => (
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 (
+
+ );
+ }
+ return -;
+ },
+ },
+ {
+ ...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 (
-
- );
- }
- return -;
- },
- },
],
filters: [
{
diff --git a/frontend/src/config/pages/content.config.tsx b/frontend/src/config/pages/content.config.tsx
index a0d7be97..5c64f3a8 100644
--- a/frontend/src/config/pages/content.config.tsx
+++ b/frontend/src/config/pages/content.config.tsx
@@ -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) => (
{handlers.onRowClick ? (
) : (
-
+
{row.title || `Content #${row.id}`}
)}
@@ -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 -;
+ return -;
}
return (
-
- {clusterName}
-
+
+ {clusterName}
+
);
},
},
@@ -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 (
-
+
{formatRelativeDate(value)}
diff --git a/frontend/src/config/pages/ideas.config.tsx b/frontend/src/config/pages/ideas.config.tsx
index e5f1ea10..9b9f5fa3 100644
--- a/frontend/src/config/pages/ideas.config.tsx
+++ b/frontend/src/config/pages/ideas.config.tsx
@@ -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) => (
-
+
{value || '-'}
),
@@ -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
diff --git a/frontend/src/config/pages/images.config.tsx b/frontend/src/config/pages/images.config.tsx
index b3658eda..d8b25dcc 100644
--- a/frontend/src/config/pages/images.config.tsx
+++ b/frontend/src/config/pages/images.config.tsx
@@ -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 = {
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 = {
'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 ? (
-
- ) : (
- -
- )}
- >
- );
- },
- });
-
return {
columns,
filters: [
diff --git a/frontend/src/config/pages/keywords.config.tsx b/frontend/src/config/pages/keywords.config.tsx
index 0aa8f250..777e1e0c 100644
--- a/frontend/src/config/pages/keywords.config.tsx
+++ b/frontend/src/config/pages/keywords.config.tsx
@@ -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) => (
+
+ {value || '-'}
+
+ ),
},
// Sector column - only show when viewing all sectors
...(showSectorColumn ? [{
...sectorColumn,
render: (value: string, row: Keyword) => (
- {row.sector_name || '-'}
+ {row.sector_name || '-'}
),
}] : []),
@@ -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 ? (
-
- {row.cluster_name}
-
+
+ {row.cluster_name}
+
) : -,
},
{
@@ -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' ? (
- {difficultyNum}
+ {difficultyNum}
) : (
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 = {
- 'US': 'United States',
- 'CA': 'Canada',
- 'GB': 'United Kingdom',
- 'AE': 'United Arab Emirates',
- 'AU': 'Australia',
- 'IN': 'India',
- 'PK': 'Pakistan',
- };
- const displayName = countryNames[value] || value || '-';
- return (
-
- {value || '-'}
-
- );
- },
+ headingAlign: 'center' as const,
+ render: (value: string) => (
+
+ {value || '-'}
+
+ ),
},
{
...statusColumn,
@@ -242,16 +240,17 @@ export const createKeywordsPageConfig = (
size="xs"
variant="soft"
>
- {properCase}
+ {properCase}
);
},
},
{
- ...createdColumn,
+ ...createdWithActionsColumn,
label: 'Added',
sortable: true,
sortField: 'created_at',
+ width: '130px',
render: (value: string) => formatRelativeDate(value),
},
],
diff --git a/frontend/src/config/pages/review.config.tsx b/frontend/src/config/pages/review.config.tsx
index fb47dd62..53c80c5e 100644
--- a/frontend/src/config/pages/review.config.tsx
+++ b/frontend/src/config/pages/review.config.tsx
@@ -62,17 +62,18 @@ export function createReviewPageConfig(params: {
label: 'Content Idea Title',
sortable: true,
sortField: 'title',
+ width: '400px',
render: (value: string, row: Content) => (
{params.onRowClick ? (
) : (
-
+
{value || `Content #${row.id}`}
)}
@@ -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 -;
}
return (
-
- {clusterName}
-
+
+ {clusterName}
+
);
},
},
@@ -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 = {
@@ -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) => (
{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) => (
{formatRelativeDate(value)}
diff --git a/frontend/src/config/pages/tasks.config.tsx b/frontend/src/config/pages/tasks.config.tsx
index 42e5a2b1..dba0722c 100644
--- a/frontend/src/config/pages/tasks.config.tsx
+++ b/frontend/src/config/pages/tasks.config.tsx
@@ -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) => (
+
+ {row.cluster_name || '-'}
+
+ ),
},
{
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
diff --git a/frontend/src/config/snippets/columns.snippets.ts b/frontend/src/config/snippets/columns.snippets.ts
index c66cd065..e5a59131 100644
--- a/frontend/src/config/snippets/columns.snippets.ts
+++ b/frontend/src/config/snippets/columns.snippets.ts
@@ -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',
};
diff --git a/frontend/src/pages/Planner/Keywords.tsx b/frontend/src/pages/Planner/Keywords.tsx
index 23ae813f..084b9ca0 100644
--- a/frontend/src/pages/Planner/Keywords.tsx
+++ b/frontend/src/pages/Planner/Keywords.tsx
@@ -663,6 +663,7 @@ export default function Keywords() {
data={keywords}
loading={loading}
showContent={showContent}
+ checkboxColumnWidth="40px"
filters={pageConfig.filters}
filterValues={{
search: searchTerm,
diff --git a/frontend/src/pages/Setup/SetupWizard.tsx b/frontend/src/pages/Setup/SetupWizard.tsx
index c847b251..aa63f239 100644
--- a/frontend/src/pages/Setup/SetupWizard.tsx
+++ b/frontend/src/pages/Setup/SetupWizard.tsx
@@ -20,14 +20,14 @@ export default function SetupWizard() {
};
return (
-
+
, color: 'blue' }}
description="Complete guided setup for your site"
/>
-
+
ReactNode;
toggleable?: boolean; // If true, this column will have a toggle button for expanding content
toggleContentKey?: string; // Key of the field containing content to display when toggled
toggleContentLabel?: string; // Label for the expanded content (e.g., "Content Outline")
defaultVisible?: boolean; // Whether column is visible by default (default: true)
+ hasActions?: boolean; // If true, this column will include row actions (3-dot menu)
}
interface FilterConfig {
@@ -179,6 +181,8 @@ interface TablePageTemplateProps {
};
// Custom row highlight function (returns bg class based on row data)
getRowClassName?: (row: any) => string;
+ // Custom checkbox column width (default: w-12 = 48px)
+ checkboxColumnWidth?: string;
}
export default function TablePageTemplate({
@@ -217,6 +221,7 @@ export default function TablePageTemplate({
bulkActions: customBulkActions,
primaryAction,
getRowClassName,
+ checkboxColumnWidth = '48px',
}: TablePageTemplateProps) {
const location = useLocation();
const [isBulkActionsDropdownOpen, setIsBulkActionsDropdownOpen] = useState(false);
@@ -799,7 +804,8 @@ export default function TablePageTemplate({
{selection && (
{showContent && (
{
const isLastColumn = colIndex === visibleColumnsList.length - 1;
+ const hasActionsInColumn = column.hasActions && rowActions.length > 0;
const displayName = column.label || formatColumnKey(column.key);
+ const headerAlign = column.headingAlign || column.align || 'start';
+ const justifyClass = headerAlign === 'center' ? 'justify-center' : headerAlign === 'right' ? 'justify-end' : 'justify-start';
return (
0 ? 'pr-16' : ''}`}
+ className={`font-medium text-gray-500 text-${hasActionsInColumn ? 'start' : headerAlign} dark:text-gray-400 ${column.sortable ? 'cursor-pointer hover:text-gray-700 dark:hover:text-gray-300' : ''} ${hasActionsInColumn ? 'pr-16' : ''}`}
+ style={column.width ? { width: column.width } : undefined}
>
-
-
+
+
{column.sortable ? (
handleSort(column)} className="flex items-center">
{displayName}
@@ -915,7 +925,10 @@ export default function TablePageTemplate({
className={`igny8-data-row ${isRowAdded ? 'bg-brand-50 dark:bg-brand-500/10' : ''} ${customRowClass}`}
>
{selection && (
-
+
handleSelectRow(row.id?.toString() || '', checked)}
@@ -925,11 +938,11 @@ export default function TablePageTemplate({
)}
{visibleColumnsList.map((column, colIndex) => {
- const isLastColumn = colIndex === visibleColumnsList.length - 1;
const rowId = row.id || index;
+ const hasActionsInColumn = column.hasActions && rowActions.length > 0;
// Get or create ref for this row's actions button
- if (isLastColumn && rowActions.length > 0) {
+ if (hasActionsInColumn) {
if (!rowActionButtonRefs.current.has(rowId)) {
const ref = React.createRef();
rowActionButtonRefs.current.set(rowId, ref);
@@ -939,7 +952,7 @@ export default function TablePageTemplate({
return (
0 ? 'relative pr-16' : ''}`}
+ className={`text-${column.align || 'start'} text-gray-800 dark:text-white/90 ${hasActionsInColumn ? 'relative pr-16' : ''}`}
>
@@ -962,8 +975,8 @@ export default function TablePageTemplate({
)}
- {/* Actions button - absolutely positioned in last column */}
- {isLastColumn && rowActions.length > 0 && (() => {
+ {/* Actions button - absolutely positioned in column with hasActions flag */}
+ {hasActionsInColumn && (() => {
// Check if row is already added - use same logic as handleBulkAddSelected
const isRowAdded = !!(row as any).isAdded;
@@ -1172,7 +1185,7 @@ export default function TablePageTemplate({
{/* Pagination - Compact icon-based pagination */}
{pagination && (
-
+
Showing {data.length} of {pagination.totalCount} {selectionLabel || 'items'}
|