12
This commit is contained in:
@@ -1,260 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogTitle,
|
|
||||||
DialogContent,
|
|
||||||
DialogActions,
|
|
||||||
Typography,
|
|
||||||
Alert,
|
|
||||||
Box,
|
|
||||||
CircularProgress,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
ListItemText,
|
|
||||||
Divider
|
|
||||||
} from '@mui/material';
|
|
||||||
import {
|
|
||||||
Publish as PublishIcon,
|
|
||||||
CheckCircle as SuccessIcon,
|
|
||||||
Error as ErrorIcon
|
|
||||||
} from '@mui/icons-material';
|
|
||||||
import { api } from '../../services/api';
|
|
||||||
|
|
||||||
interface BulkWordPressPublishProps {
|
|
||||||
contentItems: Array<{
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
imageGenerationStatus: 'pending' | 'generating' | 'complete' | 'failed';
|
|
||||||
wordpressStatus: 'draft' | 'publishing' | 'published' | 'failed';
|
|
||||||
}>;
|
|
||||||
onPublishComplete?: (results: { success: string[], failed: string[] }) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PublishResult {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
status: 'success' | 'failed' | 'pending';
|
|
||||||
message?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BulkWordPressPublish: React.FC<BulkWordPressPublishProps> = ({
|
|
||||||
contentItems,
|
|
||||||
onPublishComplete
|
|
||||||
}) => {
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [publishing, setPublishing] = useState(false);
|
|
||||||
const [results, setResults] = useState<PublishResult[]>([]);
|
|
||||||
|
|
||||||
// Filter items that are ready to publish
|
|
||||||
const readyToPublish = contentItems.filter(item =>
|
|
||||||
item.imageGenerationStatus === 'complete' &&
|
|
||||||
item.wordpressStatus !== 'published' &&
|
|
||||||
item.wordpressStatus !== 'publishing'
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleBulkPublish = async () => {
|
|
||||||
if (readyToPublish.length === 0) return;
|
|
||||||
|
|
||||||
setPublishing(true);
|
|
||||||
setResults([]);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await api.post('/api/wordpress/bulk-publish/', {
|
|
||||||
content_ids: readyToPublish.map(item => item.id)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.success) {
|
|
||||||
const publishResults: PublishResult[] = response.data.data.results.map((result: any) => ({
|
|
||||||
id: result.content_id,
|
|
||||||
title: readyToPublish.find(item => item.id === result.content_id)?.title || 'Unknown',
|
|
||||||
status: result.success ? 'success' : 'failed',
|
|
||||||
message: result.message
|
|
||||||
}));
|
|
||||||
|
|
||||||
setResults(publishResults);
|
|
||||||
|
|
||||||
// Notify parent component
|
|
||||||
if (onPublishComplete) {
|
|
||||||
const success = publishResults.filter(r => r.status === 'success').map(r => r.id);
|
|
||||||
const failed = publishResults.filter(r => r.status === 'failed').map(r => r.id);
|
|
||||||
onPublishComplete({ success, failed });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Handle API error
|
|
||||||
const failedResults: PublishResult[] = readyToPublish.map(item => ({
|
|
||||||
id: item.id,
|
|
||||||
title: item.title,
|
|
||||||
status: 'failed',
|
|
||||||
message: response.data.message || 'Failed to publish'
|
|
||||||
}));
|
|
||||||
setResults(failedResults);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Bulk publish error:', error);
|
|
||||||
const failedResults: PublishResult[] = readyToPublish.map(item => ({
|
|
||||||
id: item.id,
|
|
||||||
title: item.title,
|
|
||||||
status: 'failed',
|
|
||||||
message: 'Network error or server unavailable'
|
|
||||||
}));
|
|
||||||
setResults(failedResults);
|
|
||||||
} finally {
|
|
||||||
setPublishing(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
if (!publishing) {
|
|
||||||
setOpen(false);
|
|
||||||
setResults([]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const successCount = results.filter(r => r.status === 'success').length;
|
|
||||||
const failedCount = results.filter(r => r.status === 'failed').length;
|
|
||||||
|
|
||||||
if (readyToPublish.length === 0) {
|
|
||||||
return null; // Don't show button if nothing to publish
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
startIcon={<PublishIcon />}
|
|
||||||
onClick={() => setOpen(true)}
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
Publish Ready ({readyToPublish.length})
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Dialog
|
|
||||||
open={open}
|
|
||||||
onClose={handleClose}
|
|
||||||
maxWidth="md"
|
|
||||||
fullWidth
|
|
||||||
disableEscapeKeyDown={publishing}
|
|
||||||
>
|
|
||||||
<DialogTitle>
|
|
||||||
Bulk Publish to WordPress
|
|
||||||
</DialogTitle>
|
|
||||||
|
|
||||||
<DialogContent>
|
|
||||||
{!publishing && results.length === 0 && (
|
|
||||||
<>
|
|
||||||
<Typography variant="body1" gutterBottom>
|
|
||||||
Ready to publish <strong>{readyToPublish.length}</strong> content items to WordPress:
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Alert severity="info" sx={{ mt: 2, mb: 2 }}>
|
|
||||||
Only content with generated images and not yet published will be included.
|
|
||||||
</Alert>
|
|
||||||
|
|
||||||
<Box sx={{ maxHeight: 300, overflow: 'auto', border: 1, borderColor: 'divider', borderRadius: 1 }}>
|
|
||||||
<List dense>
|
|
||||||
{readyToPublish.map((item, index) => (
|
|
||||||
<div key={item.id}>
|
|
||||||
<ListItem>
|
|
||||||
<ListItemText
|
|
||||||
primary={item.title}
|
|
||||||
secondary={`ID: ${item.id}`}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
{index < readyToPublish.length - 1 && <Divider />}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{publishing && (
|
|
||||||
<Box display="flex" alignItems="center" gap={2} py={4}>
|
|
||||||
<CircularProgress />
|
|
||||||
<Typography>
|
|
||||||
Publishing {readyToPublish.length} items to WordPress...
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!publishing && results.length > 0 && (
|
|
||||||
<>
|
|
||||||
<Box sx={{ mb: 2 }}>
|
|
||||||
{successCount > 0 && (
|
|
||||||
<Alert severity="success" sx={{ mb: 1 }}>
|
|
||||||
✓ Successfully published {successCount} items
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{failedCount > 0 && (
|
|
||||||
<Alert severity="error">
|
|
||||||
✗ Failed to publish {failedCount} items
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Typography variant="h6" gutterBottom>
|
|
||||||
Results:
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Box sx={{ maxHeight: 400, overflow: 'auto', border: 1, borderColor: 'divider', borderRadius: 1 }}>
|
|
||||||
<List dense>
|
|
||||||
{results.map((result, index) => (
|
|
||||||
<div key={result.id}>
|
|
||||||
<ListItem>
|
|
||||||
<Box display="flex" alignItems="center" width="100%">
|
|
||||||
{result.status === 'success' ? (
|
|
||||||
<SuccessIcon color="success" sx={{ mr: 1 }} />
|
|
||||||
) : (
|
|
||||||
<ErrorIcon color="error" sx={{ mr: 1 }} />
|
|
||||||
)}
|
|
||||||
<ListItemText
|
|
||||||
primary={result.title}
|
|
||||||
secondary={result.message || (result.status === 'success' ? 'Published successfully' : 'Publishing failed')}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</ListItem>
|
|
||||||
{index < results.length - 1 && <Divider />}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DialogContent>
|
|
||||||
|
|
||||||
<DialogActions>
|
|
||||||
{!publishing && results.length === 0 && (
|
|
||||||
<>
|
|
||||||
<Button onClick={handleClose}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={handleBulkPublish}
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
startIcon={<PublishIcon />}
|
|
||||||
>
|
|
||||||
Publish All ({readyToPublish.length})
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{publishing && (
|
|
||||||
<Button disabled>
|
|
||||||
Publishing...
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!publishing && results.length > 0 && (
|
|
||||||
<Button onClick={handleClose} variant="contained">
|
|
||||||
Close
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import {
|
|
||||||
IconButton,
|
|
||||||
Menu,
|
|
||||||
MenuItem,
|
|
||||||
ListItemIcon,
|
|
||||||
ListItemText,
|
|
||||||
Divider
|
|
||||||
} from '@mui/material';
|
|
||||||
import {
|
|
||||||
MoreVert as MoreVertIcon,
|
|
||||||
Publish as PublishIcon,
|
|
||||||
Edit as EditIcon,
|
|
||||||
Image as ImageIcon,
|
|
||||||
GetApp as ExportIcon,
|
|
||||||
Delete as DeleteIcon
|
|
||||||
} from '@mui/icons-material';
|
|
||||||
import { WordPressPublish } from './WordPressPublish';
|
|
||||||
|
|
||||||
interface ContentActionsMenuProps {
|
|
||||||
contentId: string;
|
|
||||||
contentTitle: string;
|
|
||||||
imageGenerationStatus: 'pending' | 'generating' | 'complete' | 'failed';
|
|
||||||
wordpressStatus: 'draft' | 'publishing' | 'published' | 'failed';
|
|
||||||
onEdit?: () => void;
|
|
||||||
onGenerateImage?: () => void;
|
|
||||||
onExport?: () => void;
|
|
||||||
onDelete?: () => void;
|
|
||||||
onWordPressStatusChange?: (status: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ContentActionsMenu: React.FC<ContentActionsMenuProps> = ({
|
|
||||||
contentId,
|
|
||||||
contentTitle,
|
|
||||||
imageGenerationStatus,
|
|
||||||
wordpressStatus,
|
|
||||||
onEdit,
|
|
||||||
onGenerateImage,
|
|
||||||
onExport,
|
|
||||||
onDelete,
|
|
||||||
onWordPressStatusChange
|
|
||||||
}) => {
|
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
|
||||||
const [showWordPressDialog, setShowWordPressDialog] = useState(false);
|
|
||||||
const open = Boolean(anchorEl);
|
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
||||||
setAnchorEl(event.currentTarget);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setAnchorEl(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePublishClick = () => {
|
|
||||||
setShowWordPressDialog(true);
|
|
||||||
handleClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMenuAction = (action: () => void) => {
|
|
||||||
action();
|
|
||||||
handleClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check if WordPress publishing is available
|
|
||||||
const canPublishToWordPress = imageGenerationStatus === 'complete' &&
|
|
||||||
wordpressStatus !== 'published' &&
|
|
||||||
wordpressStatus !== 'publishing';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<IconButton
|
|
||||||
aria-label="more actions"
|
|
||||||
id="content-actions-button"
|
|
||||||
aria-controls={open ? 'content-actions-menu' : undefined}
|
|
||||||
aria-expanded={open ? 'true' : undefined}
|
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={handleClick}
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
<MoreVertIcon />
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
<Menu
|
|
||||||
id="content-actions-menu"
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
open={open}
|
|
||||||
onClose={handleClose}
|
|
||||||
MenuListProps={{
|
|
||||||
'aria-labelledby': 'content-actions-button',
|
|
||||||
}}
|
|
||||||
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
|
||||||
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
|
||||||
>
|
|
||||||
{/* WordPress Publishing - Only show if images are ready */}
|
|
||||||
{canPublishToWordPress && (
|
|
||||||
<>
|
|
||||||
<MenuItem onClick={handlePublishClick}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<PublishIcon fontSize="small" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>Publish to WordPress</ListItemText>
|
|
||||||
</MenuItem>
|
|
||||||
<Divider />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Edit Action */}
|
|
||||||
{onEdit && (
|
|
||||||
<MenuItem onClick={() => handleMenuAction(onEdit)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<EditIcon fontSize="small" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>Edit</ListItemText>
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Generate Image Action */}
|
|
||||||
{onGenerateImage && (
|
|
||||||
<MenuItem onClick={() => handleMenuAction(onGenerateImage)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ImageIcon fontSize="small" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>Generate Image Prompts</ListItemText>
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Export Action */}
|
|
||||||
{onExport && (
|
|
||||||
<MenuItem onClick={() => handleMenuAction(onExport)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ExportIcon fontSize="small" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>Export</ListItemText>
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Delete Action */}
|
|
||||||
{onDelete && (
|
|
||||||
<>
|
|
||||||
<Divider />
|
|
||||||
<MenuItem onClick={() => handleMenuAction(onDelete)} sx={{ color: 'error.main' }}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<DeleteIcon fontSize="small" color="error" />
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>Delete</ListItemText>
|
|
||||||
</MenuItem>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Menu>
|
|
||||||
|
|
||||||
{/* WordPress Publish Dialog */}
|
|
||||||
{showWordPressDialog && (
|
|
||||||
<WordPressPublish
|
|
||||||
contentId={contentId}
|
|
||||||
contentTitle={contentTitle}
|
|
||||||
currentStatus={wordpressStatus}
|
|
||||||
imageGenerationStatus={imageGenerationStatus}
|
|
||||||
onStatusChange={onWordPressStatusChange}
|
|
||||||
showOnlyIfImagesReady={true}
|
|
||||||
size="medium"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -27,10 +27,8 @@ export interface WordPressPublishProps {
|
|||||||
contentId: string;
|
contentId: string;
|
||||||
contentTitle: string;
|
contentTitle: string;
|
||||||
currentStatus?: 'draft' | 'publishing' | 'published' | 'failed';
|
currentStatus?: 'draft' | 'publishing' | 'published' | 'failed';
|
||||||
imageGenerationStatus?: 'pending' | 'generating' | 'complete' | 'failed';
|
|
||||||
onStatusChange?: (status: string) => void;
|
onStatusChange?: (status: string) => void;
|
||||||
size?: 'small' | 'medium' | 'large';
|
size?: 'small' | 'medium' | 'large';
|
||||||
showOnlyIfImagesReady?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WordPressStatus {
|
interface WordPressStatus {
|
||||||
@@ -45,10 +43,8 @@ export const WordPressPublish: React.FC<WordPressPublishProps> = ({
|
|||||||
contentId,
|
contentId,
|
||||||
contentTitle,
|
contentTitle,
|
||||||
currentStatus = 'draft',
|
currentStatus = 'draft',
|
||||||
imageGenerationStatus = 'pending',
|
|
||||||
onStatusChange,
|
onStatusChange,
|
||||||
size = 'medium',
|
size = 'medium'
|
||||||
showOnlyIfImagesReady = false
|
|
||||||
}) => {
|
}) => {
|
||||||
const [wpStatus, setWpStatus] = useState<WordPressStatus | null>(null);
|
const [wpStatus, setWpStatus] = useState<WordPressStatus | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -197,34 +193,7 @@ export const WordPressPublish: React.FC<WordPressPublishProps> = ({
|
|||||||
|
|
||||||
const statusInfo = getStatusInfo();
|
const statusInfo = getStatusInfo();
|
||||||
|
|
||||||
// Don't show publish button if images aren't ready and showOnlyIfImagesReady is true
|
|
||||||
const shouldShowPublishButton = !showOnlyIfImagesReady || imageGenerationStatus === 'complete';
|
|
||||||
|
|
||||||
if (!shouldShowPublishButton) {
|
|
||||||
return (
|
|
||||||
<Tooltip title={`Images must be generated before publishing to WordPress`}>
|
|
||||||
<Box display="flex" alignItems="center" gap={1}>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
disabled
|
|
||||||
size={size}
|
|
||||||
startIcon={<PendingIcon />}
|
|
||||||
>
|
|
||||||
Awaiting Images
|
|
||||||
</Button>
|
|
||||||
{size !== 'small' && (
|
|
||||||
<Chip
|
|
||||||
icon={<PendingIcon />}
|
|
||||||
label="Images Pending"
|
|
||||||
color="warning"
|
|
||||||
size="small"
|
|
||||||
variant="outlined"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderButton = () => {
|
const renderButton = () => {
|
||||||
if (size === 'small') {
|
if (size === 'small') {
|
||||||
@@ -333,18 +302,7 @@ export const WordPressPublish: React.FC<WordPressPublishProps> = ({
|
|||||||
This will create a new post on your connected WordPress site with all content,
|
This will create a new post on your connected WordPress site with all content,
|
||||||
images, categories, and SEO metadata.
|
images, categories, and SEO metadata.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{imageGenerationStatus === 'complete' && (
|
|
||||||
<Alert severity="success" sx={{ mt: 2 }}>
|
|
||||||
✓ Images are generated and ready for publishing
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{imageGenerationStatus !== 'complete' && showOnlyIfImagesReady && (
|
|
||||||
<Alert severity="warning" sx={{ mt: 2 }}>
|
|
||||||
Images are still being generated. Please wait before publishing.
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{wpStatus?.wordpress_sync_status === 'success' && (
|
{wpStatus?.wordpress_sync_status === 'success' && (
|
||||||
<Alert severity="info" sx={{ mt: 2 }}>
|
<Alert severity="info" sx={{ mt: 2 }}>
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
export { WordPressPublish } from './WordPressPublish';
|
export { WordPressPublish } from './WordPressPublish';
|
||||||
export { BulkWordPressPublish } from './BulkWordPressPublish';
|
|
||||||
export { ContentActionsMenu } from './ContentActionsMenu';
|
|
||||||
export type { WordPressPublishProps } from './WordPressPublish';
|
export type { WordPressPublishProps } from './WordPressPublish';
|
||||||
@@ -327,10 +327,8 @@ const tableActionsConfigs: Record<string, TableActionsConfig> = {
|
|||||||
icon: <ArrowRightIcon className="w-5 h-5" />,
|
icon: <ArrowRightIcon className="w-5 h-5" />,
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
shouldShow: (row: any) => {
|
shouldShow: (row: any) => {
|
||||||
// Only show if images are generated and not already published/publishing
|
// Only show if images are generated (complete) - WordPress status is tracked separately
|
||||||
return row.status === 'complete' &&
|
return row.overall_status === 'complete';
|
||||||
(!row.wordpress_status ||
|
|
||||||
(row.wordpress_status !== 'published' && row.wordpress_status !== 'publishing'));
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import {
|
|||||||
generateImages,
|
generateImages,
|
||||||
bulkUpdateImagesStatus,
|
bulkUpdateImagesStatus,
|
||||||
ContentImage,
|
ContentImage,
|
||||||
api,
|
fetchAPI,
|
||||||
|
publishContent,
|
||||||
} from '../../services/api';
|
} from '../../services/api';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import { FileIcon, DownloadIcon, BoltIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
|
import { FileIcon, DownloadIcon, BoltIcon, TaskIcon, ImageIcon, CheckCircleIcon } from '../../icons';
|
||||||
@@ -208,12 +209,10 @@ export default function Images() {
|
|||||||
// Bulk action handler
|
// Bulk action handler
|
||||||
const handleBulkAction = useCallback(async (action: string, ids: string[]) => {
|
const handleBulkAction = useCallback(async (action: string, ids: string[]) => {
|
||||||
if (action === 'bulk_publish_wordpress') {
|
if (action === 'bulk_publish_wordpress') {
|
||||||
// Filter to only publish items that have images generated and are not already published
|
// Filter to only publish items that have images generated
|
||||||
const readyItems = images
|
const readyItems = images
|
||||||
.filter(item => ids.includes(item.content_id.toString()))
|
.filter(item => ids.includes(item.content_id.toString()))
|
||||||
.filter(item => item.status === 'complete' &&
|
.filter(item => item.overall_status === 'complete');
|
||||||
(!item.wordpress_status ||
|
|
||||||
(item.wordpress_status !== 'published' && item.wordpress_status !== 'publishing')));
|
|
||||||
|
|
||||||
if (readyItems.length === 0) {
|
if (readyItems.length === 0) {
|
||||||
toast.warning('No items are ready for WordPress publishing. Items must have generated images and not already be published.');
|
toast.warning('No items are ready for WordPress publishing. Items must have generated images and not already be published.');
|
||||||
@@ -221,27 +220,30 @@ export default function Images() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await api.post('/api/wordpress/bulk-publish/', {
|
let successCount = 0;
|
||||||
content_ids: readyItems.map(item => item.content_id.toString())
|
let failedCount = 0;
|
||||||
});
|
const errors: string[] = [];
|
||||||
|
|
||||||
if (response.data.success) {
|
// Process each item individually using the existing publishContent function
|
||||||
const results = response.data.data.results;
|
for (const item of readyItems) {
|
||||||
const successCount = results.filter((r: any) => r.success).length;
|
try {
|
||||||
const failedCount = results.filter((r: any) => !r.success).length;
|
await publishContent(item.content_id);
|
||||||
|
successCount++;
|
||||||
if (successCount > 0) {
|
} catch (error: any) {
|
||||||
toast.success(`Successfully published ${successCount} item(s) to WordPress`);
|
failedCount++;
|
||||||
|
errors.push(`${item.content_title}: ${error.message}`);
|
||||||
}
|
}
|
||||||
if (failedCount > 0) {
|
|
||||||
toast.warning(`${failedCount} item(s) failed to publish`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reload images to reflect the updated WordPress status
|
|
||||||
loadImages();
|
|
||||||
} else {
|
|
||||||
toast.error(`Bulk publish failed: ${response.data.message}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (successCount > 0) {
|
||||||
|
toast.success(`Successfully published ${successCount} item(s) to WordPress`);
|
||||||
|
}
|
||||||
|
if (failedCount > 0) {
|
||||||
|
toast.warning(`${failedCount} item(s) failed to publish`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload images to reflect the updated WordPress status
|
||||||
|
loadImages();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Bulk WordPress publish error:', error);
|
console.error('Bulk WordPress publish error:', error);
|
||||||
toast.error(`Failed to bulk publish to WordPress: ${error.message || 'Network error'}`);
|
toast.error(`Failed to bulk publish to WordPress: ${error.message || 'Network error'}`);
|
||||||
@@ -258,19 +260,13 @@ export default function Images() {
|
|||||||
setStatusUpdateRecordName(row.content_title || `Content #${row.content_id}`);
|
setStatusUpdateRecordName(row.content_title || `Content #${row.content_id}`);
|
||||||
setIsStatusModalOpen(true);
|
setIsStatusModalOpen(true);
|
||||||
} else if (action === 'publish_wordpress') {
|
} else if (action === 'publish_wordpress') {
|
||||||
// Handle WordPress publishing for individual item
|
// Handle WordPress publishing for individual item using existing publishContent function
|
||||||
try {
|
try {
|
||||||
const response = await api.post('/api/wordpress/publish/', {
|
// Use the existing publishContent function from the API
|
||||||
content_id: row.content_id.toString()
|
const result = await publishContent(row.content_id);
|
||||||
});
|
toast.success(`Successfully published "${row.content_title}" to WordPress! View at: ${result.external_url}`);
|
||||||
|
// Reload images to reflect the updated WordPress status
|
||||||
if (response.data.success) {
|
loadImages();
|
||||||
toast.success(`Successfully published "${row.content_title}" to WordPress`);
|
|
||||||
// Reload images to reflect the updated WordPress status
|
|
||||||
loadImages();
|
|
||||||
} else {
|
|
||||||
toast.error(`Failed to publish: ${response.data.message}`);
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('WordPress publish error:', error);
|
console.error('WordPress publish error:', error);
|
||||||
toast.error(`Failed to publish to WordPress: ${error.message || 'Network error'}`);
|
toast.error(`Failed to publish to WordPress: ${error.message || 'Network error'}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user