Revert "12"

This reverts commit 636b7ddca9.
This commit is contained in:
alorig
2025-11-28 13:33:27 +05:00
parent 3fcba76d0b
commit e360c5fede
6 changed files with 512 additions and 36 deletions

View File

@@ -0,0 +1,260 @@
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>
</>
);
};

View File

@@ -0,0 +1,166 @@
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"
/>
)}
</>
);
};

View File

@@ -27,8 +27,10 @@ export interface WordPressPublishProps {
contentId: string;
contentTitle: string;
currentStatus?: 'draft' | 'publishing' | 'published' | 'failed';
imageGenerationStatus?: 'pending' | 'generating' | 'complete' | 'failed';
onStatusChange?: (status: string) => void;
size?: 'small' | 'medium' | 'large';
showOnlyIfImagesReady?: boolean;
}
interface WordPressStatus {
@@ -43,8 +45,10 @@ export const WordPressPublish: React.FC<WordPressPublishProps> = ({
contentId,
contentTitle,
currentStatus = 'draft',
imageGenerationStatus = 'pending',
onStatusChange,
size = 'medium'
size = 'medium',
showOnlyIfImagesReady = false
}) => {
const [wpStatus, setWpStatus] = useState<WordPressStatus | null>(null);
const [loading, setLoading] = useState(false);
@@ -193,7 +197,34 @@ export const WordPressPublish: React.FC<WordPressPublishProps> = ({
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 = () => {
if (size === 'small') {
@@ -302,7 +333,18 @@ export const WordPressPublish: React.FC<WordPressPublishProps> = ({
This will create a new post on your connected WordPress site with all content,
images, categories, and SEO metadata.
</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' && (
<Alert severity="info" sx={{ mt: 2 }}>

View File

@@ -1,2 +1,4 @@
export { WordPressPublish } from './WordPressPublish';
export { BulkWordPressPublish } from './BulkWordPressPublish';
export { ContentActionsMenu } from './ContentActionsMenu';
export type { WordPressPublishProps } from './WordPressPublish';