Updated iamge prompt flow adn frotnend backend

This commit is contained in:
IGNY8 VPS (Salman)
2025-11-11 18:10:18 +00:00
parent fa696064e2
commit a1b21f39f6
10 changed files with 611 additions and 196 deletions

View File

@@ -1,17 +1,13 @@
/**
* Images Page Configuration
* Centralized config for Images page table, filters, and actions
* Centralized config for Content Images page table, filters, and actions
* Shows one row per content with featured and in-article images
*/
import React from 'react';
import {
titleColumn,
statusColumn,
createdColumn,
} from '../snippets/columns.snippets';
import Badge from '../../components/ui/badge/Badge';
import { formatRelativeDate } from '../../utils/date';
import { TaskImage } from '../../services/api';
import ContentImageCell, { ContentImageData } from '../../components/common/ContentImageCell';
import { ContentImagesGroup } from '../../services/api';
export interface ColumnConfig {
key: string;
@@ -35,121 +31,112 @@ export interface HeaderMetricConfig {
label: string;
value: number;
accentColor: 'blue' | 'green' | 'amber' | 'purple';
calculate: (data: { images: any[]; totalCount: number }) => number;
calculate: (data: { images: ContentImagesGroup[]; totalCount: number }) => number;
}
export interface ImagesPageConfig {
columns: ColumnConfig[];
filters: FilterConfig[];
headerMetrics: HeaderMetricConfig[];
maxInArticleImages: number; // Maximum number of in-article image columns to show
}
export const createImagesPageConfig = (
handlers: {
searchTerm: string;
setSearchTerm: (value: string) => void;
imageTypeFilter: string;
setImageTypeFilter: (value: string) => void;
statusFilter: string;
setStatusFilter: (value: string) => void;
setCurrentPage: (page: number) => void;
maxInArticleImages?: number; // Optional: max in-article images to display
}
): ImagesPageConfig => {
const maxImages = handlers.maxInArticleImages || 5; // Default to 5 in-article images
// Build columns dynamically based on max in-article images
const columns: ColumnConfig[] = [
{
key: 'content_title',
label: 'Content Title',
sortable: false,
width: '250px',
render: (_value: string, row: ContentImagesGroup) => (
<div>
<a
href={`/writer/content/${row.content_id}`}
className="font-medium text-brand-500 hover:text-brand-600 dark:text-brand-400"
>
{row.content_title}
</a>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">
ID: {row.content_id}
</div>
</div>
),
},
{
key: 'featured_image',
label: 'Featured Image',
sortable: false,
width: '200px',
render: (_value: any, row: ContentImagesGroup) => (
<ContentImageCell image={row.featured_image} />
),
},
];
// Add in-article image columns dynamically
for (let i = 1; i <= maxImages; i++) {
columns.push({
key: `in_article_${i}`,
label: `In-Article ${i}`,
sortable: false,
width: '200px',
render: (_value: any, row: ContentImagesGroup) => {
const image = row.in_article_images.find(img => img.position === i);
return <ContentImageCell image={image || null} />;
},
});
}
// Add overall status column
columns.push({
key: 'overall_status',
label: 'Status',
sortable: false,
width: '120px',
render: (value: string) => {
const statusColors: Record<string, 'success' | 'warning' | 'error' | 'info'> = {
'complete': 'success',
'partial': 'info',
'pending': 'warning',
'failed': 'error',
};
const labels: Record<string, string> = {
'complete': 'Complete',
'partial': 'Partial',
'pending': 'Pending',
'failed': 'Failed',
};
return (
<Badge
color={statusColors[value] || 'warning'}
size="sm"
>
{labels[value] || value}
</Badge>
);
},
});
return {
columns: [
{
key: 'task_title',
label: 'Task',
sortable: false,
width: '250px',
render: (_value: string, row: TaskImage) => (
<span className="font-medium text-gray-800 dark:text-white/90">
{row.task_title || '-'}
</span>
),
},
{
key: 'image_type',
label: 'Image Type',
sortable: false,
width: '150px',
render: (value: string) => (
<Badge color="info" size="sm" variant="light">
{value?.replace('_', ' ') || '-'}
</Badge>
),
},
{
key: 'image_url',
label: 'Image',
sortable: false,
width: '200px',
render: (value: string) => {
if (!value) return <span className="text-gray-400">-</span>;
return (
<a
href={value}
target="_blank"
rel="noopener noreferrer"
className="text-brand-500 hover:text-brand-600 text-sm truncate block max-w-[200px]"
>
View Image
</a>
);
},
},
{
...statusColumn,
sortable: true,
sortField: 'status',
render: (value: string) => {
const statusColors: Record<string, 'success' | 'warning' | 'error'> = {
'pending': 'warning',
'generated': 'success',
'failed': 'error',
};
return (
<Badge
color={statusColors[value] || 'warning'}
size="sm"
>
{value}
</Badge>
);
},
},
{
key: 'position',
label: 'Position',
sortable: false,
width: '100px',
render: (value: number) => value || 0,
},
{
...createdColumn,
sortable: true,
sortField: 'created_at',
render: (value: string) => formatRelativeDate(value),
},
],
columns,
filters: [
{
key: 'search',
label: 'Search',
type: 'text',
placeholder: 'Search by task title...',
},
{
key: 'image_type',
label: 'Image Type',
type: 'select',
options: [
{ value: '', label: 'All Types' },
{ value: 'featured', label: 'Featured Image' },
{ value: 'desktop', label: 'Desktop Image' },
{ value: 'mobile', label: 'Mobile Image' },
{ value: 'in_article', label: 'In-Article Image' },
],
placeholder: 'Search by content title...',
},
{
key: 'status',
@@ -157,38 +144,39 @@ export const createImagesPageConfig = (
type: 'select',
options: [
{ value: '', label: 'All Status' },
{ value: 'complete', label: 'Complete' },
{ value: 'partial', label: 'Partial' },
{ value: 'pending', label: 'Pending' },
{ value: 'generated', label: 'Generated' },
{ value: 'failed', label: 'Failed' },
],
},
],
headerMetrics: [
{
label: 'Total Images',
label: 'Total Content',
value: 0,
accentColor: 'blue' as const,
calculate: (data) => data.totalCount || 0,
},
{
label: 'Generated',
label: 'Complete',
value: 0,
accentColor: 'green' as const,
calculate: (data) => data.images.filter((i: TaskImage) => i.status === 'generated').length,
calculate: (data) => data.images.filter((i: ContentImagesGroup) => i.overall_status === 'complete').length,
},
{
label: 'Partial',
value: 0,
accentColor: 'info' as const,
calculate: (data) => data.images.filter((i: ContentImagesGroup) => i.overall_status === 'partial').length,
},
{
label: 'Pending',
value: 0,
accentColor: 'amber' as const,
calculate: (data) => data.images.filter((i: TaskImage) => i.status === 'pending').length,
},
{
label: 'Failed',
value: 0,
accentColor: 'error' as const,
calculate: (data) => data.images.filter((i: TaskImage) => i.status === 'failed').length,
calculate: (data) => data.images.filter((i: ContentImagesGroup) => i.overall_status === 'pending').length,
},
],
maxInArticleImages: maxImages,
};
};