- "frontend/src/components/ConfirmModal.tsx" - "frontend/src/components/ConfirmModal.module.css" - "frontend/src/api/auth.ts" - "frontend/src/context/AuthContext.tsx" - "frontend/src/pages/AdminUsers.tsx" - "frontend/src/pages/AdminUsers.module.css" - "frontend/src/components/ImpersonationBanner.tsx" - "frontend/src/components/ImpersonationBanner.module.css" GSD-Task: S07/T02
66 lines
1.6 KiB
TypeScript
66 lines
1.6 KiB
TypeScript
import { useEffect, useCallback } from "react";
|
|
import styles from "./ConfirmModal.module.css";
|
|
|
|
interface ConfirmModalProps {
|
|
open: boolean;
|
|
title: string;
|
|
message: string;
|
|
confirmLabel?: string;
|
|
cancelLabel?: string;
|
|
onConfirm: () => void;
|
|
onCancel: () => void;
|
|
variant?: "warning" | "danger";
|
|
}
|
|
|
|
export default function ConfirmModal({
|
|
open,
|
|
title,
|
|
message,
|
|
confirmLabel = "Confirm",
|
|
cancelLabel = "Cancel",
|
|
onConfirm,
|
|
onCancel,
|
|
variant = "warning",
|
|
}: ConfirmModalProps) {
|
|
const handleKeyDown = useCallback(
|
|
(e: KeyboardEvent) => {
|
|
if (e.key === "Escape") onCancel();
|
|
},
|
|
[onCancel],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
document.addEventListener("keydown", handleKeyDown);
|
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
}, [open, handleKeyDown]);
|
|
|
|
if (!open) return null;
|
|
|
|
return (
|
|
<div
|
|
className={styles.backdrop}
|
|
onClick={onCancel}
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label={title}
|
|
>
|
|
<div className={styles.card} onClick={(e) => e.stopPropagation()}>
|
|
<h2 className={styles.title}>{title}</h2>
|
|
<p className={styles.message}>{message}</p>
|
|
<div className={styles.actions}>
|
|
<button className={styles.cancelBtn} onClick={onCancel}>
|
|
{cancelLabel}
|
|
</button>
|
|
<button
|
|
className={styles.confirmBtn}
|
|
data-variant={variant}
|
|
onClick={onConfirm}
|
|
>
|
|
{confirmLabel}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|