feat: Curator-Hilfe-System implementiert
- Hilfe-Seite /curator/help mit vollständiger Dokumentation (de/en) - HelpTooltip-Komponente mit Hover- und Click-Modi - Tooltips bei allen wichtigen Dashboard-Bereichen: * Dashboard-Übersicht * Upload-Bereich & Genre-Zuweisung * Track-Liste (Suche, Filter, Batch-Edit) * Kommentar-Verwaltung - Prominenter Hilfe-Button im Header - Umfassende Übersetzungen für alle Hilfe-Texte - Fix: TypeScript-Fehler in batch route behoben - Fix: Doppelter Browser-Tooltip entfernt (nur noch custom Tooltip)
This commit is contained in:
173
components/HelpTooltip.tsx
Normal file
173
components/HelpTooltip.tsx
Normal file
@@ -0,0 +1,173 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
|
||||
interface HelpTooltipProps {
|
||||
shortText: string; // Text für Hover
|
||||
longText: string; // Text für Click/Modal
|
||||
position?: 'top' | 'bottom' | 'left' | 'right';
|
||||
}
|
||||
|
||||
export default function HelpTooltip({ shortText, longText, position = 'top' }: HelpTooltipProps) {
|
||||
const [showHover, setShowHover] = useState(false);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const tooltipRef = useRef<HTMLDivElement>(null);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
if (
|
||||
tooltipRef.current &&
|
||||
!tooltipRef.current.contains(event.target as Node) &&
|
||||
buttonRef.current &&
|
||||
!buttonRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setShowModal(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (showModal) {
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}
|
||||
}, [showModal]);
|
||||
|
||||
const positionStyles = {
|
||||
top: { bottom: '100%', left: '50%', transform: 'translateX(-50%)', marginBottom: '0.5rem' },
|
||||
bottom: { top: '100%', left: '50%', transform: 'translateX(-50%)', marginTop: '0.5rem' },
|
||||
left: { right: '100%', top: '50%', transform: 'translateY(-50%)', marginRight: '0.5rem' },
|
||||
right: { left: '100%', top: '50%', transform: 'translateY(-50%)', marginLeft: '0.5rem' },
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
||||
<button
|
||||
ref={buttonRef}
|
||||
type="button"
|
||||
onClick={() => setShowModal(!showModal)}
|
||||
onMouseEnter={() => setShowHover(true)}
|
||||
onMouseLeave={() => setShowHover(false)}
|
||||
style={{
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
color: '#6b7280',
|
||||
fontSize: '1rem',
|
||||
padding: '0.25rem',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: '50%',
|
||||
width: '1.5rem',
|
||||
height: '1.5rem',
|
||||
transition: 'background-color 0.2s',
|
||||
}}
|
||||
onMouseOver={(e) => {
|
||||
e.currentTarget.style.backgroundColor = '#f3f4f6';
|
||||
}}
|
||||
onMouseOut={(e) => {
|
||||
e.currentTarget.style.backgroundColor = 'transparent';
|
||||
}}
|
||||
aria-label="Help"
|
||||
>
|
||||
?
|
||||
</button>
|
||||
|
||||
{/* Hover Tooltip */}
|
||||
{showHover && !showModal && (
|
||||
<div
|
||||
ref={tooltipRef}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
...positionStyles[position],
|
||||
background: '#1f2937',
|
||||
color: 'white',
|
||||
padding: '0.5rem 0.75rem',
|
||||
borderRadius: '0.375rem',
|
||||
fontSize: '0.875rem',
|
||||
whiteSpace: 'normal',
|
||||
zIndex: 1000,
|
||||
pointerEvents: 'none',
|
||||
maxWidth: '250px',
|
||||
}}
|
||||
>
|
||||
{shortText}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
...(position === 'top' && { top: '100%', left: '50%', transform: 'translateX(-50%)', borderLeft: '6px solid transparent', borderRight: '6px solid transparent', borderTop: '6px solid #1f2937' }),
|
||||
...(position === 'bottom' && { bottom: '100%', left: '50%', transform: 'translateX(-50%)', borderLeft: '6px solid transparent', borderRight: '6px solid transparent', borderBottom: '6px solid #1f2937' }),
|
||||
...(position === 'left' && { left: '100%', top: '50%', transform: 'translateY(-50%)', borderTop: '6px solid transparent', borderBottom: '6px solid transparent', borderLeft: '6px solid #1f2937' }),
|
||||
...(position === 'right' && { right: '100%', top: '50%', transform: 'translateY(-50%)', borderTop: '6px solid transparent', borderBottom: '6px solid transparent', borderRight: '6px solid #1f2937' }),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Modal für detaillierte Informationen */}
|
||||
{showModal && (
|
||||
<>
|
||||
{/* Overlay */}
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
background: 'rgba(0, 0, 0, 0.5)',
|
||||
zIndex: 9998,
|
||||
}}
|
||||
onClick={() => setShowModal(false)}
|
||||
/>
|
||||
{/* Modal Content */}
|
||||
<div
|
||||
ref={tooltipRef}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
background: 'white',
|
||||
padding: '1.5rem',
|
||||
borderRadius: '0.5rem',
|
||||
boxShadow: '0 10px 25px rgba(0, 0, 0, 0.2)',
|
||||
maxWidth: '500px',
|
||||
width: '90%',
|
||||
maxHeight: '80vh',
|
||||
overflowY: 'auto',
|
||||
zIndex: 9999,
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', marginBottom: '1rem' }}>
|
||||
<h3 style={{ margin: 0, fontSize: '1.25rem', fontWeight: 'bold' }}>Hilfe</h3>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowModal(false)}
|
||||
style={{
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
fontSize: '1.5rem',
|
||||
cursor: 'pointer',
|
||||
color: '#6b7280',
|
||||
padding: '0',
|
||||
lineHeight: '1',
|
||||
}}
|
||||
aria-label="Close"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ fontSize: '0.9rem', lineHeight: '1.6', whiteSpace: 'pre-wrap' }}>
|
||||
{longText}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user