Add comprehensive social media management system and improve admin pages

## Social Media Management System
- Add SocialMediaLink database model with platform, URL, icon, and ordering
- Create complete CRUD API endpoints for admin social media management
- Implement admin social media management page with Material-UI DataGrid
- Add "Social Media" menu item to admin navigation
- Update footer to dynamically load and display enabled social media links
- Support multiple platforms: Facebook, Twitter, Instagram, YouTube, LinkedIn, GitHub, TikTok
- Include proper icon mapping and fallback handling

## Admin Pages Improvements
- Replace broken TinyMCE editor with working WYSIWYG rich text editor
- Create SimpleRichEditor component with toolbar for formatting
- Fix admin authentication to use cookies instead of localStorage tokens
- Update all admin API calls to use credentials: 'include'
- Increase content editor height to 800px for better editing experience
- Add Lexical editor component as alternative (not currently used)

## Footer Pages System
- Create 8 comprehensive footer pages: About, Blog, Support, API Docs, Terms, Privacy, Cookies, GDPR
- Implement dynamic footer link management with smart categorization
- Separate Quick Links and Legal sections with automatic filtering
- Remove duplicate hardcoded links and use database-driven system
- All footer pages are fully written with professional content

## Database & Dependencies
- Add uuid package for ID generation
- Update Prisma schema with new SocialMediaLink model and relations
- Seed default social media links for Facebook, Twitter, Instagram, YouTube
- Add Lexical rich text editor packages (@lexical/react, etc.)

## Technical Improvements
- Fix async params compatibility for Next.js 15
- Update MUI DataGrid deprecated props
- Improve admin layout navigation structure
- Add proper TypeScript interfaces for all new components
- Implement proper error handling and user feedback

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-24 12:08:01 +00:00
parent 3b34d7518b
commit 4adf1d286e
17 changed files with 1817 additions and 122 deletions

View File

@@ -15,6 +15,10 @@ import {
Twitter,
Instagram,
YouTube,
LinkedIn,
GitHub,
MusicNote as TikTok,
Share as DefaultIcon
} from '@mui/icons-material'
import { useRouter } from 'next/navigation'
import { useTranslations, useLocale } from 'next-intl'
@@ -27,8 +31,18 @@ interface DynamicPage {
footerOrder?: number
}
interface SocialMediaLink {
id: string
platform: string
name: string
url: string
icon: string
order: number
}
export function Footer() {
const [dynamicPages, setDynamicPages] = useState<DynamicPage[]>([])
const [socialLinks, setSocialLinks] = useState<SocialMediaLink[]>([])
const router = useRouter()
const t = useTranslations('home')
const tSeo = useTranslations('seo')
@@ -36,6 +50,7 @@ export function Footer() {
useEffect(() => {
fetchDynamicPages()
fetchSocialLinks()
}, [])
const fetchDynamicPages = async () => {
@@ -50,10 +65,36 @@ export function Footer() {
}
}
const fetchSocialLinks = async () => {
try {
const response = await fetch('/api/social-media')
if (response.ok) {
const data = await response.json()
setSocialLinks(data.data || [])
}
} catch (error) {
console.error('Failed to fetch social media links:', error)
}
}
const getCurrentYear = () => {
return new Date().getFullYear()
}
const renderSocialIcon = (iconName: string) => {
const iconMap = {
'Facebook': Facebook,
'Twitter': Twitter,
'Instagram': Instagram,
'YouTube': YouTube,
'LinkedIn': LinkedIn,
'GitHub': GitHub,
'TikTok': TikTok
}
const IconComponent = iconMap[iconName as keyof typeof iconMap] || DefaultIcon
return <IconComponent />
}
return (
<Paper component="footer" sx={{ bgcolor: 'grey.900', color: 'white', py: 6 }}>
<Container maxWidth="lg">
@@ -74,12 +115,7 @@ export function Footer() {
{t('footer.quickLinks.title')}
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Button color="inherit" sx={{ justifyContent: 'flex-start', p: 0 }}>
{t('footer.quickLinks.about')}
</Button>
<Button color="inherit" sx={{ justifyContent: 'flex-start', p: 0 }}>
{t('footer.quickLinks.blog')}
</Button>
{/* Static important links */}
<Button
color="inherit"
sx={{ justifyContent: 'flex-start', p: 0 }}
@@ -87,22 +123,21 @@ export function Footer() {
>
{t('footer.quickLinks.contact')}
</Button>
<Button color="inherit" sx={{ justifyContent: 'flex-start', p: 0 }}>
{t('footer.quickLinks.support')}
</Button>
<Button color="inherit" sx={{ justifyContent: 'flex-start', p: 0 }}>
{t('footer.quickLinks.api')}
</Button>
{dynamicPages.map((page) => (
<Button
key={page.id}
color="inherit"
sx={{ justifyContent: 'flex-start', p: 0 }}
onClick={() => router.push(`/${locale}/pages/${page.slug}`)}
>
{page.title}
</Button>
))}
{/* Dynamic pages - filtered for non-legal pages */}
{dynamicPages
.filter(page => !['terms', 'privacy', 'cookies', 'gdpr'].includes(page.slug))
.sort((a, b) => (a.footerOrder || 999) - (b.footerOrder || 999))
.map((page) => (
<Button
key={page.id}
color="inherit"
sx={{ justifyContent: 'flex-start', p: 0 }}
onClick={() => router.push(`/${locale}/pages/${page.slug}`)}
>
{page.title}
</Button>
))}
</Box>
</Box>
@@ -112,18 +147,20 @@ export function Footer() {
{t('footer.legal.title')}
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Button color="inherit" sx={{ justifyContent: 'flex-start', p: 0 }}>
{t('footer.legal.terms')}
</Button>
<Button color="inherit" sx={{ justifyContent: 'flex-start', p: 0 }}>
{t('footer.legal.privacy')}
</Button>
<Button color="inherit" sx={{ justifyContent: 'flex-start', p: 0 }}>
{t('footer.legal.cookies')}
</Button>
<Button color="inherit" sx={{ justifyContent: 'flex-start', p: 0 }}>
{t('footer.legal.gdpr')}
</Button>
{/* Dynamic legal pages */}
{dynamicPages
.filter(page => ['terms', 'privacy', 'cookies', 'gdpr'].includes(page.slug))
.sort((a, b) => (a.footerOrder || 999) - (b.footerOrder || 999))
.map((page) => (
<Button
key={page.id}
color="inherit"
sx={{ justifyContent: 'flex-start', p: 0 }}
onClick={() => router.push(`/${locale}/pages/${page.slug}`)}
>
{page.title}
</Button>
))}
</Box>
</Box>
@@ -132,19 +169,25 @@ export function Footer() {
<Typography variant="h6" sx={{ mb: 2 }}>
{t('footer.social.title')}
</Typography>
<Box sx={{ display: 'flex', gap: 1 }}>
<IconButton color="inherit" size="small">
<Facebook />
</IconButton>
<IconButton color="inherit" size="small">
<Twitter />
</IconButton>
<IconButton color="inherit" size="small">
<Instagram />
</IconButton>
<IconButton color="inherit" size="small">
<YouTube />
</IconButton>
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
{socialLinks.map((link) => (
<IconButton
key={link.id}
color="inherit"
size="small"
href={link.url}
target="_blank"
rel="noopener noreferrer"
title={link.name}
>
{renderSocialIcon(link.icon)}
</IconButton>
))}
{socialLinks.length === 0 && (
<Typography variant="caption" color="grey.500">
No social media links configured
</Typography>
)}
</Box>
</Box>
</Box>