ALwrity Version 0.5.0 (Fastapi + React )

This commit is contained in:
ajaysi
2025-08-06 12:48:02 +05:30
parent f28a919caa
commit 32f97fa6b3
476 changed files with 115544 additions and 28747 deletions

View File

@@ -0,0 +1,597 @@
import React, { useState, useEffect } from 'react';
import {
Box,
Grid,
Paper,
Typography,
Button,
TextField,
Card,
CardContent,
CardActions,
Chip,
IconButton,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
FormControl,
InputLabel,
Select,
MenuItem,
Alert,
CircularProgress,
Tabs,
Tab,
Accordion,
AccordionSummary,
AccordionDetails,
List,
ListItem,
ListItemText,
ListItemIcon,
Divider,
LinearProgress,
Tooltip,
Badge
} from '@mui/material';
import {
Add as AddIcon,
Edit as EditIcon,
Delete as DeleteIcon,
CalendarToday as CalendarIcon,
Event as EventIcon,
Refresh as RefreshIcon,
AutoAwesome as AIIcon,
TrendingUp as TrendingIcon,
ContentCopy as RepurposeIcon,
Analytics as AnalyticsIcon,
ExpandMore as ExpandMoreIcon,
Schedule as ScheduleIcon,
Psychology as PsychologyIcon,
Business as BusinessIcon,
Group as GroupIcon,
Timeline as TimelineIcon,
Lightbulb as LightbulbIcon,
CheckCircle as CheckCircleIcon,
Warning as WarningIcon,
Info as InfoIcon,
DataUsage as DataUsageIcon,
Insights as InsightsIcon,
Assessment as AssessmentIcon,
Campaign as CampaignIcon,
Speed as SpeedIcon
} from '@mui/icons-material';
import { useContentPlanningStore } from '../../../stores/contentPlanningStore';
import { contentPlanningApi } from '../../../services/contentPlanningApi';
import CalendarGenerationWizard from '../components/CalendarGenerationWizard';
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,
loading,
error,
loadCalendarEvents,
updateCalendarEvents,
// New calendar generation state
generatedCalendar,
contentOptimization,
performancePrediction,
contentRepurposing,
trendingTopics,
aiInsights,
calendarGenerationError,
dataLoading
} = 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'
});
// Enhanced state for data transparency
const [userData, setUserData] = useState<any>({
onboardingData: {},
gapAnalysis: {},
strategyData: {},
recommendationsData: [],
performanceData: {},
aiAnalysisResults: []
});
const [calendarGenerationMode, setCalendarGenerationMode] = useState<'transparency' | 'wizard'>('transparency');
useEffect(() => {
loadCalendarData();
}, []);
const loadCalendarData = async () => {
try {
// Load comprehensive user data for calendar generation
const comprehensiveData = await contentPlanningApi.getComprehensiveUserData(1); // Pass user ID
setUserData(comprehensiveData.data); // Extract the data from the response
// 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 handleGenerateAICalendar = async () => {
try {
// This will now use the comprehensive data from the transparency dashboard
const calendarConfig = {
userData,
calendarType: 'monthly',
industry: userData.onboardingData?.industry || 'technology',
businessSize: 'sme'
};
await contentPlanningApi.generateComprehensiveCalendar(calendarConfig);
} catch (error) {
console.error('Error generating AI calendar:', error);
}
};
const handleDataUpdate = (updatedData: any) => {
setUserData((prev: any) => ({ ...prev, ...updatedData }));
};
const handleGenerateCalendar = async (calendarConfig: any) => {
try {
await contentPlanningApi.generateComprehensiveCalendar({
...calendarConfig,
userData
});
} catch (error) {
console.error('Error generating calendar:', error);
}
};
const handleOptimizeContent = async (contentData: any) => {
try {
await contentPlanningApi.optimizeContent(contentData);
} catch (error) {
console.error('Error optimizing content:', error);
}
};
const handlePredictPerformance = async (contentData: any) => {
try {
await contentPlanningApi.predictPerformance(contentData);
} catch (error) {
console.error('Error predicting performance:', error);
}
};
const handleGetTrendingTopics = async () => {
try {
await contentPlanningApi.getTrendingTopics({ user_id: 1, industry: 'technology' });
} catch (error) {
console.error('Error getting trending topics:', error);
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'draft': return 'default';
case 'scheduled': return 'warning';
case 'published': return 'success';
default: return 'default';
}
};
// Ensure calendarEvents is always an array
const safeCalendarEvents = Array.isArray(calendarEvents) ? calendarEvents : [];
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>
)}
<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="Calendar Wizard" icon={<AIIcon />} iconPosition="start" />
<Tab label="Content Optimizer" icon={<AnalyticsIcon />} iconPosition="start" />
<Tab label="Trending Topics" icon={<TrendingIcon />} 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}>
{/* Calendar Generation Wizard with Data Transparency */}
<CalendarGenerationWizard
userData={userData}
onGenerateCalendar={handleGenerateCalendar}
loading={loading}
/>
</TabPanel>
<TabPanel value={tabValue} index={2}>
{/* Content Optimizer Tab */}
<Grid container spacing={3}>
<Grid item xs={12}>
<Paper sx={{ p: 3 }}>
<Typography variant="h6" gutterBottom>
<AnalyticsIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
Content Optimization
</Typography>
{contentOptimization ? (
<Box>
<Typography variant="body1" gutterBottom>
Optimization Recommendations
</Typography>
<List>
{contentOptimization.recommendations?.map((rec: any, index: number) => (
<ListItem key={index}>
<ListItemIcon>
<LightbulbIcon color="primary" />
</ListItemIcon>
<ListItemText
primary={rec.title}
secondary={rec.description}
/>
</ListItem>
))}
</List>
</Box>
) : (
<Box sx={{ textAlign: 'center', py: 4 }}>
<AnalyticsIcon sx={{ fontSize: 64, color: 'text.secondary', mb: 2 }} />
<Typography variant="h6" color="text.secondary" gutterBottom>
No optimization data
</Typography>
<Typography variant="body2" color="text.secondary">
Generate content optimization recommendations
</Typography>
</Box>
)}
</Paper>
</Grid>
</Grid>
</TabPanel>
<TabPanel value={tabValue} index={3}>
{/* Trending Topics Tab */}
<Grid container spacing={3}>
<Grid item xs={12}>
<Paper sx={{ p: 3 }}>
<Typography variant="h6" gutterBottom>
<TrendingIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
Trending Topics
</Typography>
{trendingTopics ? (
<Box>
<Typography variant="body1" gutterBottom>
Current Trending Topics
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{trendingTopics.trending_topics?.map((topic: any, index: number) => (
<Chip
key={index}
label={topic.name || topic.keyword}
color="primary"
variant="outlined"
/>
))}
</Box>
</Box>
) : (
<Box sx={{ textAlign: 'center', py: 4 }}>
<TrendingIcon sx={{ fontSize: 64, color: 'text.secondary', mb: 2 }} />
<Typography variant="h6" color="text.secondary" gutterBottom>
No trending topics
</Typography>
<Typography variant="body2" color="text.secondary">
Get trending topics for your industry
</Typography>
</Box>
)}
</Paper>
</Grid>
</Grid>
</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;