From b3a55d9facca69492c8c0076e8c59b8296fe80e6 Mon Sep 17 00:00:00 2001 From: Bas van Rossem Date: Thu, 19 Feb 2026 16:27:43 +0100 Subject: [PATCH] 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 --- web/src/components/ui/ConfirmDialog.tsx | 35 ++++++++++++++++++++++ web/src/pages/ShipListPage.tsx | 39 +++++++++---------------- 2 files changed, 49 insertions(+), 25 deletions(-) create mode 100644 web/src/components/ui/ConfirmDialog.tsx diff --git a/web/src/components/ui/ConfirmDialog.tsx b/web/src/components/ui/ConfirmDialog.tsx new file mode 100644 index 0000000..7d935ef --- /dev/null +++ b/web/src/components/ui/ConfirmDialog.tsx @@ -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 ( + +

{message}

+
+ + +
+
+ ); +} diff --git a/web/src/pages/ShipListPage.tsx b/web/src/pages/ShipListPage.tsx index 0225276..091f743 100644 --- a/web/src/pages/ShipListPage.tsx +++ b/web/src/pages/ShipListPage.tsx @@ -3,6 +3,7 @@ import TopBar from '../components/layout/TopBar'; import PageContainer from '../components/layout/PageContainer'; import ShipCard from '../components/ships/ShipCard'; import CreateShipModal from '../components/ships/CreateShipModal'; +import ConfirmDialog from '../components/ui/ConfirmDialog'; import { useShipsList } from '../store/use-ships-list'; export default function ShipListPage() { @@ -14,9 +15,9 @@ export default function ShipListPage() { fetchShips(); }, [fetchShips]); - const handleDelete = async (id: string) => { - setPendingDelete(id); - }; + const pendingShipName = pendingDelete + ? ships.find((s) => s.id === pendingDelete)?.name ?? 'this ship' + : ''; const confirmDelete = async () => { if (!pendingDelete) return; @@ -42,33 +43,21 @@ export default function ShipListPage() {
{ships.map((ship) => ( - + setPendingDelete(id)} /> ))}
setShowCreate(false)} /> - {/* Simple delete confirmation */} - {pendingDelete && ( -
setPendingDelete(null)}> -
e.stopPropagation()}> -
-

Delete Ship?

-
-

- This will permanently delete this ship and all its weapons. -

-
- - -
-
-
- )} + setPendingDelete(null)} + onConfirm={confirmDelete} + title="Delete Ship?" + message={`Permanently delete "${pendingShipName}" and all its weapons?`} + confirmLabel="Delete" + danger + /> );