UX safety: confirm destructive deletes, scope dice clear, back up sessionLog, add 404
- New themed useConfirm() hook (no native dialogs). World deletes (notes, quests, homebrew, maps) now confirm before removing. - Dice 'Clear history' is disabled with no active campaign (it used to wipe EVERY campaign's rolls) and now confirms; only ever clears the active campaign. - sessionLog is included in backup/restore and the campaign cascade-delete — it was silently dropped on backup and orphaned on campaign delete. - Unknown URLs render a themed 'Page not found' with a link home instead of a blank shell. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { useState } from 'react';
|
||||
import { Modal } from './Modal';
|
||||
import { Button } from './Button';
|
||||
|
||||
interface ConfirmOptions {
|
||||
title: string;
|
||||
message: React.ReactNode;
|
||||
confirmLabel?: string;
|
||||
danger?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise-based confirmation using the themed Modal (no native window.confirm).
|
||||
*
|
||||
* const { confirm, confirmElement } = useConfirm();
|
||||
* ...
|
||||
* <Button onClick={async () => { if (await confirm({ title, message })) doIt(); }} />
|
||||
* {confirmElement}
|
||||
*/
|
||||
export function useConfirm() {
|
||||
const [state, setState] = useState<(ConfirmOptions & { resolve: (ok: boolean) => void }) | null>(null);
|
||||
|
||||
const confirm = (opts: ConfirmOptions) =>
|
||||
new Promise<boolean>((resolve) => setState({ ...opts, resolve }));
|
||||
|
||||
const settle = (ok: boolean) => {
|
||||
state?.resolve(ok);
|
||||
setState(null);
|
||||
};
|
||||
|
||||
const confirmElement = state ? (
|
||||
<Modal
|
||||
open
|
||||
onClose={() => settle(false)}
|
||||
title={state.title}
|
||||
className="max-w-md"
|
||||
footer={
|
||||
<>
|
||||
<Button variant="ghost" onClick={() => settle(false)}>Cancel</Button>
|
||||
<Button variant={state.danger === false ? 'primary' : 'danger'} onClick={() => settle(true)}>
|
||||
{state.confirmLabel ?? 'Delete'}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="text-sm text-muted">{state.message}</div>
|
||||
</Modal>
|
||||
) : null;
|
||||
|
||||
return { confirm, confirmElement };
|
||||
}
|
||||
Reference in New Issue
Block a user