mirror of
https://github.com/ceratic/project_vollidioten_website.git
synced 2026-05-14 00:16:47 +02:00
feat: Add DatabaseManager and LinkPlayer components, implement authentication and linking logic
- Created DatabaseManager component for managing database access via phpMyAdmin. - Developed LinkPlayer component to link Discord accounts with game characters, including user authentication and error handling. - Added mock data files for players, organizations, and projects to handle backend unavailability. - Implemented AuthService for managing user authentication and session checks. - Created DatabaseService to fetch and manage player, organization, and project data with fallback to mock data. - Added HTML page for handling authentication unavailability. - Developed a test script for validating Docker setup and required files.
This commit is contained in:
116
components/MarkdownEditor.tsx
Normal file
116
components/MarkdownEditor.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Icons } from './IconSet';
|
||||
|
||||
interface MarkdownEditorProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const MarkdownEditor: React.FC<MarkdownEditorProps> = ({ value, onChange, className = '' }) => {
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const insertText = (before: string, after: string = '', placeholder: string = 'text') => {
|
||||
const textarea = textareaRef.current;
|
||||
if (!textarea) return;
|
||||
|
||||
const start = textarea.selectionStart;
|
||||
const end = textarea.selectionEnd;
|
||||
const selectedText = value.substring(start, end);
|
||||
const textToInsert = selectedText || placeholder;
|
||||
|
||||
const newText = value.substring(0, start) + before + textToInsert + after + value.substring(end);
|
||||
onChange(newText);
|
||||
|
||||
// Set cursor position after the inserted text
|
||||
setTimeout(() => {
|
||||
const newCursorPos = start + before.length + textToInsert.length + after.length;
|
||||
textarea.setSelectionRange(newCursorPos, newCursorPos);
|
||||
textarea.focus();
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const formatButtons = [
|
||||
{
|
||||
icon: 'H',
|
||||
label: 'Überschrift',
|
||||
action: () => insertText('# ', ''),
|
||||
className: 'text-lg font-bold'
|
||||
},
|
||||
{
|
||||
icon: <Icons.Hammer className="w-3 h-3" />,
|
||||
label: 'Fett',
|
||||
action: () => insertText('**', '**', 'fetter Text'),
|
||||
},
|
||||
{
|
||||
icon: <Icons.Tag className="w-3 h-3" />,
|
||||
label: 'Kursiv',
|
||||
action: () => insertText('*', '*', 'kursiver Text'),
|
||||
},
|
||||
{
|
||||
icon: <Icons.Scroll className="w-3 h-3" />,
|
||||
label: 'Liste',
|
||||
action: () => insertText('- ', ''),
|
||||
},
|
||||
{
|
||||
icon: <Icons.Crown className="w-3 h-3" />,
|
||||
label: 'Nummerierte Liste',
|
||||
action: () => insertText('1. ', ''),
|
||||
},
|
||||
{
|
||||
icon: <Icons.Shield className="w-3 h-3" />,
|
||||
label: 'Zitat',
|
||||
action: () => insertText('> ', ''),
|
||||
},
|
||||
{
|
||||
icon: '🔗',
|
||||
label: 'Link',
|
||||
action: () => insertText('[', '](url)', 'Link-Text'),
|
||||
},
|
||||
{
|
||||
icon: '📷',
|
||||
label: 'Bild',
|
||||
action: () => insertText('', 'Alt-Text'),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={`border border-border rounded-lg overflow-hidden ${className}`}>
|
||||
{/* Toolbar */}
|
||||
<div className="bg-surfaceHighlight border-b border-border p-2 flex flex-wrap gap-1">
|
||||
{formatButtons.map((button, index) => (
|
||||
<button
|
||||
key={index}
|
||||
type="button"
|
||||
onClick={button.action}
|
||||
className="p-1.5 hover:bg-white/10 rounded text-textMuted hover:text-textMain transition-colors text-sm flex items-center justify-center min-w-[32px] h-8"
|
||||
title={button.label}
|
||||
>
|
||||
{typeof button.icon === 'string' ? (
|
||||
<span className={button.className}>{button.icon}</span>
|
||||
) : (
|
||||
button.icon
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Textarea */}
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="w-full h-64 md:h-96 bg-[#0b0b0d] border-0 p-4 text-sm font-mono text-gray-300 focus:outline-none resize-none"
|
||||
placeholder="Schreibe dein Journal hier... Verwende die Toolbar für Formatierung."
|
||||
spellCheck={false}
|
||||
/>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="bg-surfaceHighlight border-t border-border px-4 py-2 text-xs text-textMuted">
|
||||
Markdown-Formatierung unterstützt. Verwende die Toolbar für schnelle Formatierung.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownEditor;
|
||||
Reference in New Issue
Block a user