Files
ALwrity/frontend/src/components/OnboardingWizard/WebsiteStep/utils/renderUtils.tsx
2025-10-10 13:08:09 +05:30

575 lines
18 KiB
TypeScript

/**
* Website Step Rendering Utility Functions
* Extracted rendering components for website analysis display
*/
import React from 'react';
import {
Box,
Typography,
Paper,
Card,
CardContent,
Grid,
Accordion,
AccordionSummary,
AccordionDetails,
Alert,
Button,
Slide,
Zoom
} from '@mui/material';
import { useTheme, alpha } from '@mui/material/styles';
import {
ExpandMore as ExpandMoreIcon,
CheckCircle as CheckIcon,
Psychology as PsychologyIcon,
Analytics as AnalyticsIcon,
Business as BusinessIcon,
AutoAwesome as AutoAwesomeIcon,
Star as StarIcon,
Warning as WarningIcon
} from '@mui/icons-material';
/**
* Key Insight Card Component
*/
interface KeyInsightProps {
title: string;
value: string | string[];
icon: React.ReactNode;
color?: 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info';
}
const KeyInsightCard: React.FC<KeyInsightProps> = ({
title,
value,
icon,
color = 'primary'
}) => {
const theme = useTheme();
const isDark = theme.palette.mode === 'dark';
// Helper function to safely get palette colors
const getPaletteColor = (colorKey: string) => {
const palette = theme.palette as any;
return palette[colorKey] || palette.primary;
};
const paletteColor = getPaletteColor(color);
return (
<Paper
elevation={0}
sx={{
p: 2.5,
mb: 0,
borderRadius: 2.5,
// Force high-contrast base color so nested text never inherits a light color
color: isDark ? '#ffffff' : '#1a202c',
background: isDark
? `linear-gradient(135deg, ${alpha(paletteColor.main, 0.08)} 0%, ${alpha(paletteColor.main, 0.04)} 100%)`
: `linear-gradient(135deg, ${alpha(paletteColor.main, 0.06)} 0%, ${alpha(paletteColor.light, 0.08)} 100%)`,
border: `2px solid`,
borderColor: isDark
? alpha(paletteColor.main, 0.2)
: alpha(paletteColor.main, 0.15),
borderLeftWidth: '5px',
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
'&:hover': {
background: isDark
? `linear-gradient(135deg, ${alpha(paletteColor.main, 0.12)} 0%, ${alpha(paletteColor.main, 0.08)} 100%)`
: `linear-gradient(135deg, ${alpha(paletteColor.main, 0.10)} 0%, ${alpha(paletteColor.light, 0.12)} 100%)`,
borderColor: alpha(paletteColor.main, 0.4),
transform: 'translateY(-4px)',
boxShadow: isDark
? `0 12px 40px ${alpha(paletteColor.main, 0.2)}`
: `0 12px 40px ${alpha(paletteColor.main, 0.15)}`
}
}}
>
<Box display="flex" alignItems="center" gap={2}>
<Box
sx={{
color: `${color}.main`,
fontSize: '1.75rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 48,
height: 48,
borderRadius: 2,
background: isDark
? alpha(paletteColor.main, 0.15)
: alpha(paletteColor.main, 0.1),
}}
>
{icon}
</Box>
<Box flex={1}>
<Typography
variant="caption"
sx={{
fontWeight: 800,
fontSize: '0.78rem',
letterSpacing: '0.6px',
textTransform: 'uppercase',
color: isDark ? '#ffffff !important' : '#0f172a !important',
textShadow: isDark ? 'none' : '0 1px 0 rgba(255,255,255,0.6)',
mb: 0.5,
display: 'block'
}}
>
{title}
</Typography>
<Typography
variant="body1"
sx={{
fontWeight: 700,
fontSize: '1.1rem',
color: isDark ? '#ffffff !important' : '#0b1220 !important',
lineHeight: 1.35
}}
>
{Array.isArray(value) ? value.join(', ') : value}
</Typography>
</Box>
</Box>
</Paper>
);
};
/**
* Backward-compatible wrapper function for renderKeyInsight
* @deprecated Use KeyInsightCard component directly instead
*/
export const renderKeyInsight = (
title: string,
value: string | string[],
icon: React.ReactNode,
color: 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info' = 'primary'
) => <KeyInsightCard title={title} value={value} icon={icon} color={color} />;
/**
* Renders a guidelines card with title, items, and icon
*/
export const renderGuidelinesCard = (
title: string,
items: string[],
icon: React.ReactNode,
color: string = 'primary'
) => (
<Zoom in timeout={600}>
<Card sx={{ mb: 2, border: `1px solid ${color}.light` }}>
<CardContent>
<Box display="flex" alignItems="center" gap={1} mb={2}>
<Box sx={{ color: `${color}.main` }}>
{icon}
</Box>
<Typography variant="h6" fontWeight={600}>
{title}
</Typography>
</Box>
<Box component="ul" sx={{ pl: 2, m: 0 }}>
{items.map((item, index) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1, lineHeight: 1.6 }}>
{item}
</Typography>
))}
</Box>
</CardContent>
</Card>
</Zoom>
);
/**
* Renders the pro upgrade alert
*/
export const renderProUpgradeAlert = () => (
<Slide direction="up" in timeout={1000}>
<Alert
severity="info"
sx={{
mb: 3,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
color: 'white',
'& .MuiAlert-icon': { color: 'white' }
}}
action={
<Button color="inherit" size="small" variant="outlined" sx={{ color: 'white', borderColor: 'white' }}>
Learn More
</Button>
}
>
<Typography variant="subtitle2" gutterBottom>
<StarIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
Limited Analysis Scope
</Typography>
<Typography variant="body2">
This analysis is based on your homepage only. <strong>ALwrity Pro</strong> can index your entire website and social media content for comprehensive personalized content generation.
</Typography>
</Alert>
</Slide>
);
/**
* Renders the brand analysis section
*/
export const renderBrandAnalysisSection = (brandAnalysis: any) => (
<Zoom in timeout={700}>
<Card sx={{ mb: 2, border: '2px solid info.light', background: 'info.50' }}>
<CardContent>
<Box display="flex" alignItems="center" gap={1} mb={2}>
<BusinessIcon color="info" />
<Typography variant="h6" fontWeight={600} color="info.main">
Brand Analysis
</Typography>
</Box>
<Grid container spacing={2}>
{brandAnalysis.brand_voice && (
<Grid item xs={12} md={6}>
<Typography variant="subtitle2" color="primary" gutterBottom>
Brand Voice:
</Typography>
<Typography variant="body2" sx={{ mb: 2 }}>
{brandAnalysis.brand_voice}
</Typography>
</Grid>
)}
{brandAnalysis.brand_positioning && (
<Grid item xs={12} md={6}>
<Typography variant="subtitle2" color="primary" gutterBottom>
Brand Positioning:
</Typography>
<Typography variant="body2" sx={{ mb: 2 }}>
{brandAnalysis.brand_positioning}
</Typography>
</Grid>
)}
{brandAnalysis.brand_values && brandAnalysis.brand_values.length > 0 && (
<Grid item xs={12}>
<Typography variant="subtitle2" color="primary" gutterBottom>
Brand Values:
</Typography>
<Box component="ul" sx={{ pl: 2, m: 0 }}>
{brandAnalysis.brand_values.map((value: string, index: number) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1 }}>
{value}
</Typography>
))}
</Box>
</Grid>
)}
</Grid>
</CardContent>
</Card>
</Zoom>
);
/**
* Renders the content strategy insights section
*/
export const renderContentStrategyInsightsSection = (insights: any) => (
<Zoom in timeout={800}>
<Card sx={{ mb: 2, border: '2px solid secondary.light', background: 'secondary.50' }}>
<CardContent>
<Box display="flex" alignItems="center" gap={1} mb={2}>
<AnalyticsIcon color="secondary" />
<Typography variant="h6" fontWeight={600} color="secondary.main">
Content Strategy Insights
</Typography>
</Box>
<Grid container spacing={3}>
{insights.strengths && insights.strengths.length > 0 && (
<Grid item xs={12} md={6}>
<Typography variant="subtitle2" color="success.main" gutterBottom>
Strengths:
</Typography>
<Box component="ul" sx={{ pl: 2, m: 0 }}>
{insights.strengths.map((strength: string, index: number) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1 }}>
{strength}
</Typography>
))}
</Box>
</Grid>
)}
{insights.opportunities && insights.opportunities.length > 0 && (
<Grid item xs={12} md={6}>
<Typography variant="subtitle2" color="info.main" gutterBottom>
🎯 Opportunities:
</Typography>
<Box component="ul" sx={{ pl: 2, m: 0 }}>
{insights.opportunities.map((opportunity: string, index: number) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1 }}>
{opportunity}
</Typography>
))}
</Box>
</Grid>
)}
{insights.recommended_improvements && insights.recommended_improvements.length > 0 && (
<Grid item xs={12}>
<Typography variant="subtitle2" color="primary" gutterBottom>
🔧 Recommended Improvements:
</Typography>
<Box component="ul" sx={{ pl: 2, m: 0 }}>
{insights.recommended_improvements.map((improvement: string, index: number) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1 }}>
{improvement}
</Typography>
))}
</Box>
</Grid>
)}
</Grid>
</CardContent>
</Card>
</Zoom>
);
/**
* Renders the AI generation tips section
*/
export const renderAIGenerationTipsSection = (tips: string[]) => (
<Zoom in timeout={900}>
<Card sx={{ mb: 2, border: '2px solid primary.light', background: 'primary.50' }}>
<CardContent>
<Box display="flex" alignItems="center" gap={1} mb={2}>
<AutoAwesomeIcon color="primary" />
<Typography variant="h6" fontWeight={600} color="primary.main">
AI Content Generation Tips
</Typography>
</Box>
<Box component="ul" sx={{ pl: 2, m: 0 }}>
{tips.map((tip: string, index: number) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1, lineHeight: 1.6 }}>
{tip}
</Typography>
))}
</Box>
</CardContent>
</Card>
</Zoom>
);
/**
* Renders a best practices section card
*/
export const renderBestPracticesSection = (bestPractices: string[]) => (
<Zoom in timeout={800}>
<Card sx={{ border: '2px solid success.light', background: 'success.50' }}>
<CardContent>
<Box display="flex" alignItems="center" gap={1} mb={2}>
<CheckIcon color="success" />
<Typography variant="h6" fontWeight={600} color="success.main">
Best Practices
</Typography>
</Box>
<Box component="ul" sx={{ pl: 2, m: 0 }}>
{bestPractices.map((practice, index) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1, lineHeight: 1.6 }}>
{practice}
</Typography>
))}
</Box>
</CardContent>
</Card>
</Zoom>
);
/**
* Renders an avoid elements section card
*/
export const renderAvoidElementsSection = (avoidElements: string[]) => (
<Zoom in timeout={1000}>
<Card sx={{ border: '2px solid warning.light', background: 'warning.50' }}>
<CardContent>
<Box display="flex" alignItems="center" gap={1} mb={2}>
<WarningIcon color="warning" />
<Typography variant="h6" fontWeight={600} color="warning.main">
Elements to Avoid
</Typography>
</Box>
<Box component="ul" sx={{ pl: 2, m: 0 }}>
{avoidElements.map((element, index) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1, lineHeight: 1.6 }}>
{element}
</Typography>
))}
</Box>
</CardContent>
</Card>
</Zoom>
);
/**
* Renders a generic analysis section accordion
*/
export const renderAnalysisSection = (
title: string,
data: any,
icon: React.ReactNode,
description?: string
) => (
<Accordion key={title} sx={{ mb: 2 }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Box display="flex" alignItems="center" gap={1}>
{icon}
<Typography variant="h6">{title}</Typography>
</Box>
</AccordionSummary>
<AccordionDetails>
{description && (
<Typography variant="body2" color="textSecondary" sx={{ mb: 2 }}>
{description}
</Typography>
)}
<Grid container spacing={2}>
{Object.entries(data).map(([key, value]) => (
<Grid item xs={12} md={6} key={key}>
<Typography variant="subtitle2" color="primary" gutterBottom>
{key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:
</Typography>
<Typography variant="body2">
{Array.isArray(value) ? value.join(', ') : String(value)}
</Typography>
</Grid>
))}
</Grid>
</AccordionDetails>
</Accordion>
);
/**
* Renders the guidelines section accordion
*/
export const renderGuidelinesSection = (guidelines: any) => (
<Accordion sx={{ mb: 2 }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Box display="flex" alignItems="center" gap={1}>
<PsychologyIcon color="primary" />
<Typography variant="h6">Content Guidelines</Typography>
</Box>
</AccordionSummary>
<AccordionDetails>
<Typography variant="body2" color="textSecondary" sx={{ mb: 2 }}>
Personalized recommendations for improving your content creation based on your writing style analysis.
</Typography>
{guidelines.tone_recommendations && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle1" color="primary" gutterBottom>
Tone Recommendations
</Typography>
<Box component="ul" sx={{ pl: 2 }}>
{guidelines.tone_recommendations.map((rec: string, index: number) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1 }}>
{rec}
</Typography>
))}
</Box>
</Box>
)}
{guidelines.structure_guidelines && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle1" color="primary" gutterBottom>
Structure Guidelines
</Typography>
<Box component="ul" sx={{ pl: 2 }}>
{guidelines.structure_guidelines.map((guideline: string, index: number) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1 }}>
{guideline}
</Typography>
))}
</Box>
</Box>
)}
{guidelines.vocabulary_suggestions && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle1" color="primary" gutterBottom>
Vocabulary Suggestions
</Typography>
<Box component="ul" sx={{ pl: 2 }}>
{guidelines.vocabulary_suggestions.map((suggestion: string, index: number) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1 }}>
{suggestion}
</Typography>
))}
</Box>
</Box>
)}
{guidelines.engagement_tips && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle1" color="primary" gutterBottom>
Engagement Tips
</Typography>
<Box component="ul" sx={{ pl: 2 }}>
{guidelines.engagement_tips.map((tip: string, index: number) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1 }}>
{tip}
</Typography>
))}
</Box>
</Box>
)}
{guidelines.audience_considerations && (
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle1" color="primary" gutterBottom>
Audience Considerations
</Typography>
<Box component="ul" sx={{ pl: 2 }}>
{guidelines.audience_considerations.map((consideration: string, index: number) => (
<Typography component="li" variant="body2" key={index} sx={{ mb: 1 }}>
{consideration}
</Typography>
))}
</Box>
</Box>
)}
</AccordionDetails>
</Accordion>
);
/**
* Renders the style patterns section accordion
*/
export const renderStylePatternsSection = (patterns: any) => (
<Accordion sx={{ mb: 2 }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Box display="flex" alignItems="center" gap={1}>
<AnalyticsIcon color="secondary" />
<Typography variant="h6">Style Patterns</Typography>
</Box>
</AccordionSummary>
<AccordionDetails>
<Typography variant="body2" color="textSecondary" sx={{ mb: 2 }}>
Recurring patterns and characteristics identified in your writing style.
</Typography>
<Grid container spacing={2}>
{Object.entries(patterns).map(([key, value]) => (
<Grid item xs={12} md={6} key={key}>
<Typography variant="subtitle2" color="primary" gutterBottom>
{key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:
</Typography>
<Typography variant="body2">
{Array.isArray(value) ? value.join(', ') : String(value)}
</Typography>
</Grid>
))}
</Grid>
</AccordionDetails>
</Accordion>
);