Improve domain input loading

This commit is contained in:
Dries Augustyns
2025-12-06 09:45:13 +01:00
parent d392d69fd2
commit 903ea73ca2
+24 -53
View File
@@ -1,4 +1,4 @@
import {useEffect, useState} from 'react'; import {useMemo} from 'react';
import { import {
Alert, Alert,
AlertDescription, AlertDescription,
@@ -28,69 +28,40 @@ export function EmailDomainInput({value, onChange, id, placeholder, required, la
const {activeProject} = useActiveProject(); const {activeProject} = useActiveProject();
const {domains, isLoading} = useDomains(activeProject?.id); const {domains, isLoading} = useDomains(activeProject?.id);
// Split the email into local part and domain // Get verified domains only - memoized to avoid re-creating on every render
const [localPart, setLocalPart] = useState(''); const verifiedDomains = useMemo(() => domains?.filter(d => d.verified) || [], [domains]);
const [selectedDomain, setSelectedDomain] = useState('');
// Get verified domains only // Derive local state directly from the value prop
const verifiedDomains = domains?.filter(d => d.verified) || []; const parsedEmail = useMemo(() => {
// Initialize from value
useEffect(() => {
if (value && value.includes('@')) { if (value && value.includes('@')) {
const [local, domain] = value.split('@'); const [local, domain] = value.split('@');
return {localPart: local ?? '', domain: domain ?? ''};
setLocalPart(local ?? '');
// Check if the domain is in our verified list
const domainPart = domain ?? '';
const matchingDomain = verifiedDomains.find(d => d.domain === domainPart);
if (matchingDomain) {
setSelectedDomain(domainPart);
} else {
// If domain not verified, keep it in the domain field
setSelectedDomain(domainPart);
}
} else if (value) { } else if (value) {
// If no @ sign, treat entire value as local part return {localPart: value, domain: ''};
setLocalPart(value);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps return {localPart: '', domain: ''};
}, [value]); }, [value]);
// Initialize selected domain with first verified domain if none selected // Use derived state for display, with fallback to first domain if none specified
useEffect(() => { const displayDomain = parsedEmail.domain || (verifiedDomains.length > 0 ? verifiedDomains[0]!.domain : '');
if (!selectedDomain && verifiedDomains.length > 0) { const displayLocalPart = parsedEmail.localPart;
const firstDomain = verifiedDomains[0];
if (firstDomain) {
setSelectedDomain(firstDomain.domain);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedDomain]);
// Update parent component when local part or domain changes
const handleUpdate = (newLocal: string, newDomain: string) => {
if (newLocal && newDomain) {
onChange(`${newLocal}@${newDomain}`);
} else if (newLocal) {
onChange(newLocal);
} else {
onChange('');
}
};
const handleLocalPartChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleLocalPartChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newLocal = e.target.value; const newLocal = e.target.value;
setLocalPart(newLocal); if (newLocal && displayDomain) {
handleUpdate(newLocal, selectedDomain); onChange(`${newLocal}@${displayDomain}`);
} else {
onChange(newLocal);
}
}; };
const handleDomainChange = (newDomain: string) => { const handleDomainChange = (newDomain: string) => {
setSelectedDomain(newDomain); if (displayLocalPart && newDomain) {
handleUpdate(localPart, newDomain); onChange(`${displayLocalPart}@${newDomain}`);
} else if (displayLocalPart) {
onChange(displayLocalPart);
}
// If no local part, don't call onChange - wait for user to type something
}; };
if (isLoading) { if (isLoading) {
@@ -142,14 +113,14 @@ export function EmailDomainInput({value, onChange, id, placeholder, required, la
<Input <Input
id={id} id={id}
type="text" type="text"
value={localPart} value={displayLocalPart}
onChange={handleLocalPartChange} onChange={handleLocalPartChange}
placeholder={placeholder || 'hello'} placeholder={placeholder || 'hello'}
required={required} required={required}
className="flex-1" className="flex-1"
/> />
<span className="text-neutral-500">@</span> <span className="text-neutral-500">@</span>
<Select value={selectedDomain} onValueChange={handleDomainChange} required={required}> <Select value={displayDomain} onValueChange={handleDomainChange} required={required}>
<SelectTrigger className="w-[200px] shrink-0"> <SelectTrigger className="w-[200px] shrink-0">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>