ieeeucsd-org/src/components/dashboard/SettingsSection/EmailRequestSettings.tsx
2025-04-28 13:33:28 -07:00

489 lines
No EOL
23 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Authentication } from '../../../scripts/pocketbase/Authentication';
import { Collections, type User } from '../../../schemas/pocketbase/schema';
import { toast } from 'react-hot-toast';
export default function EmailRequestSettings() {
const auth = Authentication.getInstance();
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [requesting, setRequesting] = useState(false);
const [resettingPassword, setResettingPassword] = useState(false);
const [isOfficer, setIsOfficer] = useState(false);
const [createdEmail, setCreatedEmail] = useState<string | null>(null);
const [showPasswordReset, setShowPasswordReset] = useState(false);
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [passwordError, setPasswordError] = useState('');
// For initial email creation
const [initialPassword, setInitialPassword] = useState('');
const [initialConfirmPassword, setInitialConfirmPassword] = useState('');
const [initialPasswordError, setInitialPasswordError] = useState('');
const [showEmailForm, setShowEmailForm] = useState(false);
useEffect(() => {
const loadUserData = async () => {
try {
setLoading(true);
const currentUser = auth.getCurrentUser();
if (!currentUser) {
// Don't show toast on dashboard page for unauthenticated users
if (!window.location.pathname.includes('/dashboard')) {
toast.error('You must be logged in to access this page');
}
return;
}
setUser(currentUser);
// Check if user is an officer
const pb = auth.getPocketBase();
try {
const officerRecord = await pb.collection('officers').getFirstListItem(`user="${currentUser.id}"`);
if (officerRecord) {
setIsOfficer(true);
}
} catch (error) {
// Not an officer, which is fine
console.log('User is not an officer');
}
} catch (error) {
console.error('Error loading user data:', error);
// Don't show toast on dashboard page for unauthenticated users
if (auth.isAuthenticated() || !window.location.pathname.includes('/dashboard')) {
toast.error('Failed to load user data. Please try again later.');
}
} finally {
setLoading(false);
}
};
loadUserData();
}, []);
const toggleEmailForm = () => {
setShowEmailForm(!showEmailForm);
setInitialPassword('');
setInitialConfirmPassword('');
setInitialPasswordError('');
};
const validateInitialPassword = () => {
if (initialPassword.length < 8) {
setInitialPasswordError('Password must be at least 8 characters long');
return false;
}
if (initialPassword !== initialConfirmPassword) {
setInitialPasswordError('Passwords do not match');
return false;
}
setInitialPasswordError('');
return true;
};
const handleRequestEmail = async () => {
if (!user) return;
if (initialPassword && !validateInitialPassword()) {
return;
}
try {
setRequesting(true);
// Determine what the email will be
const emailUsername = user.email.split('@')[0].toLowerCase().replace(/[^a-z0-9]/g, "");
const emailDomain = import.meta.env.PUBLIC_MXROUTE_EMAIL_DOMAIN || 'ieeeucsd.org';
const expectedEmail = `${emailUsername}@${emailDomain}`;
// Call the API to create the email account
const response = await fetch('/api/create-ieee-email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId: user.id,
name: user.name,
email: user.email,
password: initialPassword || undefined
})
});
const result = await response.json();
if (response.ok && result.success) {
// Email created successfully
setCreatedEmail(result.data.ieeeEmail);
// Update the user record to mark email as requested
const pb = auth.getPocketBase();
await pb.collection(Collections.USERS).update(user.id, {
requested_email: true
});
toast.success('IEEE email created successfully!');
setShowEmailForm(false);
} else {
toast.error(result.message || 'Failed to create email. Please contact the webmaster for assistance.');
}
} catch (error) {
console.error('Error requesting email:', error);
toast.error('Failed to create email. Please try again later.');
} finally {
setRequesting(false);
}
};
const togglePasswordReset = () => {
setShowPasswordReset(!showPasswordReset);
setNewPassword('');
setConfirmPassword('');
setPasswordError('');
};
const validatePassword = () => {
if (newPassword.length < 8) {
setPasswordError('Password must be at least 8 characters long');
return false;
}
if (newPassword !== confirmPassword) {
setPasswordError('Passwords do not match');
return false;
}
setPasswordError('');
return true;
};
const handleResetPassword = async () => {
if (!user || !user.requested_email) return;
if (!validatePassword()) {
return;
}
// Determine the email address
const emailAddress = createdEmail || (user ? `${user.email.split('@')[0].toLowerCase().replace(/[^a-z0-9]/g, "")}@ieeeucsd.org` : '');
try {
setResettingPassword(true);
// Call the API to reset the password
const response = await fetch('/api/reset-email-password', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: emailAddress,
password: newPassword
})
});
const result = await response.json();
if (response.ok && result.success) {
toast.success('Password reset successfully!');
setShowPasswordReset(false);
setNewPassword('');
setConfirmPassword('');
} else {
toast.error(result.message || 'Failed to reset password. Please contact the webmaster for assistance.');
}
} catch (error) {
console.error('Error resetting password:', error);
toast.error('Failed to reset password. Please try again later.');
} finally {
setResettingPassword(false);
}
};
if (loading) {
return (
<div className="flex justify-center items-center p-8">
<span className="loading loading-spinner loading-lg text-primary"></span>
</div>
);
}
if (!isOfficer) {
return (
<div className="p-4 bg-base-200 rounded-lg">
<p>IEEE email addresses are only available to officers. If you are an officer and don't see the option to request an email, please contact the webmaster.</p>
</div>
);
}
if (user?.requested_email || createdEmail) {
return (
<div className="space-y-6">
<div className="p-4 bg-base-200 rounded-lg">
<h3 className="font-bold text-lg mb-2">
{createdEmail ? 'Your IEEE Email Address' : 'Email Request Status'}
</h3>
<div className="mb-4">
<p className="text-xl font-mono bg-base-100 p-2 rounded-sm">
{createdEmail || (user ? `${user.email.split('@')[0].toLowerCase().replace(/[^a-z0-9]/g, "")}@ieeeucsd.org` : '')}
</p>
{initialPassword ? (
<p className="mt-2 text-sm">Your email has been created with the password you provided.</p>
) : (
<p className="mt-2 text-sm">Check your personal email for login instructions.</p>
)}
</div>
<div className="mb-4">
<h4 className="font-semibold mb-1">Access Your Email</h4>
<ul className="list-disc list-inside space-y-1">
<li>Webmail: <a href="https://mail.ieeeucsd.org" target="_blank" rel="noopener noreferrer" className="link link-primary">https://mail.ieeeucsd.org</a></li>
</ul>
</div>
<div className="mt-4">
{!showPasswordReset ? (
<button
className="btn btn-secondary w-full"
onClick={togglePasswordReset}
>
Reset Email Password
</button>
) : (
<div className="space-y-4 p-4 bg-base-100 rounded-lg">
<h4 className="font-semibold">Reset Your Email Password</h4>
<div className="alert alert-warning">
<svg xmlns="http://www.w3.org/2000/svg" className="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
<span>
<strong>Important:</strong> After resetting your password, you'll need to update it in any email clients, Gmail integrations, or mobile devices where you've set up this email account.
</span>
</div>
<div className="form-control">
<label className="label">
<span className="label-text">New Password</span>
</label>
<input
type="password"
className="input input-bordered"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
placeholder="Enter new password"
/>
</div>
<div className="form-control">
<label className="label">
<span className="label-text">Confirm Password</span>
</label>
<input
type="password"
className="input input-bordered"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
placeholder="Confirm new password"
/>
</div>
{passwordError && (
<div className="text-error text-sm">{passwordError}</div>
)}
<div className="flex gap-2">
<button
className="btn btn-secondary flex-1"
onClick={handleResetPassword}
disabled={resettingPassword}
>
{resettingPassword ? (
<>
<span className="loading loading-spinner loading-sm"></span>
Resetting...
</>
) : (
'Reset Password'
)}
</button>
<button
className="btn btn-outline"
onClick={togglePasswordReset}
disabled={resettingPassword}
>
Cancel
</button>
</div>
</div>
)}
{!showPasswordReset && (
<p className="text-xs mt-2 opacity-70">
Reset your IEEE email password to a new password of your choice.
</p>
)}
</div>
</div>
<div className="p-4 bg-base-200 rounded-lg">
<h3 className="font-bold text-lg mb-4">Setting Up Your IEEE Email in Gmail</h3>
<div className="mb-6">
<h4 className="font-semibold mb-2">First Step: Set Up Sending From Your IEEE Email</h4>
<ol className="list-decimal list-inside space-y-2">
<li>Go to settings (gear icon) → Accounts and Import</li>
<li>In the section that says <span className="text-blue-600">Send mail as:</span>, select <span className="text-blue-600">Reply from the same address the message was sent to</span></li>
<li>In that same section, select <span className="text-blue-600">Add another email address</span></li>
<li>For the Name, put your actual name (e.g. Charles Nguyen) if this is your personal ieeeucsd.org or put the department name (e.g. IEEEUCSD Webmaster)</li>
<li>For the Email address, put the email that was provided for you</li>
<li>Make sure the <span className="text-blue-600">Treat as an alias</span> button is selected. Go to the next step</li>
<li>For the SMTP Server, put <span className="text-blue-600">mail.ieeeucsd.org</span></li>
<li>For the username, put in your <span className="text-blue-600">FULL ieeeucsd email address</span></li>
<li>For the password, put in the email's password</li>
<li>For the port, put in <span className="text-blue-600">587</span></li>
<li>Make sure you select <span className="text-blue-600">Secured connection with TLS</span></li>
<li>Go back to mail.ieeeucsd.org and verify the email that Google has sent you</li>
</ol>
</div>
<div>
<h4 className="font-semibold mb-2">Second Step: Set Up Receiving Your IEEE Email</h4>
<ol className="list-decimal list-inside space-y-2">
<li>Go to settings (gear icon) Accounts and Import</li>
<li>In the section that says <span className="text-blue-600">Check mail from other accounts:</span>, select <span className="text-blue-600">Add a mail account</span></li>
<li>Put in the ieeeucsd email and hit next</li>
<li>Make sure <span className="text-blue-600">Import emails from my other account (POP3)</span> is selected, then hit next</li>
<li>For the username, put in your full ieeeucsd.org email</li>
<li>For the password, put in your ieeeucsd.org password</li>
<li>For the POP Server, put in <span className="text-blue-600">mail.ieeeucsd.org</span></li>
<li>For the Port, put in <span className="text-blue-600">995</span></li>
<li>Select <span className="text-blue-600">Leave a copy of retrieved message on the server</span></li>
<li>Select <span className="text-blue-600">Always use a secure connection (SSL) when retrieving mail</span></li>
<li>Select <span className="text-blue-600">Label incoming messages</span></li>
<li>Then hit <span className="text-blue-600">Add Account</span></li>
</ol>
</div>
</div>
<div className="p-4 bg-base-200 rounded-lg">
<p className="text-sm">
If you have any questions or need help with your IEEE email, please contact <a href="mailto:webmaster@ieeeucsd.org" className="underline">webmaster@ieeeucsd.org</a>
</p>
</div>
</div>
);
}
return (
<div className="space-y-4">
{!showEmailForm ? (
<>
<p className="text-sm">
As an IEEE officer, you're eligible for an official IEEE UCSD email address. This email can be used for all IEEE-related communications and provides a professional identity when representing the organization.
</p>
<div className="p-4 bg-base-200 rounded-lg">
<h3 className="font-bold text-lg mb-2">Benefits of an IEEE email:</h3>
<ul className="list-disc list-inside space-y-1">
<li>Professional communication with sponsors and partners</li>
<li>Consistent branding for IEEE UCSD</li>
<li>Separation between personal and IEEE communications</li>
<li>Access to IEEE UCSD shared resources</li>
</ul>
</div>
<div className="p-4 bg-base-200 rounded-lg">
<h3 className="font-bold text-lg mb-2">Your IEEE Email Address</h3>
<p className="text-sm mb-2">When you request an email, you'll receive:</p>
<p className="text-xl font-mono bg-base-100 p-2 rounded-sm">
{user?.email ? `${user.email.split('@')[0].toLowerCase().replace(/[^a-z0-9]/g, "")}@ieeeucsd.org` : 'Loading...'}
</p>
</div>
<button
className="btn btn-primary w-full"
onClick={toggleEmailForm}
>
Request IEEE Email Address
</button>
<div className="text-xs opacity-70">
<p>By requesting an email, you agree to use it responsibly and in accordance with IEEE UCSD policies.</p>
</div>
</>
) : (
<div className="p-4 bg-base-200 rounded-lg space-y-4">
<h3 className="font-bold text-lg">Create Your IEEE Email</h3>
<div className="p-4 bg-base-100 rounded-lg">
<p className="font-semibold">Your email address will be:</p>
<p className="text-xl font-mono mt-2">
{user?.email ? `${user.email.split('@')[0].toLowerCase().replace(/[^a-z0-9]/g, "")}@ieeeucsd.org` : 'Loading...'}
</p>
</div>
<div className="form-control">
<label className="label">
<span className="label-text">Choose a Password</span>
</label>
<input
type="password"
className="input input-bordered"
value={initialPassword}
onChange={(e) => setInitialPassword(e.target.value)}
placeholder="Enter password (min. 8 characters)"
/>
</div>
<div className="form-control">
<label className="label">
<span className="label-text">Confirm Password</span>
</label>
<input
type="password"
className="input input-bordered"
value={initialConfirmPassword}
onChange={(e) => setInitialConfirmPassword(e.target.value)}
placeholder="Confirm password"
/>
</div>
{initialPasswordError && (
<div className="text-error text-sm">{initialPasswordError}</div>
)}
<p className="text-sm opacity-70">
Leave the password fields empty if you want a secure random password to be generated and sent to your personal email.
</p>
<div className="flex gap-2">
<button
className="btn btn-primary flex-1"
onClick={handleRequestEmail}
disabled={requesting}
>
{requesting ? (
<>
<span className="loading loading-spinner loading-sm"></span>
Creating Email...
</>
) : (
'Create IEEE Email'
)}
</button>
<button
className="btn btn-outline"
onClick={toggleEmailForm}
disabled={requesting}
>
Cancel
</button>
</div>
</div>
)}
</div>
);
}