small fixes
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { FiPlus, FiTrash2, FiEdit2, FiUser, FiShield, FiArrowLeft } from 'react-icons/fi';
|
||||
import Modal from './Modal';
|
||||
import LoadingOverlay from './LoadingOverlay';
|
||||
@@ -219,11 +219,47 @@ const AdminForm = ({ target, domains, currentUserEmail, onCancel, onSaved, onToa
|
||||
const [error, setError] = useState('');
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
// Tracks whether the user has manually typed in the email field.
|
||||
// Once they have, we stop auto-filling to avoid clobbering their input.
|
||||
const [emailTouched, setEmailTouched] = useState(isEdit);
|
||||
const emailRef = useRef(null);
|
||||
|
||||
const sortedDomainNames = useMemo(
|
||||
() => [...domains].map((d) => d.domain).sort(),
|
||||
[domains]
|
||||
);
|
||||
|
||||
// Auto-fill email with "@<domain>" when creating a NEW domain_admin
|
||||
// and exactly one domain is selected, as long as the user hasn't
|
||||
// manually edited the email field yet.
|
||||
useEffect(() => {
|
||||
if (isEdit) return;
|
||||
if (role !== 'domain_admin') return;
|
||||
if (emailTouched) return;
|
||||
if (allowedDomains.size === 1) {
|
||||
const [onlyDomain] = [...allowedDomains];
|
||||
setEmail(`@${onlyDomain}`);
|
||||
// Place caret before the @ so the user can type the local part directly.
|
||||
requestAnimationFrame(() => {
|
||||
const el = emailRef.current;
|
||||
if (el && document.activeElement === el) {
|
||||
try { el.setSelectionRange(0, 0); } catch { /* ignore */ }
|
||||
}
|
||||
});
|
||||
} else if (allowedDomains.size === 0) {
|
||||
setEmail('');
|
||||
}
|
||||
}, [allowedDomains, role, isEdit, emailTouched]);
|
||||
|
||||
// When the user focuses the email field for the first time after
|
||||
// an auto-fill, place the caret at position 0 so they can just type.
|
||||
const handleEmailFocus = () => {
|
||||
const el = emailRef.current;
|
||||
if (el && !emailTouched && email.startsWith('@')) {
|
||||
try { el.setSelectionRange(0, 0); } catch { /* ignore */ }
|
||||
}
|
||||
};
|
||||
|
||||
const toggleDomain = (d) => {
|
||||
const next = new Set(allowedDomains);
|
||||
if (next.has(d)) next.delete(d); else next.add(d);
|
||||
@@ -297,9 +333,11 @@ const AdminForm = ({ target, domains, currentUserEmail, onCancel, onSaved, onToa
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 mb-2">Email</label>
|
||||
<input
|
||||
ref={emailRef}
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
onChange={(e) => { setEmail(e.target.value); setEmailTouched(true); }}
|
||||
onFocus={handleEmailFocus}
|
||||
className="input-field"
|
||||
disabled={isEdit}
|
||||
placeholder="admin@example.com"
|
||||
@@ -395,6 +433,11 @@ const AdminForm = ({ target, domains, currentUserEmail, onCancel, onSaved, onToa
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{!isEdit && allowedDomains.size === 1 && !emailTouched && (
|
||||
<p className="mt-2 text-xs text-gray-500">
|
||||
Email pre-filled with the selected domain. You can still edit it.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@ const Modal = ({ open, onClose, title, subtitle, children, size = 'md' }) => {
|
||||
lg: 'max-w-4xl',
|
||||
};
|
||||
|
||||
// Note: clicking the backdrop does NOT close the modal anymore.
|
||||
// Use the X button or the Escape key instead. This avoids accidental
|
||||
// dismissal when the user is in the middle of editing settings.
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-30 bg-gray-900/40 backdrop-blur-sm flex items-start justify-center p-4 sm:p-8 overflow-y-auto"
|
||||
onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
|
||||
>
|
||||
<div className="fixed inset-0 z-30 bg-gray-900/40 backdrop-blur-sm flex items-start justify-center p-4 sm:p-8 overflow-y-auto">
|
||||
<div className={`bg-white rounded-xl shadow-xl border border-gray-200 w-full ${widths[size]} my-8`}>
|
||||
<div className="flex items-start justify-between px-6 py-4 border-b border-gray-100">
|
||||
<div>
|
||||
@@ -36,6 +36,7 @@ const Modal = ({ open, onClose, title, subtitle, children, size = 'md' }) => {
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-700 p-1 rounded-lg hover:bg-gray-100 transition-colors"
|
||||
aria-label="Close"
|
||||
title="Close (Esc)"
|
||||
>
|
||||
<FiX className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user