feat(web): add reusable ConfirmDialog and polish delete interactions

Extracts reusable ConfirmDialog component, refactors ship delete to show
the ship name in confirmation, and improves interaction consistency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bas van Rossem
2026-02-19 16:27:43 +01:00
parent 1ef2f6338c
commit b3a55d9fac
2 changed files with 49 additions and 25 deletions

View File

@@ -0,0 +1,35 @@
import Modal from './Modal';
interface Props {
open: boolean;
onClose: () => void;
onConfirm: () => void;
title: string;
message: string;
confirmLabel?: string;
danger?: boolean;
}
export default function ConfirmDialog({
open,
onClose,
onConfirm,
title,
message,
confirmLabel = 'Confirm',
danger = false,
}: Props) {
return (
<Modal open={open} onClose={onClose} title={title}>
<p style={{ margin: 'var(--spacing-md) 0', lineHeight: 1.5 }}>{message}</p>
<div className="modal-actions">
<button className="btn-secondary" onClick={onClose}>
Cancel
</button>
<button className={danger ? 'btn-danger' : 'btn-primary'} onClick={onConfirm}>
{confirmLabel}
</button>
</div>
</Modal>
);
}

View File

@@ -3,6 +3,7 @@ import TopBar from '../components/layout/TopBar';
import PageContainer from '../components/layout/PageContainer'; import PageContainer from '../components/layout/PageContainer';
import ShipCard from '../components/ships/ShipCard'; import ShipCard from '../components/ships/ShipCard';
import CreateShipModal from '../components/ships/CreateShipModal'; import CreateShipModal from '../components/ships/CreateShipModal';
import ConfirmDialog from '../components/ui/ConfirmDialog';
import { useShipsList } from '../store/use-ships-list'; import { useShipsList } from '../store/use-ships-list';
export default function ShipListPage() { export default function ShipListPage() {
@@ -14,9 +15,9 @@ export default function ShipListPage() {
fetchShips(); fetchShips();
}, [fetchShips]); }, [fetchShips]);
const handleDelete = async (id: string) => { const pendingShipName = pendingDelete
setPendingDelete(id); ? ships.find((s) => s.id === pendingDelete)?.name ?? 'this ship'
}; : '';
const confirmDelete = async () => { const confirmDelete = async () => {
if (!pendingDelete) return; if (!pendingDelete) return;
@@ -42,33 +43,21 @@ export default function ShipListPage() {
<div className="ship-list"> <div className="ship-list">
{ships.map((ship) => ( {ships.map((ship) => (
<ShipCard key={ship.id} ship={ship} onDelete={handleDelete} /> <ShipCard key={ship.id} ship={ship} onDelete={(id) => setPendingDelete(id)} />
))} ))}
</div> </div>
<CreateShipModal open={showCreate} onClose={() => setShowCreate(false)} /> <CreateShipModal open={showCreate} onClose={() => setShowCreate(false)} />
{/* Simple delete confirmation */} <ConfirmDialog
{pendingDelete && ( open={!!pendingDelete}
<div className="modal-overlay" onClick={() => setPendingDelete(null)}> onClose={() => setPendingDelete(null)}
<div className="modal-content" onClick={(e) => e.stopPropagation()}> onConfirm={confirmDelete}
<div className="modal-header"> title="Delete Ship?"
<h2 className="modal-title">Delete Ship?</h2> message={`Permanently delete "${pendingShipName}" and all its weapons?`}
</div> confirmLabel="Delete"
<p style={{ margin: 'var(--spacing-md) 0' }}> danger
This will permanently delete this ship and all its weapons. />
</p>
<div className="modal-actions">
<button className="btn-secondary" onClick={() => setPendingDelete(null)}>
Cancel
</button>
<button className="btn-danger" onClick={confirmDelete}>
Delete
</button>
</div>
</div>
</div>
)}
</PageContainer> </PageContainer>
</> </>
); );