feat(storage): migrate to IndexedDB with localStorage fallback and async app flow

- Add IndexedDB service with schema, indexes, and player stats
- Migrate GameService to async IndexedDB and auto-migrate from localStorage
- Update hooks and App handlers to async; add error handling and UX feedback
- Convert remaining JSX components to TSX
- Add test utility for IndexedDB and migration checks
- Extend game types with sync fields for future online sync
This commit is contained in:
Frank Schwenk
2025-10-30 09:36:17 +01:00
parent e89ae1039d
commit 8085d2ecc8
20 changed files with 1288 additions and 277 deletions
+36
View File
@@ -0,0 +1,36 @@
import { h } from 'preact';
import styles from './Modal.module.css';
interface ModalProps {
open: boolean;
title: string;
message: string;
onCancel: () => void;
onConfirm: () => void;
}
/**
* Generic modal dialog for confirmation.
*/
const Modal = ({ open, title, message, onCancel, onConfirm }: ModalProps) => {
if (!open) return null;
return (
<div className={styles['modal'] + ' ' + styles['show']} role="dialog" aria-modal="true" aria-labelledby="modal-title">
<div className={styles['modal-content']}>
<div className={styles['modal-header']}>
<span className={styles['modal-title']} id="modal-title">{title}</span>
<button className={styles['close-button']} onClick={onCancel} aria-label="Schließen">×</button>
</div>
<div className={styles['modal-body']}>
<div className={styles['modal-message']}>{message}</div>
</div>
<div className={styles['modal-footer']}>
<button className={styles['modal-button'] + ' ' + styles['cancel']} onClick={onCancel} aria-label="Abbrechen">Abbrechen</button>
<button className={styles['modal-button'] + ' ' + styles['confirm']} onClick={onConfirm} aria-label="Löschen">Löschen</button>
</div>
</div>
</div>
);
};
export default Modal;