Files
igny8/frontend/src/components/common/ScheduleContentModal.tsx
IGNY8 VPS (Salman) 9d4aa32f9e styling fixes
2026-01-19 23:34:41 +00:00

188 lines
5.9 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Modal } from '../ui/modal';
import Button from '../ui/button/Button';
import { CalendarIcon, ClockIcon } from '../../icons';
import { getAccountTimezone } from '../../utils/timezone';
interface Content {
id: number;
title: string;
scheduled_publish_at?: string | null;
}
interface ScheduleContentModalProps {
isOpen: boolean;
onClose: () => void;
content: Content | null;
onSchedule: (contentId: number, scheduledDate: string) => Promise<void>;
mode?: 'schedule' | 'reschedule';
}
const ScheduleContentModal: React.FC<ScheduleContentModalProps> = ({
isOpen,
onClose,
content,
onSchedule,
mode = 'schedule'
}) => {
const [selectedDate, setSelectedDate] = useState<string>('');
const [selectedTime, setSelectedTime] = useState<string>('09:00');
const [isSubmitting, setIsSubmitting] = useState(false);
useEffect(() => {
if (isOpen && content) {
if (mode === 'reschedule' && content.scheduled_publish_at) {
// Pre-fill with existing schedule
const existingDate = new Date(content.scheduled_publish_at);
const dateStr = existingDate.toISOString().split('T')[0];
const hours = existingDate.getHours().toString().padStart(2, '0');
const minutes = existingDate.getMinutes().toString().padStart(2, '0');
setSelectedDate(dateStr);
setSelectedTime(`${hours}:${minutes}`);
} else {
// Default to tomorrow at 9:00 AM
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const dateStr = tomorrow.toISOString().split('T')[0];
setSelectedDate(dateStr);
setSelectedTime('09:00');
}
}
}, [isOpen, content, mode]);
const handleSubmit = async () => {
if (!content || !selectedDate || !selectedTime) return;
// Combine date and time into ISO string
const scheduledDateTime = new Date(`${selectedDate}T${selectedTime}`);
// Validate future date
if (scheduledDateTime <= new Date()) {
alert('Please select a future date and time');
return;
}
setIsSubmitting(true);
try {
await onSchedule(content.id, scheduledDateTime.toISOString());
onClose();
} catch (error) {
console.error('Failed to schedule:', error);
} finally {
setIsSubmitting(false);
}
};
const formatPreviewDate = () => {
if (!selectedDate || !selectedTime) return '';
try {
const dateTime = new Date(`${selectedDate}T${selectedTime}`);
return dateTime.toLocaleString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
hour12: true,
timeZone: getAccountTimezone(),
});
} catch (error) {
return '';
}
};
if (!content) return null;
return (
<Modal
isOpen={isOpen}
onClose={onClose}
showCloseButton={!isSubmitting}
>
<div className="p-6">
{/* Header */}
<div className="flex items-center gap-3 mb-6">
<CalendarIcon className="w-8 h-8 text-primary-500" />
<div>
<h2 className="text-xl font-semibold text-gray-900">
{mode === 'reschedule' ? 'Reschedule' : 'Schedule'} Content Publishing
</h2>
<p className="text-sm text-gray-500 mt-1">
Content: "{content.title}"
</p>
</div>
</div>
{/* Date/Time Selection */}
<div className="space-y-4">
{/* Date Picker */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Schedule Date
</label>
<div className="relative">
<input
type="date"
value={selectedDate}
onChange={(e) => setSelectedDate(e.target.value)}
min={new Date().toISOString().split('T')[0]}
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
disabled={isSubmitting}
/>
<CalendarIcon className="absolute right-3 top-2.5 w-5 h-5 text-gray-400 pointer-events-none" />
</div>
</div>
{/* Time Picker */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Schedule Time
</label>
<div className="relative">
<input
type="time"
value={selectedTime}
onChange={(e) => setSelectedTime(e.target.value)}
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
disabled={isSubmitting}
/>
<ClockIcon className="absolute right-3 top-2.5 w-5 h-5 text-gray-400 pointer-events-none" />
</div>
</div>
{/* Preview */}
{selectedDate && selectedTime && (
<div className="bg-brand-50 border-l-4 border-brand-500 p-4 rounded">
<p className="text-sm font-medium text-brand-900">
Preview: {formatPreviewDate()} ({getAccountTimezone()})
</p>
</div>
)}
</div>
{/* Actions */}
<div className="flex gap-3 mt-6 justify-end">
<Button
variant="outline"
onClick={onClose}
disabled={isSubmitting}
>
Cancel
</Button>
<Button
variant="primary"
onClick={handleSubmit}
disabled={isSubmitting || !selectedDate || !selectedTime}
>
{isSubmitting ? 'Scheduling...' : (mode === 'reschedule' ? 'Reschedule' : 'Schedule')}
</Button>
</div>
</div>
</Modal>
);
};
export default ScheduleContentModal;