refactor: remove toast notifications from game detail

- Delete Toast component and styles
- Remove all toast usage from GameDetail

Simplifies UX and eliminates transient notifications.

Refs #26
This commit is contained in:
Frank Schwenk
2025-10-30 11:05:27 +01:00
parent 634d012097
commit 147906af59
3 changed files with 1 additions and 214 deletions

View File

@@ -1,7 +1,6 @@
import { h } from 'preact';
import { useState } from 'preact/hooks';
import styles from './GameDetail.module.css';
import Toast from './Toast';
import type { Game, EndlosGame } from '../types/game';
interface GameDetailProps {
@@ -18,23 +17,11 @@ interface GameDetailProps {
* Game detail view for a single game.
*/
const GameDetail = ({ game, onFinishGame, onUpdateScore, onUpdateGame, onUndo, onForfeit, onBack }: GameDetailProps) => {
const [toast, setToast] = useState<{ show: boolean; message: string; type: 'success' | 'error' | 'info' }>({
show: false,
message: '',
type: 'info'
});
if (!game) return null;
const showToast = (message: string, type: 'success' | 'error' | 'info' = 'info') => {
setToast({ show: true, message, type });
};
const handleScoreUpdate = (playerIndex: number, change: number) => {
onUpdateScore(playerIndex, change);
const playerName = [game.player1, game.player2, game.player3][playerIndex - 1];
const action = change > 0 ? 'Punkt hinzugefügt' : 'Punkt abgezogen';
showToast(`${action} für ${playerName}`, 'success');
// Silent update; toast notifications removed
};
@@ -108,7 +95,6 @@ const GameDetail = ({ game, onFinishGame, onUpdateScore, onUpdateGame, onUndo, o
className="btn btn--secondary"
onClick={() => {
onUndo();
showToast('Letzte Aktion rückgängig gemacht', 'info');
}}
aria-label="Rückgängig"
>
@@ -117,12 +103,6 @@ const GameDetail = ({ game, onFinishGame, onUpdateScore, onUpdateGame, onUndo, o
)}
<button className="btn" disabled={isCompleted} onClick={onFinishGame} aria-label={isCompleted ? 'Abgeschlossen' : 'Spiel beenden'}>{isCompleted ? 'Abgeschlossen' : 'Spiel beenden'}</button>
</div>
<Toast
show={toast.show}
message={toast.message}
type={toast.type}
onClose={() => setToast({ show: false, message: '', type: 'info' })}
/>
</div>
);
};

View File

@@ -1,134 +0,0 @@
.toast {
position: fixed;
top: 20px;
right: 20px;
z-index: 10000;
min-width: 300px;
max-width: 400px;
background: #fff;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0,0,0,0.2);
transform: translateX(100%);
opacity: 0;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden;
}
.toast.show {
transform: translateX(0);
opacity: 1;
}
.toast.hide {
transform: translateX(100%);
opacity: 0;
}
.toastContent {
display: flex;
align-items: center;
padding: 16px 20px;
gap: 12px;
}
.toastIcon {
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 14px;
flex-shrink: 0;
}
.toastMessage {
flex: 1;
font-size: 14px;
font-weight: 500;
color: #333;
line-height: 1.4;
}
.toastClose {
background: none;
border: none;
font-size: 18px;
color: #666;
cursor: pointer;
padding: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.2s ease;
flex-shrink: 0;
}
.toastClose:hover {
background: #f0f0f0;
color: #333;
}
/* Toast types */
.toast.success {
border-left: 4px solid #4caf50;
}
.toast.success .toastIcon {
background: #4caf50;
color: #fff;
}
.toast.error {
border-left: 4px solid #f44336;
}
.toast.error .toastIcon {
background: #f44336;
color: #fff;
}
.toast.info {
border-left: 4px solid #2196f3;
}
.toast.info .toastIcon {
background: #2196f3;
color: #fff;
}
/* Animation keyframes */
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOutRight {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
.toast.show {
animation: slideInRight 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.toast.hide {
animation: slideOutRight 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

View File

@@ -1,59 +0,0 @@
import { h } from 'preact';
import { useEffect, useState } from 'preact/hooks';
import styles from './Toast.module.css';
type ToastType = 'success' | 'error' | 'info';
interface ToastProps {
show: boolean;
message: string;
type?: ToastType;
onClose: () => void;
duration?: number;
}
/**
* Toast notification component for user feedback
*/
const Toast = ({ show, message, type = 'info', onClose, duration = 3000 }: ToastProps) => {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
if (show) {
setIsVisible(true);
const timer = setTimeout(() => {
setIsVisible(false);
setTimeout(onClose, 300); // Wait for animation to complete
}, duration);
return () => clearTimeout(timer);
}
}, [show, duration, onClose]);
if (!show && !isVisible) return null;
return (
<div className={`${styles.toast} ${styles[type]} ${isVisible ? styles.show : styles.hide}`}>
<div className={styles.toastContent}>
<div className={styles.toastIcon}>
{type === 'success' && '✓'}
{type === 'error' && '✕'}
{type === 'info' && ''}
</div>
<span className={styles.toastMessage}>{message}</span>
<button
className={styles.toastClose}
onClick={() => {
setIsVisible(false);
setTimeout(onClose, 300);
}}
aria-label="Schließen"
>
×
</button>
</div>
</div>
);
};
export default Toast;