Add email field to RSVP edit form with automatic confirmation
- Added email_address field to RSVPEditForm interface and form - Email field is required and positioned after name field - Added helper text explaining new confirmation will be sent if email changes - Updated form validation to include email as required field - Backend now detects email changes and sends new confirmation emails - Email field follows same editing rules as other fields (disabled when event closed) - Updated backend to use email_address parameter instead of attendee_email for consistency
This commit is contained in:
@@ -547,10 +547,10 @@ app.delete('/api/events/:slug/rsvps/:id', async (req: Request, res: Response) =>
|
|||||||
app.put('/api/rsvps/edit/:editId', async (req: Request, res: Response) => {
|
app.put('/api/rsvps/edit/:editId', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { editId } = req.params;
|
const { editId } = req.params;
|
||||||
const { name, attending, bringing_guests, guest_count, guest_names, items_bringing, other_items, send_event_conclusion_email, attendee_email } = req.body; // Receive new fields
|
const { name, email_address, attending, bringing_guests, guest_count, guest_names, items_bringing, other_items, send_event_conclusion_email } = req.body; // Updated to use email_address
|
||||||
|
|
||||||
// Find the RSVP by edit_id
|
// Find the RSVP by edit_id and get current email
|
||||||
const rsvp = await db.get('SELECT id, event_id FROM rsvps WHERE edit_id = ?', [editId]);
|
const rsvp = await db.get('SELECT id, event_id, attendee_email, name FROM rsvps WHERE edit_id = ?', [editId]);
|
||||||
|
|
||||||
if (!rsvp) {
|
if (!rsvp) {
|
||||||
return res.status(404).json({ error: 'RSVP not found' });
|
return res.status(404).json({ error: 'RSVP not found' });
|
||||||
@@ -558,6 +558,8 @@ app.put('/api/rsvps/edit/:editId', async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const rsvpId = rsvp.id;
|
const rsvpId = rsvp.id;
|
||||||
const eventId = rsvp.event_id;
|
const eventId = rsvp.event_id;
|
||||||
|
const currentEmail = rsvp.attendee_email;
|
||||||
|
const newEmail = email_address?.trim() || null;
|
||||||
|
|
||||||
// Parse items_bringing if it's a string
|
// Parse items_bringing if it's a string
|
||||||
let parsedItemsBringing: string[] = [];
|
let parsedItemsBringing: string[] = [];
|
||||||
@@ -594,8 +596,30 @@ app.put('/api/rsvps/edit/:editId', async (req: Request, res: Response) => {
|
|||||||
(send_event_conclusion_email === 'true' || send_event_conclusion_email === true) :
|
(send_event_conclusion_email === 'true' || send_event_conclusion_email === true) :
|
||||||
Boolean(rsvp.send_event_conclusion_email); // Use existing value if not provided
|
Boolean(rsvp.send_event_conclusion_email); // Use existing value if not provided
|
||||||
|
|
||||||
const attendeeEmailToSave = attendee_email !== undefined ? attendee_email?.trim() || null : rsvp.attendee_email;
|
const attendeeEmailToSave = newEmail;
|
||||||
|
|
||||||
|
// Check if email address changed and send new confirmation if needed
|
||||||
|
const emailChanged = currentEmail !== newEmail && newEmail && process.env.EMAIL_USER;
|
||||||
|
|
||||||
|
if (emailChanged) {
|
||||||
|
try {
|
||||||
|
// Get event details for the email
|
||||||
|
const event = await db.get('SELECT title, slug FROM events WHERE id = ?', [eventId]);
|
||||||
|
if (event) {
|
||||||
|
const editLink = `${process.env.FRONTEND_BASE_URL}/events/${event.slug}/rsvp/edit/${editId}`;
|
||||||
|
await sendRSVPEditLinkEmail({
|
||||||
|
eventTitle: event.title,
|
||||||
|
eventSlug: event.slug,
|
||||||
|
name: name ?? rsvp.name,
|
||||||
|
to: newEmail,
|
||||||
|
editLink,
|
||||||
|
});
|
||||||
|
console.log(`Sent new RSVP edit link email to updated address: ${newEmail}`);
|
||||||
|
}
|
||||||
|
} catch (emailErr) {
|
||||||
|
console.error('Error sending RSVP edit link email to new address:', emailErr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update the RSVP
|
// Update the RSVP
|
||||||
await db.run(
|
await db.run(
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { Event } from '../types';
|
|||||||
|
|
||||||
interface RSVPFormData {
|
interface RSVPFormData {
|
||||||
name: string;
|
name: string;
|
||||||
|
email_address: string;
|
||||||
attending: string;
|
attending: string;
|
||||||
bringing_guests: string;
|
bringing_guests: string;
|
||||||
guest_count: number;
|
guest_count: number;
|
||||||
@@ -35,6 +36,7 @@ const RSVPEditForm: React.FC = () => {
|
|||||||
const { slug, editId } = useParams<{ slug: string; editId: string }>();
|
const { slug, editId } = useParams<{ slug: string; editId: string }>();
|
||||||
const [formData, setFormData] = useState<RSVPFormData>({
|
const [formData, setFormData] = useState<RSVPFormData>({
|
||||||
name: '',
|
name: '',
|
||||||
|
email_address: '',
|
||||||
attending: '',
|
attending: '',
|
||||||
bringing_guests: '',
|
bringing_guests: '',
|
||||||
guest_count: 1,
|
guest_count: 1,
|
||||||
@@ -80,6 +82,7 @@ const RSVPEditForm: React.FC = () => {
|
|||||||
// Pre-fill the form with existing RSVP data
|
// Pre-fill the form with existing RSVP data
|
||||||
setFormData({
|
setFormData({
|
||||||
name: rsvpResponse.data.name,
|
name: rsvpResponse.data.name,
|
||||||
|
email_address: rsvpResponse.data.email_address || '',
|
||||||
attending: rsvpResponse.data.attending,
|
attending: rsvpResponse.data.attending,
|
||||||
bringing_guests: rsvpResponse.data.bringing_guests,
|
bringing_guests: rsvpResponse.data.bringing_guests,
|
||||||
guest_count: rsvpResponse.data.guest_count,
|
guest_count: rsvpResponse.data.guest_count,
|
||||||
@@ -272,7 +275,7 @@ const RSVPEditForm: React.FC = () => {
|
|||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
if (!formData.name.trim() || !formData.attending) {
|
if (!formData.name.trim() || !formData.email_address.trim() || !formData.attending) {
|
||||||
setError('Please fill in all required fields');
|
setError('Please fill in all required fields');
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
return;
|
return;
|
||||||
@@ -431,6 +434,19 @@ const RSVPEditForm: React.FC = () => {
|
|||||||
disabled={isEventClosed} // Disable if event is closed
|
disabled={isEventClosed} // Disable if event is closed
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
label="Email Address"
|
||||||
|
name="email_address"
|
||||||
|
type="email"
|
||||||
|
value={formData.email_address}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
disabled={isEventClosed} // Disable if event is closed
|
||||||
|
helperText="If you change your email, a new confirmation will be sent to the new address"
|
||||||
|
/>
|
||||||
|
|
||||||
<FormControl fullWidth required disabled={isEventClosed}> {/* Disable if event is closed */}
|
<FormControl fullWidth required disabled={isEventClosed}> {/* Disable if event is closed */}
|
||||||
<InputLabel>Are you attending?</InputLabel>
|
<InputLabel>Are you attending?</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
@@ -574,6 +590,7 @@ const RSVPEditForm: React.FC = () => {
|
|||||||
size="large"
|
size="large"
|
||||||
disabled={isSubmitting ||
|
disabled={isSubmitting ||
|
||||||
!formData.name.trim() ||
|
!formData.name.trim() ||
|
||||||
|
!formData.email_address.trim() ||
|
||||||
!formData.attending ||
|
!formData.attending ||
|
||||||
(formData.attending === 'yes' && !formData.bringing_guests) ||
|
(formData.attending === 'yes' && !formData.bringing_guests) ||
|
||||||
(formData.bringing_guests === 'yes' && (formData.guest_count < 1 || formData.guest_names.some(name => !name.trim())))}
|
(formData.bringing_guests === 'yes' && (formData.guest_count < 1 || formData.guest_names.some(name => !name.trim())))}
|
||||||
|
|||||||
Reference in New Issue
Block a user