647 lines
23 KiB
TypeScript
647 lines
23 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import {
|
|
Box,
|
|
Grid,
|
|
Paper,
|
|
Typography,
|
|
Button,
|
|
TextField,
|
|
Card,
|
|
CardContent,
|
|
Chip,
|
|
IconButton,
|
|
Dialog,
|
|
DialogTitle,
|
|
DialogContent,
|
|
DialogActions,
|
|
FormControl,
|
|
InputLabel,
|
|
Select,
|
|
MenuItem,
|
|
Alert,
|
|
CircularProgress,
|
|
Tabs,
|
|
Tab,
|
|
Accordion,
|
|
AccordionSummary,
|
|
AccordionDetails,
|
|
List,
|
|
ListItem,
|
|
ListItemText,
|
|
ListItemIcon,
|
|
LinearProgress
|
|
} from '@mui/material';
|
|
import {
|
|
Add as AddIcon,
|
|
Edit as EditIcon,
|
|
Delete as DeleteIcon,
|
|
CalendarToday as CalendarIcon,
|
|
Event as EventIcon,
|
|
Refresh as RefreshIcon,
|
|
TrendingUp as TrendingIcon,
|
|
Analytics as AnalyticsIcon,
|
|
ExpandMore as ExpandMoreIcon,
|
|
Schedule as ScheduleIcon,
|
|
Psychology as PsychologyIcon,
|
|
Business as BusinessIcon,
|
|
Timeline as TimelineIcon,
|
|
Lightbulb as LightbulbIcon,
|
|
CheckCircle as CheckCircleIcon,
|
|
AutoAwesome as AutoAwesomeIcon
|
|
} from '@mui/icons-material';
|
|
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
|
|
|
|
interface TabPanelProps {
|
|
children?: React.ReactNode;
|
|
index: number;
|
|
value: number;
|
|
}
|
|
|
|
function TabPanel(props: TabPanelProps) {
|
|
const { children, value, index, ...other } = props;
|
|
|
|
return (
|
|
<div
|
|
role="tabpanel"
|
|
hidden={value !== index}
|
|
id={`calendar-tabpanel-${index}`}
|
|
aria-labelledby={`calendar-tab-${index}`}
|
|
{...other}
|
|
>
|
|
{value === index && <Box sx={{ p: 3 }}>{children}</Box>}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const CalendarTab: React.FC = () => {
|
|
const {
|
|
calendarEvents,
|
|
createEvent,
|
|
updateEvent,
|
|
deleteEvent,
|
|
error,
|
|
loadCalendarEvents,
|
|
// New calendar generation state
|
|
generatedCalendar,
|
|
calendarGenerationError,
|
|
dataLoading,
|
|
calendarGenerationLoading
|
|
} = useContentPlanningStore();
|
|
|
|
const [tabValue, setTabValue] = useState(0);
|
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
const [selectedEvent, setSelectedEvent] = useState<any>(null);
|
|
const [eventForm, setEventForm] = useState({
|
|
title: '',
|
|
description: '',
|
|
content_type: '',
|
|
platform: '',
|
|
scheduled_date: '',
|
|
status: 'draft' as 'draft' | 'scheduled' | 'published'
|
|
});
|
|
|
|
const safeCalendarEvents = Array.isArray(calendarEvents) ? calendarEvents : [];
|
|
|
|
useEffect(() => {
|
|
loadCalendarData();
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
|
|
const loadCalendarData = async () => {
|
|
try {
|
|
// Load existing calendar events
|
|
await loadCalendarEvents();
|
|
} catch (error) {
|
|
console.error('Error loading calendar data:', error);
|
|
}
|
|
};
|
|
|
|
const handleOpenDialog = (event?: any) => {
|
|
if (event) {
|
|
setSelectedEvent(event);
|
|
setEventForm({
|
|
title: event.title,
|
|
description: event.description,
|
|
content_type: event.content_type,
|
|
platform: event.platform,
|
|
scheduled_date: event.scheduled_date || event.date,
|
|
status: event.status as 'draft' | 'scheduled' | 'published'
|
|
});
|
|
} else {
|
|
setSelectedEvent(null);
|
|
setEventForm({
|
|
title: '',
|
|
description: '',
|
|
content_type: '',
|
|
platform: '',
|
|
scheduled_date: '',
|
|
status: 'draft' as 'draft' | 'scheduled' | 'published'
|
|
});
|
|
}
|
|
setDialogOpen(true);
|
|
};
|
|
|
|
const handleCloseDialog = () => {
|
|
setDialogOpen(false);
|
|
setSelectedEvent(null);
|
|
};
|
|
|
|
const handleSaveEvent = async () => {
|
|
try {
|
|
const eventData = {
|
|
title: eventForm.title,
|
|
description: eventForm.description,
|
|
content_type: eventForm.content_type,
|
|
platform: eventForm.platform,
|
|
date: eventForm.scheduled_date, // Map scheduled_date to date for API compatibility
|
|
status: eventForm.status as 'draft' | 'scheduled' | 'published'
|
|
};
|
|
|
|
if (selectedEvent) {
|
|
await updateEvent(selectedEvent.id, eventData);
|
|
} else {
|
|
await createEvent(eventData);
|
|
}
|
|
handleCloseDialog();
|
|
} catch (error) {
|
|
console.error('Error saving event:', error);
|
|
}
|
|
};
|
|
|
|
const handleDeleteEvent = async (eventId: string) => {
|
|
try {
|
|
await deleteEvent(eventId);
|
|
} catch (error) {
|
|
console.error('Error deleting event:', error);
|
|
}
|
|
};
|
|
|
|
const handleRefreshData = async () => {
|
|
await loadCalendarData();
|
|
};
|
|
|
|
const getStatusColor = (status: string) => {
|
|
switch (status) {
|
|
case 'draft': return 'default';
|
|
case 'scheduled': return 'warning';
|
|
case 'published': return 'success';
|
|
default: return 'default';
|
|
}
|
|
};
|
|
|
|
const renderGeneratedCalendar = () => {
|
|
if (!generatedCalendar) {
|
|
return (
|
|
<Box sx={{ textAlign: 'center', py: 4 }}>
|
|
<AutoAwesomeIcon sx={{ fontSize: 64, color: 'text.secondary', mb: 2 }} />
|
|
<Typography variant="h6" color="text.secondary" gutterBottom>
|
|
No AI-generated calendar available
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary">
|
|
Generate a calendar using the Calendar Wizard to see AI-powered content recommendations
|
|
</Typography>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Grid container spacing={3}>
|
|
{/* Calendar Overview */}
|
|
<Grid item xs={12}>
|
|
<Paper sx={{ p: 3, mb: 3 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
<AutoAwesomeIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|
AI-Generated Content Calendar
|
|
</Typography>
|
|
<Box sx={{ display: 'flex', gap: 2, mb: 2 }}>
|
|
<Chip label={`Type: ${generatedCalendar.calendar_type || 'Monthly'}`} variant="outlined" />
|
|
<Chip label={`Industry: ${generatedCalendar.industry || 'Technology'}`} variant="outlined" />
|
|
<Chip label={`Business Size: ${generatedCalendar.business_size || 'SME'}`} variant="outlined" />
|
|
</Box>
|
|
{generatedCalendar.metadata && (
|
|
<Typography variant="body2" color="text.secondary">
|
|
Generated: {new Date(generatedCalendar.metadata.generated_at).toLocaleString()}
|
|
</Typography>
|
|
)}
|
|
</Paper>
|
|
</Grid>
|
|
|
|
{/* Daily Schedule */}
|
|
{generatedCalendar.daily_schedule && generatedCalendar.daily_schedule.length > 0 && (
|
|
<Grid item xs={12} md={6}>
|
|
<Paper sx={{ p: 3 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
<ScheduleIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|
Daily Schedule
|
|
</Typography>
|
|
<List>
|
|
{generatedCalendar.daily_schedule.map((item: any, index: number) => (
|
|
<ListItem key={index} divider>
|
|
<ListItemIcon>
|
|
<EventIcon />
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary={item.topic || item.content_type}
|
|
secondary={`${item.platform} • ${item.content_type} • ${item.estimated_engagement}% engagement`}
|
|
/>
|
|
</ListItem>
|
|
))}
|
|
</List>
|
|
</Paper>
|
|
</Grid>
|
|
)}
|
|
|
|
{/* Weekly Themes */}
|
|
{generatedCalendar.weekly_themes && generatedCalendar.weekly_themes.length > 0 && (
|
|
<Grid item xs={12} md={6}>
|
|
<Paper sx={{ p: 3 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
<TimelineIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|
Weekly Themes
|
|
</Typography>
|
|
<List>
|
|
{generatedCalendar.weekly_themes.map((theme: any, index: number) => (
|
|
<ListItem key={index} divider>
|
|
<ListItemIcon>
|
|
<LightbulbIcon />
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary={`Week ${theme.week}: ${theme.theme}`}
|
|
secondary={`${theme.content_count} pieces • ${theme.platforms?.join(', ')}`}
|
|
/>
|
|
</ListItem>
|
|
))}
|
|
</List>
|
|
</Paper>
|
|
</Grid>
|
|
)}
|
|
|
|
{/* Content Recommendations */}
|
|
{generatedCalendar.content_recommendations && generatedCalendar.content_recommendations.length > 0 && (
|
|
<Grid item xs={12} md={6}>
|
|
<Paper sx={{ p: 3 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
<TrendingIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|
Content Recommendations
|
|
</Typography>
|
|
<List>
|
|
{generatedCalendar.content_recommendations.map((rec: any, index: number) => (
|
|
<ListItem key={index} divider>
|
|
<ListItemIcon>
|
|
<CheckCircleIcon />
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary={`${rec.type}: ${rec.topic}`}
|
|
secondary={`Priority: ${rec.priority} • ROI: ${(rec.estimated_roi * 100).toFixed(0)}%`}
|
|
/>
|
|
</ListItem>
|
|
))}
|
|
</List>
|
|
</Paper>
|
|
</Grid>
|
|
)}
|
|
|
|
{/* Performance Predictions */}
|
|
{generatedCalendar.performance_predictions && (
|
|
<Grid item xs={12} md={6}>
|
|
<Paper sx={{ p: 3 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
<AnalyticsIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|
Performance Predictions
|
|
</Typography>
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
<Box>
|
|
<Typography variant="body2" color="text.secondary">Estimated Engagement</Typography>
|
|
<LinearProgress
|
|
variant="determinate"
|
|
value={generatedCalendar.performance_predictions.estimated_engagement || 0}
|
|
sx={{ height: 8, borderRadius: 4 }}
|
|
/>
|
|
<Typography variant="body2">{generatedCalendar.performance_predictions.estimated_engagement || 0}%</Typography>
|
|
</Box>
|
|
<Box>
|
|
<Typography variant="body2" color="text.secondary">Estimated Reach</Typography>
|
|
<Typography variant="h6">{generatedCalendar.performance_predictions.estimated_reach || 0}</Typography>
|
|
</Box>
|
|
<Box>
|
|
<Typography variant="body2" color="text.secondary">Estimated Conversions</Typography>
|
|
<Typography variant="h6">{generatedCalendar.performance_predictions.estimated_conversions || 0}</Typography>
|
|
</Box>
|
|
</Box>
|
|
</Paper>
|
|
</Grid>
|
|
)}
|
|
|
|
{/* AI Insights */}
|
|
{generatedCalendar.ai_insights && generatedCalendar.ai_insights.length > 0 && (
|
|
<Grid item xs={12}>
|
|
<Paper sx={{ p: 3 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
<PsychologyIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|
AI Insights
|
|
</Typography>
|
|
<Grid container spacing={2}>
|
|
{generatedCalendar.ai_insights.map((insight: any, index: number) => (
|
|
<Grid item xs={12} md={4} key={index}>
|
|
<Card variant="outlined">
|
|
<CardContent>
|
|
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
Confidence: {(insight.confidence * 100).toFixed(0)}%
|
|
</Typography>
|
|
<Typography variant="body1" gutterBottom>
|
|
{insight.insight}
|
|
</Typography>
|
|
<Typography variant="body2" color="primary">
|
|
Action: {insight.action}
|
|
</Typography>
|
|
</CardContent>
|
|
</Card>
|
|
</Grid>
|
|
))}
|
|
</Grid>
|
|
</Paper>
|
|
</Grid>
|
|
)}
|
|
|
|
{/* Strategy Data */}
|
|
{generatedCalendar.strategy_data && Object.keys(generatedCalendar.strategy_data).length > 0 && (
|
|
<Grid item xs={12}>
|
|
<Paper sx={{ p: 3 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
<BusinessIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|
Strategy Integration
|
|
</Typography>
|
|
<Accordion>
|
|
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
|
<Typography>Strategy Analysis & Quality Indicators</Typography>
|
|
</AccordionSummary>
|
|
<AccordionDetails>
|
|
<Grid container spacing={2}>
|
|
{generatedCalendar.strategy_analysis && (
|
|
<Grid item xs={12} md={6}>
|
|
<Typography variant="subtitle2" gutterBottom>Strategy Analysis</Typography>
|
|
<Typography variant="body2">
|
|
Completion: {generatedCalendar.strategy_analysis.completion_percentage || 0}%
|
|
</Typography>
|
|
<Typography variant="body2">
|
|
Filled Fields: {generatedCalendar.strategy_analysis.filled_fields || 0}/{generatedCalendar.strategy_analysis.total_fields || 30}
|
|
</Typography>
|
|
</Grid>
|
|
)}
|
|
{generatedCalendar.quality_indicators && (
|
|
<Grid item xs={12} md={6}>
|
|
<Typography variant="subtitle2" gutterBottom>Quality Indicators</Typography>
|
|
<Typography variant="body2">
|
|
Overall Quality: {generatedCalendar.quality_indicators.overall_quality_score || 0}%
|
|
</Typography>
|
|
<Typography variant="body2">
|
|
Strategic Alignment: {generatedCalendar.quality_indicators.strategic_alignment || 0}%
|
|
</Typography>
|
|
</Grid>
|
|
)}
|
|
</Grid>
|
|
</AccordionDetails>
|
|
</Accordion>
|
|
</Paper>
|
|
</Grid>
|
|
)}
|
|
</Grid>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Box sx={{ p: 3 }}>
|
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
|
<Typography variant="h4">
|
|
Content Calendar
|
|
</Typography>
|
|
<Box sx={{ display: 'flex', gap: 1 }}>
|
|
<Button
|
|
variant="outlined"
|
|
startIcon={<RefreshIcon />}
|
|
onClick={handleRefreshData}
|
|
disabled={dataLoading}
|
|
>
|
|
Refresh
|
|
</Button>
|
|
<Button
|
|
variant="contained"
|
|
startIcon={<AddIcon />}
|
|
onClick={() => handleOpenDialog()}
|
|
>
|
|
Add Event
|
|
</Button>
|
|
</Box>
|
|
</Box>
|
|
|
|
{error && (
|
|
<Alert severity="error" sx={{ mb: 2 }}>
|
|
{error}
|
|
</Alert>
|
|
)}
|
|
|
|
{calendarGenerationError && (
|
|
<Alert severity="error" sx={{ mb: 2 }}>
|
|
{calendarGenerationError}
|
|
</Alert>
|
|
)}
|
|
|
|
{calendarGenerationLoading && (
|
|
<Alert severity="info" sx={{ mb: 2 }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
<CircularProgress size={20} />
|
|
<Typography>Generating AI-powered content calendar...</Typography>
|
|
</Box>
|
|
</Alert>
|
|
)}
|
|
|
|
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 3 }}>
|
|
<Tabs value={tabValue} onChange={(e, newValue) => setTabValue(newValue)}>
|
|
<Tab label="Calendar Events" icon={<CalendarIcon />} iconPosition="start" />
|
|
<Tab label="AI-Generated Calendar" icon={<AutoAwesomeIcon />} iconPosition="start" />
|
|
</Tabs>
|
|
</Box>
|
|
|
|
<TabPanel value={tabValue} index={0}>
|
|
{/* Calendar Events Tab */}
|
|
{dataLoading ? (
|
|
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
|
|
<CircularProgress />
|
|
</Box>
|
|
) : (
|
|
<Grid container spacing={3}>
|
|
<Grid item xs={12}>
|
|
<Paper sx={{ p: 3 }}>
|
|
<Typography variant="h6" gutterBottom>
|
|
<CalendarIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|
Scheduled Events
|
|
</Typography>
|
|
|
|
{safeCalendarEvents.length === 0 ? (
|
|
<Box sx={{ textAlign: 'center', py: 4 }}>
|
|
<EventIcon sx={{ fontSize: 64, color: 'text.secondary', mb: 2 }} />
|
|
<Typography variant="h6" color="text.secondary" gutterBottom>
|
|
No events scheduled
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary">
|
|
Create your first content event to get started
|
|
</Typography>
|
|
</Box>
|
|
) : (
|
|
<Grid container spacing={2}>
|
|
{safeCalendarEvents.map((event) => (
|
|
<Grid item xs={12} md={6} lg={4} key={event.id}>
|
|
<Card>
|
|
<CardContent>
|
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 1 }}>
|
|
<Typography variant="h6" component="div">
|
|
{event.title}
|
|
</Typography>
|
|
<Box>
|
|
<IconButton
|
|
size="small"
|
|
onClick={() => handleOpenDialog(event)}
|
|
>
|
|
<EditIcon />
|
|
</IconButton>
|
|
<IconButton
|
|
size="small"
|
|
color="error"
|
|
onClick={() => handleDeleteEvent(event.id)}
|
|
>
|
|
<DeleteIcon />
|
|
</IconButton>
|
|
</Box>
|
|
</Box>
|
|
|
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
|
{event.description}
|
|
</Typography>
|
|
|
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, mb: 2 }}>
|
|
<Chip
|
|
label={event.platform}
|
|
size="small"
|
|
variant="outlined"
|
|
/>
|
|
<Chip
|
|
label={event.content_type}
|
|
size="small"
|
|
variant="outlined"
|
|
/>
|
|
<Chip
|
|
label={event.status}
|
|
size="small"
|
|
color={getStatusColor(event.status)}
|
|
/>
|
|
</Box>
|
|
|
|
<Typography variant="caption" color="text.secondary">
|
|
Scheduled: {new Date(event.scheduled_date || event.date || '').toLocaleDateString()}
|
|
</Typography>
|
|
</CardContent>
|
|
</Card>
|
|
</Grid>
|
|
))}
|
|
</Grid>
|
|
)}
|
|
</Paper>
|
|
</Grid>
|
|
</Grid>
|
|
)}
|
|
</TabPanel>
|
|
|
|
<TabPanel value={tabValue} index={1}>
|
|
{/* AI-Generated Calendar Tab */}
|
|
{dataLoading ? (
|
|
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
|
|
<CircularProgress />
|
|
</Box>
|
|
) : (
|
|
renderGeneratedCalendar()
|
|
)}
|
|
</TabPanel>
|
|
|
|
{/* Event Dialog */}
|
|
<Dialog open={dialogOpen} onClose={handleCloseDialog} maxWidth="sm" fullWidth>
|
|
<DialogTitle>
|
|
{selectedEvent ? 'Edit Event' : 'Add New Event'}
|
|
</DialogTitle>
|
|
<DialogContent>
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, mt: 1 }}>
|
|
<TextField
|
|
label="Title"
|
|
value={eventForm.title}
|
|
onChange={(e) => setEventForm({ ...eventForm, title: e.target.value })}
|
|
fullWidth
|
|
/>
|
|
<TextField
|
|
label="Description"
|
|
value={eventForm.description}
|
|
onChange={(e) => setEventForm({ ...eventForm, description: e.target.value })}
|
|
multiline
|
|
rows={3}
|
|
fullWidth
|
|
/>
|
|
<FormControl fullWidth>
|
|
<InputLabel>Content Type</InputLabel>
|
|
<Select
|
|
value={eventForm.content_type}
|
|
onChange={(e) => setEventForm({ ...eventForm, content_type: e.target.value })}
|
|
label="Content Type"
|
|
>
|
|
<MenuItem value="blog_post">Blog Post</MenuItem>
|
|
<MenuItem value="video">Video</MenuItem>
|
|
<MenuItem value="social_post">Social Post</MenuItem>
|
|
<MenuItem value="case_study">Case Study</MenuItem>
|
|
<MenuItem value="whitepaper">Whitepaper</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
<FormControl fullWidth>
|
|
<InputLabel>Platform</InputLabel>
|
|
<Select
|
|
value={eventForm.platform}
|
|
onChange={(e) => setEventForm({ ...eventForm, platform: e.target.value })}
|
|
label="Platform"
|
|
>
|
|
<MenuItem value="website">Website</MenuItem>
|
|
<MenuItem value="linkedin">LinkedIn</MenuItem>
|
|
<MenuItem value="twitter">Twitter</MenuItem>
|
|
<MenuItem value="instagram">Instagram</MenuItem>
|
|
<MenuItem value="youtube">YouTube</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
<TextField
|
|
label="Scheduled Date"
|
|
type="datetime-local"
|
|
value={eventForm.scheduled_date}
|
|
onChange={(e) => setEventForm({ ...eventForm, scheduled_date: e.target.value })}
|
|
fullWidth
|
|
InputLabelProps={{ shrink: true }}
|
|
/>
|
|
<FormControl fullWidth>
|
|
<InputLabel>Status</InputLabel>
|
|
<Select
|
|
value={eventForm.status}
|
|
onChange={(e) => setEventForm({ ...eventForm, status: e.target.value as 'draft' | 'scheduled' | 'published' })}
|
|
label="Status"
|
|
>
|
|
<MenuItem value="draft">Draft</MenuItem>
|
|
<MenuItem value="scheduled">Scheduled</MenuItem>
|
|
<MenuItem value="published">Published</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
</Box>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Button onClick={handleCloseDialog}>Cancel</Button>
|
|
<Button onClick={handleSaveEvent} variant="contained">
|
|
{selectedEvent ? 'Update' : 'Create'}
|
|
</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default CalendarTab;
|