igny8-wp
This commit is contained in:
338
frontend/src/components/WordPressPublish/WordPressPublish.tsx
Normal file
338
frontend/src/components/WordPressPublish/WordPressPublish.tsx
Normal file
@@ -0,0 +1,338 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Alert,
|
||||
Chip,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
CircularProgress,
|
||||
Box,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Publish as PublishIcon,
|
||||
Refresh as RefreshIcon,
|
||||
CheckCircle as SuccessIcon,
|
||||
Error as ErrorIcon,
|
||||
Schedule as PendingIcon,
|
||||
Sync as SyncingIcon
|
||||
} from '@mui/icons-material';
|
||||
import { api } from '../../services/api';
|
||||
|
||||
interface WordPressPublishProps {
|
||||
contentId: string;
|
||||
contentTitle: string;
|
||||
currentStatus?: 'draft' | 'publishing' | 'published' | 'failed';
|
||||
onStatusChange?: (status: string) => void;
|
||||
size?: 'small' | 'medium' | 'large';
|
||||
}
|
||||
|
||||
interface WordPressStatus {
|
||||
wordpress_sync_status: 'pending' | 'syncing' | 'success' | 'failed';
|
||||
wordpress_post_id?: number;
|
||||
wordpress_post_url?: string;
|
||||
wordpress_sync_attempts: number;
|
||||
last_wordpress_sync?: string;
|
||||
}
|
||||
|
||||
export const WordPressPublish: React.FC<WordPressPublishProps> = ({
|
||||
contentId,
|
||||
contentTitle,
|
||||
currentStatus = 'draft',
|
||||
onStatusChange,
|
||||
size = 'medium'
|
||||
}) => {
|
||||
const [wpStatus, setWpStatus] = useState<WordPressStatus | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [publishDialogOpen, setPublishDialogOpen] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Fetch current WordPress status
|
||||
const fetchWordPressStatus = async () => {
|
||||
try {
|
||||
const response = await api.get(`/api/v1/content/${contentId}/wordpress-status/`);
|
||||
if (response.data.success) {
|
||||
setWpStatus(response.data.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch WordPress status:', error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchWordPressStatus();
|
||||
}, [contentId]);
|
||||
|
||||
// Handle publish to WordPress
|
||||
const handlePublishToWordPress = async (force: boolean = false) => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await api.post(`/api/v1/content/${contentId}/publish-to-wordpress/`, {
|
||||
force: force
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
setWpStatus(prev => prev ? { ...prev, wordpress_sync_status: 'pending' } : null);
|
||||
onStatusChange?.('publishing');
|
||||
|
||||
// Poll for status updates
|
||||
pollForStatusUpdate();
|
||||
} else {
|
||||
setError(response.data.message || 'Failed to publish to WordPress');
|
||||
}
|
||||
} catch (error: any) {
|
||||
setError(error.response?.data?.message || 'Error publishing to WordPress');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setPublishDialogOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Poll for status updates after publishing
|
||||
const pollForStatusUpdate = () => {
|
||||
const pollInterval = setInterval(async () => {
|
||||
try {
|
||||
const response = await api.get(`/api/v1/content/${contentId}/wordpress-status/`);
|
||||
if (response.data.success) {
|
||||
const status = response.data.data;
|
||||
setWpStatus(status);
|
||||
|
||||
// Stop polling if sync is complete (success or failed)
|
||||
if (status.wordpress_sync_status === 'success' || status.wordpress_sync_status === 'failed') {
|
||||
clearInterval(pollInterval);
|
||||
onStatusChange?.(status.wordpress_sync_status === 'success' ? 'published' : 'failed');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
clearInterval(pollInterval);
|
||||
}
|
||||
}, 3000); // Poll every 3 seconds
|
||||
|
||||
// Stop polling after 2 minutes
|
||||
setTimeout(() => {
|
||||
clearInterval(pollInterval);
|
||||
}, 120000);
|
||||
};
|
||||
|
||||
// Handle retry
|
||||
const handleRetry = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await api.post(`/api/v1/content/${contentId}/retry-wordpress-sync/`);
|
||||
if (response.data.success) {
|
||||
setWpStatus(prev => prev ? { ...prev, wordpress_sync_status: 'pending' } : null);
|
||||
onStatusChange?.('publishing');
|
||||
pollForStatusUpdate();
|
||||
} else {
|
||||
setError(response.data.message || 'Failed to retry WordPress sync');
|
||||
}
|
||||
} catch (error: any) {
|
||||
setError(error.response?.data?.message || 'Error retrying WordPress sync');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Get status display info
|
||||
const getStatusInfo = () => {
|
||||
if (!wpStatus) {
|
||||
return {
|
||||
color: 'default' as const,
|
||||
icon: <PublishIcon />,
|
||||
label: 'Not Published',
|
||||
action: 'publish'
|
||||
};
|
||||
}
|
||||
|
||||
switch (wpStatus.wordpress_sync_status) {
|
||||
case 'pending':
|
||||
return {
|
||||
color: 'warning' as const,
|
||||
icon: <PendingIcon />,
|
||||
label: 'Queued',
|
||||
action: 'wait'
|
||||
};
|
||||
case 'syncing':
|
||||
return {
|
||||
color: 'info' as const,
|
||||
icon: <SyncingIcon className="animate-spin" />,
|
||||
label: 'Publishing...',
|
||||
action: 'wait'
|
||||
};
|
||||
case 'success':
|
||||
return {
|
||||
color: 'success' as const,
|
||||
icon: <SuccessIcon />,
|
||||
label: 'Published',
|
||||
action: 'view'
|
||||
};
|
||||
case 'failed':
|
||||
return {
|
||||
color: 'error' as const,
|
||||
icon: <ErrorIcon />,
|
||||
label: 'Failed',
|
||||
action: 'retry'
|
||||
};
|
||||
default:
|
||||
return {
|
||||
color: 'default' as const,
|
||||
icon: <PublishIcon />,
|
||||
label: 'Not Published',
|
||||
action: 'publish'
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const statusInfo = getStatusInfo();
|
||||
|
||||
const renderButton = () => {
|
||||
if (size === 'small') {
|
||||
return (
|
||||
<Tooltip title={`WordPress: ${statusInfo.label}`}>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => {
|
||||
if (statusInfo.action === 'publish') {
|
||||
setPublishDialogOpen(true);
|
||||
} else if (statusInfo.action === 'retry') {
|
||||
handleRetry();
|
||||
} else if (statusInfo.action === 'view' && wpStatus?.wordpress_post_url) {
|
||||
window.open(wpStatus.wordpress_post_url, '_blank');
|
||||
}
|
||||
}}
|
||||
disabled={loading || statusInfo.action === 'wait'}
|
||||
color={statusInfo.color}
|
||||
>
|
||||
{loading ? <CircularProgress size={16} /> : statusInfo.icon}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant={statusInfo.action === 'publish' ? 'contained' : 'outlined'}
|
||||
color={statusInfo.color}
|
||||
startIcon={loading ? <CircularProgress size={20} /> : statusInfo.icon}
|
||||
onClick={() => {
|
||||
if (statusInfo.action === 'publish') {
|
||||
setPublishDialogOpen(true);
|
||||
} else if (statusInfo.action === 'retry') {
|
||||
handleRetry();
|
||||
} else if (statusInfo.action === 'view' && wpStatus?.wordpress_post_url) {
|
||||
window.open(wpStatus.wordpress_post_url, '_blank');
|
||||
}
|
||||
}}
|
||||
disabled={loading || statusInfo.action === 'wait'}
|
||||
size={size}
|
||||
>
|
||||
{statusInfo.action === 'publish' && 'Publish to WordPress'}
|
||||
{statusInfo.action === 'retry' && 'Retry'}
|
||||
{statusInfo.action === 'view' && 'View on WordPress'}
|
||||
{statusInfo.action === 'wait' && statusInfo.label}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const renderStatusChip = () => {
|
||||
if (size === 'small') return null;
|
||||
|
||||
return (
|
||||
<Chip
|
||||
icon={statusInfo.icon}
|
||||
label={statusInfo.label}
|
||||
color={statusInfo.color}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
if (wpStatus?.wordpress_post_url) {
|
||||
window.open(wpStatus.wordpress_post_url, '_blank');
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
marginLeft: 8,
|
||||
cursor: wpStatus?.wordpress_post_url ? 'pointer' : 'default'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
{renderButton()}
|
||||
{renderStatusChip()}
|
||||
|
||||
{error && (
|
||||
<Alert severity="error" sx={{ mt: 1 }}>
|
||||
{error}
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setError(null)}
|
||||
sx={{ ml: 1 }}
|
||||
>
|
||||
×
|
||||
</IconButton>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Publish Confirmation Dialog */}
|
||||
<Dialog
|
||||
open={publishDialogOpen}
|
||||
onClose={() => setPublishDialogOpen(false)}
|
||||
maxWidth="sm"
|
||||
fullWidth
|
||||
>
|
||||
<DialogTitle>Publish to WordPress</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
Are you sure you want to publish "<strong>{contentTitle}</strong>" to WordPress?
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" color="textSecondary" sx={{ mt: 2 }}>
|
||||
This will create a new post on your connected WordPress site with all content,
|
||||
images, categories, and SEO metadata.
|
||||
</Typography>
|
||||
|
||||
{wpStatus?.wordpress_sync_status === 'success' && (
|
||||
<Alert severity="info" sx={{ mt: 2 }}>
|
||||
This content is already published to WordPress.
|
||||
You can force republish to update the existing post.
|
||||
</Alert>
|
||||
)}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setPublishDialogOpen(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
{wpStatus?.wordpress_sync_status === 'success' && (
|
||||
<Button
|
||||
onClick={() => handlePublishToWordPress(true)}
|
||||
color="warning"
|
||||
disabled={loading}
|
||||
>
|
||||
Force Republish
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={() => handlePublishToWordPress(false)}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? 'Publishing...' : 'Publish'}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default WordPressPublish;
|
||||
Reference in New Issue
Block a user