Files
biblical-guide.com/components/admin/pages/simple-rich-editor.tsx
Andrei 4adf1d286e 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>
2025-09-24 12:08:01 +00:00

202 lines
5.7 KiB
TypeScript

'use client';
import { useRef, useEffect, useState } from 'react';
import { Box, Toolbar, IconButton, Divider, Select, MenuItem, FormControl } from '@mui/material';
import {
FormatBold,
FormatItalic,
FormatUnderlined,
FormatListBulleted,
FormatListNumbered,
Link,
Image,
Undo,
Redo
} from '@mui/icons-material';
interface SimpleRichEditorProps {
value: string;
onChange: (value: string) => void;
height?: number;
}
export function SimpleRichEditor({ value, onChange, height = 400 }: SimpleRichEditorProps) {
const editorRef = useRef<HTMLDivElement>(null);
const [isUpdating, setIsUpdating] = useState(false);
// Update editor content when value prop changes (but not when we're updating it ourselves)
useEffect(() => {
if (!isUpdating && editorRef.current && editorRef.current.innerHTML !== value) {
editorRef.current.innerHTML = value;
}
}, [value, isUpdating]);
const handleInput = () => {
if (editorRef.current) {
setIsUpdating(true);
onChange(editorRef.current.innerHTML);
setTimeout(() => setIsUpdating(false), 0);
}
};
const execCommand = (command: string, value?: string) => {
document.execCommand(command, false, value);
editorRef.current?.focus();
handleInput();
};
const insertHeading = (tag: string) => {
if (tag === 'paragraph') {
execCommand('formatBlock', 'div');
} else {
execCommand('formatBlock', tag);
}
};
return (
<Box sx={{ width: '100%' }}>
<Box sx={{ border: '1px solid #ddd', borderRadius: 1 }}>
<Toolbar
sx={{
border: 'none',
borderBottom: '1px solid #ddd',
minHeight: 48,
backgroundColor: '#f5f5f5',
flexWrap: 'wrap'
}}
>
<FormControl size="small" sx={{ minWidth: 120, mr: 1 }}>
<Select
defaultValue="paragraph"
displayEmpty
onChange={(e) => insertHeading(e.target.value)}
>
<MenuItem value="paragraph">Paragraph</MenuItem>
<MenuItem value="h1">Heading 1</MenuItem>
<MenuItem value="h2">Heading 2</MenuItem>
<MenuItem value="h3">Heading 3</MenuItem>
<MenuItem value="h4">Heading 4</MenuItem>
<MenuItem value="h5">Heading 5</MenuItem>
<MenuItem value="h6">Heading 6</MenuItem>
</Select>
</FormControl>
<Divider orientation="vertical" flexItem sx={{ mx: 1 }} />
<IconButton onClick={() => execCommand('bold')} size="small">
<FormatBold />
</IconButton>
<IconButton onClick={() => execCommand('italic')} size="small">
<FormatItalic />
</IconButton>
<IconButton onClick={() => execCommand('underline')} size="small">
<FormatUnderlined />
</IconButton>
<Divider orientation="vertical" flexItem sx={{ mx: 1 }} />
<IconButton onClick={() => execCommand('insertUnorderedList')} size="small">
<FormatListBulleted />
</IconButton>
<IconButton onClick={() => execCommand('insertOrderedList')} size="small">
<FormatListNumbered />
</IconButton>
<Divider orientation="vertical" flexItem sx={{ mx: 1 }} />
<IconButton
onClick={() => {
const url = prompt('Enter link URL:');
if (url) execCommand('createLink', url);
}}
size="small"
>
<Link />
</IconButton>
<Divider orientation="vertical" flexItem sx={{ mx: 1 }} />
<IconButton onClick={() => execCommand('undo')} size="small">
<Undo />
</IconButton>
<IconButton onClick={() => execCommand('redo')} size="small">
<Redo />
</IconButton>
</Toolbar>
<div
ref={editorRef}
contentEditable
onInput={handleInput}
style={{
minHeight: `${height}px`,
padding: '16px',
outline: 'none',
fontSize: '14px',
lineHeight: '1.5',
fontFamily: 'inherit',
border: 'none',
backgroundColor: 'white'
}}
dangerouslySetInnerHTML={{ __html: value }}
/>
</Box>
<style jsx global>{`
div[contenteditable] h1 {
font-size: 32px;
font-weight: bold;
margin: 16px 0 8px 0;
}
div[contenteditable] h2 {
font-size: 24px;
font-weight: bold;
margin: 14px 0 6px 0;
}
div[contenteditable] h3 {
font-size: 20px;
font-weight: bold;
margin: 12px 0 4px 0;
}
div[contenteditable] h4 {
font-size: 18px;
font-weight: bold;
margin: 10px 0 4px 0;
}
div[contenteditable] h5 {
font-size: 16px;
font-weight: bold;
margin: 8px 0 2px 0;
}
div[contenteditable] h6 {
font-size: 14px;
font-weight: bold;
margin: 6px 0 2px 0;
}
div[contenteditable] p {
margin: 0 0 8px 0;
}
div[contenteditable] ul,
div[contenteditable] ol {
margin: 8px 0;
padding-left: 24px;
}
div[contenteditable] li {
margin: 2px 0;
}
div[contenteditable] a {
color: #1976d2;
text-decoration: underline;
}
div[contenteditable] a:hover {
text-decoration: none;
}
div[contenteditable]:empty::before {
content: 'Start writing your content...';
color: #999;
pointer-events: none;
}
`}</style>
</Box>
);
}