phase 4-6 - with buggy contetn calendar page
This commit is contained in:
219
frontend/src/components/common/ErrorDetailsModal.tsx
Normal file
219
frontend/src/components/common/ErrorDetailsModal.tsx
Normal file
@@ -0,0 +1,219 @@
|
||||
import React from 'react';
|
||||
import { Modal } from '../ui/modal';
|
||||
import Button from '../ui/button/Button';
|
||||
import { ErrorIcon, CalendarIcon, BoltIcon, ExternalLinkIcon } from '../../icons';
|
||||
|
||||
interface Content {
|
||||
id: number;
|
||||
title: string;
|
||||
site_status?: string;
|
||||
scheduled_publish_at?: string | null;
|
||||
site_status_updated_at?: string | null;
|
||||
site_status_message?: string | null;
|
||||
}
|
||||
|
||||
interface Site {
|
||||
id: number;
|
||||
name: string;
|
||||
platform_type: string;
|
||||
}
|
||||
|
||||
interface ErrorDetailsModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
content: Content | null;
|
||||
site: Site | null;
|
||||
onPublishNow: () => void;
|
||||
onReschedule: () => void;
|
||||
onFixSettings: () => void;
|
||||
}
|
||||
|
||||
const ErrorDetailsModal: React.FC<ErrorDetailsModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
content,
|
||||
site,
|
||||
onPublishNow,
|
||||
onReschedule,
|
||||
onFixSettings
|
||||
}) => {
|
||||
if (!content || !site) return null;
|
||||
|
||||
const formatDate = (isoString: string | null) => {
|
||||
if (!isoString) return 'N/A';
|
||||
try {
|
||||
const date = new Date(isoString);
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
} catch (error) {
|
||||
return isoString;
|
||||
}
|
||||
};
|
||||
|
||||
const errorMessage = content.site_status_message || 'Publishing failed with no error message.';
|
||||
|
||||
// Parse error message to provide helpful suggestions
|
||||
const getErrorSuggestion = (error: string) => {
|
||||
const lowerError = error.toLowerCase();
|
||||
|
||||
if (lowerError.includes('credential') || lowerError.includes('authentication') || lowerError.includes('403')) {
|
||||
return 'The publishing site returned an authentication error. Please check the API key in Site Settings.';
|
||||
} else if (lowerError.includes('timeout') || lowerError.includes('network')) {
|
||||
return 'The publishing site did not respond in time. Please check your internet connection and site availability.';
|
||||
} else if (lowerError.includes('404') || lowerError.includes('not found')) {
|
||||
return 'The publishing endpoint was not found. Please verify the site URL in Site Settings.';
|
||||
} else if (lowerError.includes('500') || lowerError.includes('server error')) {
|
||||
return 'The publishing site returned a server error. Please try again later or contact site support.';
|
||||
} else if (lowerError.includes('required field') || lowerError.includes('missing')) {
|
||||
return 'Required fields are missing in the content. Please review and complete all necessary fields.';
|
||||
} else if (lowerError.includes('rate limit')) {
|
||||
return 'Too many requests were sent to the publishing site. Please wait a few minutes and try again.';
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const suggestion = getErrorSuggestion(errorMessage);
|
||||
const platformName = site.platform_type.charAt(0).toUpperCase() + site.platform_type.slice(1);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
showCloseButton={true}
|
||||
>
|
||||
<div className="p-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<ErrorIcon className="w-8 h-8 text-error-500" />
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-gray-900">
|
||||
Publishing Error Details
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Content failed to publish
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content Details */}
|
||||
<div className="space-y-3 mb-6">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-700">Content:</p>
|
||||
<p className="text-sm text-gray-900 mt-1">"{content.title}"</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-700">Site:</p>
|
||||
<p className="text-sm text-gray-900 mt-1">
|
||||
{site.name} ({platformName})
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{content.scheduled_publish_at && (
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-700">Scheduled:</p>
|
||||
<p className="text-sm text-gray-900 mt-1">
|
||||
{formatDate(content.scheduled_publish_at)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{content.site_status_updated_at && (
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-700">Failed:</p>
|
||||
<p className="text-sm text-gray-900 mt-1">
|
||||
{formatDate(content.site_status_updated_at)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Error Message */}
|
||||
<div className="mb-6">
|
||||
<p className="text-sm font-medium text-gray-700 mb-2">Error Message:</p>
|
||||
<div className="bg-error-50 border border-error-200 rounded-lg p-4">
|
||||
<p className="text-sm text-error-900 whitespace-pre-wrap break-words">
|
||||
{errorMessage}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Suggestion */}
|
||||
{suggestion && (
|
||||
<div className="bg-blue-50 border-l-4 border-blue-500 p-4 mb-6">
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="flex-shrink-0">
|
||||
<svg className="w-5 h-5 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-blue-900">Suggestion:</p>
|
||||
<p className="text-sm text-blue-800 mt-1">{suggestion}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Actions */}
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm font-medium text-gray-700">Actions:</p>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
onFixSettings();
|
||||
onClose();
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
<ExternalLinkIcon className="w-4 h-4 mr-2" />
|
||||
Fix Site Settings
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
onPublishNow();
|
||||
onClose();
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
<BoltIcon className="w-4 h-4 mr-2" />
|
||||
Publish Now
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
onReschedule();
|
||||
onClose();
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
<CalendarIcon className="w-4 h-4 mr-2" />
|
||||
Reschedule
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={onClose}
|
||||
className="w-full"
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorDetailsModal;
|
||||
Reference in New Issue
Block a user