feat: implement Versionen, Teilen, Hilfe, Benachrichtigungen topbar features
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import type { NotificationPanelProps } from '../types/ui.types';
|
||||
|
||||
interface Notification {
|
||||
id: string;
|
||||
icon: string;
|
||||
title: string;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
const initialNotifications: Notification[] = [
|
||||
{ id: 'n1', icon: '💾', title: 'Projekt automatisch gespeichert', timestamp: 'vor 2 Min.' },
|
||||
{ id: 'n2', icon: '🔄', title: 'Neue Version verfügbar', timestamp: 'vor 15 Min.' },
|
||||
{ id: 'n3', icon: '👤', title: 'Benutzer ist beigetreten', timestamp: 'vor 1 Std.' },
|
||||
];
|
||||
|
||||
const NotificationPanel: React.FC<NotificationPanelProps> = ({ open, onClose }) => {
|
||||
const [notifications, setNotifications] = useState<Notification[]>(initialNotifications);
|
||||
const [allRead, setAllRead] = useState(false);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(e.target as Node)) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, [open, onClose]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
const handleDismiss = (id: string) => {
|
||||
setNotifications((prev) => prev.filter((n) => n.id !== id));
|
||||
};
|
||||
|
||||
const handleMarkAllRead = () => {
|
||||
setAllRead(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="notif-panel" ref={ref}>
|
||||
<div className="notif-header">
|
||||
<h3>Benachrichtigungen</h3>
|
||||
<button className="notif-close" aria-label="Schließen" onClick={onClose}>
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="notif-list">
|
||||
{notifications.length === 0 ? (
|
||||
<p className="notif-empty">Keine Benachrichtigungen</p>
|
||||
) : (
|
||||
notifications.map((n) => (
|
||||
<div key={n.id} className={`notif-item ${allRead ? 'read' : ''}`}>
|
||||
<span className="notif-icon">{n.icon}</span>
|
||||
<div className="notif-content">
|
||||
<span className="notif-title">{n.title}</span>
|
||||
<span className="notif-time">{n.timestamp}</span>
|
||||
</div>
|
||||
<button className="notif-dismiss" aria-label="Entfernen" onClick={() => handleDismiss(n.id)}>
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
{notifications.length > 0 && (
|
||||
<button className="notif-mark-all" onClick={handleMarkAllRead}>
|
||||
Alle als gelesen markieren
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotificationPanel;
|
||||
Reference in New Issue
Block a user