fixes
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type ResetLinkModalProps = {
|
||||
variant?: "invite" | "reset";
|
||||
email: string;
|
||||
@@ -13,50 +15,76 @@ export default function ResetLinkModal({
|
||||
expiresAt,
|
||||
onClose
|
||||
}: ResetLinkModalProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKey = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", handleKey);
|
||||
return () => window.removeEventListener("keydown", handleKey);
|
||||
}, [onClose]);
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(resetLink);
|
||||
setCopied(true);
|
||||
window.setTimeout(() => setCopied(false), 2000);
|
||||
} catch {
|
||||
// The field stays visible even if clipboard access is unavailable.
|
||||
// Fallback: select the field so the user can copy manually.
|
||||
setCopied(false);
|
||||
}
|
||||
};
|
||||
|
||||
const isInvite = variant === "invite";
|
||||
|
||||
return (
|
||||
<div className="modal-backdrop" role="presentation">
|
||||
<div className="modal-card" role="dialog" aria-modal="true">
|
||||
<div className="modal-backdrop" role="presentation" onClick={onClose}>
|
||||
<div
|
||||
className="modal-card"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="reset-link-title"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="modal-header">
|
||||
<div>
|
||||
<h2>{isInvite ? "Mailbox setup link" : "Temporary reset link"}</h2>
|
||||
<h2 id="reset-link-title">{isInvite ? "Mailbox setup link" : "Temporary reset link"}</h2>
|
||||
<p>
|
||||
{isInvite
|
||||
? <>Share this one-time setup link for <strong>{email}</strong>. The mailbox will only be created after the user chooses a password.</>
|
||||
: <>No email was sent for <strong>{email}</strong>. Share this one-time link yourself.</>}
|
||||
</p>
|
||||
</div>
|
||||
<button type="button" className="ghost-button" onClick={onClose}>
|
||||
Close
|
||||
<button type="button" className="modal-close" onClick={onClose} aria-label="Close">
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="stack">
|
||||
<label className="field">
|
||||
<span>{isInvite ? "Setup link" : "One-time link"}</span>
|
||||
<input type="text" readOnly value={resetLink} />
|
||||
</label>
|
||||
<div className="link-field">
|
||||
<label className="field">
|
||||
<span>{isInvite ? "Setup link" : "One-time link"}</span>
|
||||
<div className="link-row">
|
||||
<input type="text" readOnly value={resetLink} onFocus={(e) => e.target.select()} />
|
||||
<button type="button" className="ghost-button" onClick={handleCopy}>
|
||||
{copied ? "Copied ✓" : "Copy"}
|
||||
</button>
|
||||
</div>
|
||||
</label>
|
||||
<span className="copy-feedback" aria-live="polite">{copied ? "Link copied to clipboard." : ""}</span>
|
||||
</div>
|
||||
|
||||
<p className="modal-copy">
|
||||
This link expires at {expiresAt} and becomes invalid after first use.
|
||||
</p>
|
||||
|
||||
<div className="modal-actions">
|
||||
<button type="button" className="ghost-button" onClick={onClose}>
|
||||
<button type="button" className="primary-button" onClick={onClose}>
|
||||
Done
|
||||
</button>
|
||||
<button type="button" className="primary-button" onClick={handleCopy}>
|
||||
Copy link
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user