AI Story Writer Backend Migration Complete, Frontend UI Components Added

This commit is contained in:
ajaysi
2025-11-16 19:25:26 +05:30
parent 3b9356e2c8
commit 4901b7eb72
70 changed files with 4765 additions and 1439 deletions

View File

@@ -1,7 +1,10 @@
import React from 'react';
import { Avatar, Box, Menu, MenuItem, Typography, Tooltip, Chip } from '@mui/material';
import React, { useState, useEffect } from 'react';
import { Avatar, Box, Menu, MenuItem, Typography, Tooltip, Chip, Divider } from '@mui/material';
import { useUser, useClerk } from '@clerk/clerk-react';
import { useSubscription } from '../../contexts/SubscriptionContext';
import SystemStatusIndicator from '../ContentPlanningDashboard/components/SystemStatusIndicator';
import UsageDashboard from './UsageDashboard';
import { apiClient } from '../../api/client';
interface UserBadgeProps {
colorMode?: 'light' | 'dark';
@@ -12,6 +15,7 @@ const UserBadge: React.FC<UserBadgeProps> = ({ colorMode = 'light' }) => {
const { signOut } = useClerk();
const { subscription } = useSubscription();
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [systemStatus, setSystemStatus] = useState<'healthy' | 'warning' | 'critical' | 'unknown'>('unknown');
const open = Boolean(anchorEl);
const initials = React.useMemo(() => {
@@ -20,8 +24,43 @@ const UserBadge: React.FC<UserBadgeProps> = ({ colorMode = 'light' }) => {
return (first + last || user?.username?.[0] || user?.primaryEmailAddress?.emailAddress?.[0] || '?').toUpperCase();
}, [user]);
// Fetch system status for status bulb
useEffect(() => {
const fetchSystemStatus = async () => {
try {
const response = await apiClient.get('/api/content-planning/monitoring/lightweight-stats');
const result = response.data;
if (result.status === 'success' && result.data) {
setSystemStatus(result.data.status || 'unknown');
}
} catch (err) {
console.error('Error fetching system status:', err);
setSystemStatus('unknown');
}
};
fetchSystemStatus();
// Refresh every 30 seconds
const interval = setInterval(fetchSystemStatus, 30000);
return () => clearInterval(interval);
}, []);
if (!isSignedIn) return null;
// Get status bulb color
const getStatusBulbColor = () => {
switch (systemStatus) {
case 'healthy':
return '#4caf50'; // Green
case 'warning':
return '#ff9800'; // Orange
case 'critical':
return '#f44336'; // Red
default:
return '#757575'; // Gray for unknown
}
};
// Get plan display info
const getPlanColor = () => {
switch (subscription?.plan) {
@@ -65,24 +104,65 @@ const UserBadge: React.FC<UserBadgeProps> = ({ colorMode = 'light' }) => {
}}
/>
<Tooltip title={`${user?.fullName || user?.username || user?.primaryEmailAddress?.emailAddress || 'User'}`}>
<Avatar
onClick={handleOpen}
sx={{
width: 36,
height: 36,
cursor: 'pointer',
bgcolor: colorMode === 'dark' ? 'rgba(255,255,255,0.2)' : 'primary.main',
color: colorMode === 'dark' ? 'white' : 'white',
fontWeight: 700,
}}
src={user?.imageUrl || undefined}
>
{initials}
</Avatar>
<Tooltip title={`${user?.fullName || user?.username || user?.primaryEmailAddress?.emailAddress || 'User'} - System: ${systemStatus.toUpperCase()}`}>
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<Avatar
onClick={handleOpen}
sx={{
width: 36,
height: 36,
cursor: 'pointer',
bgcolor: colorMode === 'dark' ? 'rgba(255,255,255,0.2)' : 'primary.main',
color: colorMode === 'dark' ? 'white' : 'white',
fontWeight: 700,
}}
src={user?.imageUrl || undefined}
>
{initials}
</Avatar>
{/* Status Bulb */}
<Box
sx={{
position: 'absolute',
bottom: 0,
right: 0,
width: 12,
height: 12,
borderRadius: '50%',
bgcolor: getStatusBulbColor(),
border: `2px solid ${colorMode === 'dark' ? '#1a1a1a' : 'white'}`,
boxShadow: `0 0 8px ${getStatusBulbColor()}80`,
animation: systemStatus === 'healthy' ? 'pulse 2s ease-in-out infinite' : 'none',
'@keyframes pulse': {
'0%, 100%': {
opacity: 1,
transform: 'scale(1)',
},
'50%': {
opacity: 0.8,
transform: 'scale(1.1)',
},
},
}}
/>
</Box>
</Tooltip>
<Menu anchorEl={anchorEl} open={open} onClose={handleClose} anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} transformOrigin={{ vertical: 'top', horizontal: 'right' }}>
<Menu
anchorEl={anchorEl}
open={open}
onClose={handleClose}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
PaperProps={{
sx: {
minWidth: 320,
maxWidth: 400,
maxHeight: '80vh',
overflow: 'auto'
}
}}
>
<Box sx={{ px: 2, py: 1, borderBottom: '1px solid rgba(0,0,0,0.1)' }}>
<Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
{user?.fullName || user?.username || 'User'}
@@ -110,6 +190,50 @@ const UserBadge: React.FC<UserBadgeProps> = ({ colorMode = 'light' }) => {
/>
</Box>
<Divider sx={{ my: 1 }} />
{/* System Status Indicator */}
<Box
sx={{
px: 2,
py: 1.5,
bgcolor: 'rgba(0,0,0,0.02)',
maxWidth: '100%',
overflow: 'hidden'
}}
onClick={(e) => e.stopPropagation()}
>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mb: 1, fontWeight: 600 }}>
System Health
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'center', '& > *': { transform: 'scale(0.85)' } }}>
<SystemStatusIndicator />
</Box>
</Box>
<Divider sx={{ my: 1 }} />
{/* Usage Dashboard */}
<Box
sx={{
px: 2,
py: 1.5,
bgcolor: 'rgba(0,0,0,0.02)',
maxWidth: '100%',
overflow: 'auto'
}}
onClick={(e) => e.stopPropagation()}
>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mb: 1, fontWeight: 600 }}>
Usage Statistics
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'center', flexWrap: 'wrap', gap: 0.5 }}>
<UsageDashboard compact={true} />
</Box>
</Box>
<Divider sx={{ my: 1 }} />
<MenuItem onClick={() => { handleClose(); window.location.href = '/pricing'; }}>
Manage Subscription
</MenuItem>