fix: clipboard error handling, Escape key support, per-notification read state

This commit is contained in:
Agent Zero
2026-06-28 12:24:35 +02:00
parent 91dceaae14
commit 0f6889cd06
3 changed files with 26 additions and 9 deletions
+5 -1
View File
@@ -27,8 +27,12 @@ const quickStart: { step: number; text: string }[] = [
const HelpPanel: React.FC<HelpPanelProps> = ({ open, onClose }) => { const HelpPanel: React.FC<HelpPanelProps> = ({ open, onClose }) => {
if (!open) return null; if (!open) return null;
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
return ( return (
<div className="modal-backdrop" onClick={onClose}> <div className="modal-backdrop" onClick={onClose} onKeyDown={handleKeyDown} tabIndex={-1}>
<div className="modal-panel help-panel" onClick={(e) => e.stopPropagation()}> <div className="modal-panel help-panel" onClick={(e) => e.stopPropagation()}>
<div className="modal-header"> <div className="modal-header">
<h2>Hilfe</h2> <h2>Hilfe</h2>
+14 -7
View File
@@ -6,17 +6,17 @@ interface Notification {
icon: string; icon: string;
title: string; title: string;
timestamp: string; timestamp: string;
read: boolean;
} }
const initialNotifications: Notification[] = [ const initialNotifications: Notification[] = [
{ id: 'n1', icon: '💾', title: 'Projekt automatisch gespeichert', timestamp: 'vor 2 Min.' }, { id: 'n1', icon: '💾', title: 'Projekt automatisch gespeichert', timestamp: 'vor 2 Min.', read: false },
{ id: 'n2', icon: '🔄', title: 'Neue Version verfügbar', timestamp: 'vor 15 Min.' }, { id: 'n2', icon: '🔄', title: 'Neue Version verfügbar', timestamp: 'vor 15 Min.', read: false },
{ id: 'n3', icon: '👤', title: 'Benutzer ist beigetreten', timestamp: 'vor 1 Std.' }, { id: 'n3', icon: '👤', title: 'Benutzer ist beigetreten', timestamp: 'vor 1 Std.', read: false },
]; ];
const NotificationPanel: React.FC<NotificationPanelProps> = ({ open, onClose }) => { const NotificationPanel: React.FC<NotificationPanelProps> = ({ open, onClose }) => {
const [notifications, setNotifications] = useState<Notification[]>(initialNotifications); const [notifications, setNotifications] = useState<Notification[]>(initialNotifications);
const [allRead, setAllRead] = useState(false);
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
@@ -26,8 +26,15 @@ const NotificationPanel: React.FC<NotificationPanelProps> = ({ open, onClose })
onClose(); onClose();
} }
}; };
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
document.addEventListener('mousedown', handleClickOutside); document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside); document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('keydown', handleKeyDown);
};
}, [open, onClose]); }, [open, onClose]);
if (!open) return null; if (!open) return null;
@@ -37,7 +44,7 @@ const NotificationPanel: React.FC<NotificationPanelProps> = ({ open, onClose })
}; };
const handleMarkAllRead = () => { const handleMarkAllRead = () => {
setAllRead(true); setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));
}; };
return ( return (
@@ -53,7 +60,7 @@ const NotificationPanel: React.FC<NotificationPanelProps> = ({ open, onClose })
<p className="notif-empty">Keine Benachrichtigungen</p> <p className="notif-empty">Keine Benachrichtigungen</p>
) : ( ) : (
notifications.map((n) => ( notifications.map((n) => (
<div key={n.id} className={`notif-item ${allRead ? 'read' : ''}`}> <div key={n.id} className={`notif-item ${n.read ? 'read' : ''}`}>
<span className="notif-icon">{n.icon}</span> <span className="notif-icon">{n.icon}</span>
<div className="notif-content"> <div className="notif-content">
<span className="notif-title">{n.title}</span> <span className="notif-title">{n.title}</span>
+7 -1
View File
@@ -14,9 +14,15 @@ const ShareDialog: React.FC<ShareDialogProps> = ({ open, onClose, projectName })
navigator.clipboard.writeText(shareLink).then(() => { navigator.clipboard.writeText(shareLink).then(() => {
setCopied(true); setCopied(true);
setTimeout(() => setCopied(false), 2000); setTimeout(() => setCopied(false), 2000);
}).catch(() => {
setCopied(false);
}); });
}; };
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
const handleInvite = () => { const handleInvite = () => {
if (email.trim()) { if (email.trim()) {
setInvited(true); setInvited(true);
@@ -26,7 +32,7 @@ const ShareDialog: React.FC<ShareDialogProps> = ({ open, onClose, projectName })
}; };
return ( return (
<div className="modal-backdrop" onClick={onClose}> <div className="modal-backdrop" onClick={onClose} onKeyDown={handleKeyDown} tabIndex={-1}>
<div className="modal-panel share-dialog" onClick={(e) => e.stopPropagation()}> <div className="modal-panel share-dialog" onClick={(e) => e.stopPropagation()}>
<div className="modal-header"> <div className="modal-header">
<h2>Projekt teilen</h2> <h2>Projekt teilen</h2>