Add email prompt when users without email register for events
This commit is contained in:
53
EMAIL_PROMPT_ON_REGISTRATION.md
Normal file
53
EMAIL_PROMPT_ON_REGISTRATION.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Email Prompt on Event Registration
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
When users without a registered email address attempt to sign up for an event, they are now prompted to add an email address. This ensures they can receive notifications about event changes or cancellations.
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
### New Component
|
||||||
|
|
||||||
|
**`src/components/event/EmailPromptModal.tsx`**
|
||||||
|
- Modal that appears when a user without an email tries to register for an event
|
||||||
|
- Provides two options:
|
||||||
|
1. "Add Email Address" - Navigates to user profile page
|
||||||
|
2. "Continue Without Email" - Proceeds with registration anyway
|
||||||
|
|
||||||
|
### Modified Files
|
||||||
|
|
||||||
|
**`src/pages/EventsPage.tsx`**
|
||||||
|
- Added email check in `handleRegister` function
|
||||||
|
- Checks if user has `primaryEmailAddress` before registration
|
||||||
|
- Shows EmailPromptModal if no email is found
|
||||||
|
- Added navigation to user profile page for email management
|
||||||
|
- Maintains pending event ID to complete registration after user decision
|
||||||
|
|
||||||
|
**`src/components/event/index.ts`**
|
||||||
|
- Added export for `EmailPromptModal`
|
||||||
|
|
||||||
|
## User Flow
|
||||||
|
|
||||||
|
1. User clicks "Register" or "Quick Register" on an event
|
||||||
|
2. System checks if user has an email address registered
|
||||||
|
3. If no email:
|
||||||
|
- EmailPromptModal appears
|
||||||
|
- User can choose to:
|
||||||
|
- Add email (redirected to profile page)
|
||||||
|
- Continue without email (registration proceeds)
|
||||||
|
4. If email exists:
|
||||||
|
- Registration proceeds normally
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
- Encourages users to add email addresses for better communication
|
||||||
|
- Non-blocking - users can still register without email
|
||||||
|
- Clear messaging about why email is beneficial
|
||||||
|
- Seamless integration with existing registration flow
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
- Uses Clerk's `user.primaryEmailAddress?.emailAddress` to check for email
|
||||||
|
- Modal is shown before API call to register
|
||||||
|
- Pending event ID is stored in state during modal interaction
|
||||||
|
- Registration completes after user makes a choice
|
||||||
57
src/components/event/EmailPromptModal.tsx
Normal file
57
src/components/event/EmailPromptModal.tsx
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
interface EmailPromptModalProps {
|
||||||
|
onClose: () => void;
|
||||||
|
onAddEmail: () => void;
|
||||||
|
onContinue: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EmailPromptModal({ onClose, onAddEmail, onContinue }: EmailPromptModalProps) {
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 overflow-y-auto">
|
||||||
|
<div className="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:p-0">
|
||||||
|
{/* Background overlay */}
|
||||||
|
<div className="fixed inset-0 transition-opacity bg-gray-500 bg-opacity-75" onClick={onClose}></div>
|
||||||
|
|
||||||
|
{/* Modal panel */}
|
||||||
|
<div className="relative inline-block align-bottom bg-white rounded-lg text-left shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-md sm:w-full z-10">
|
||||||
|
<div className="bg-white px-6 pt-6 pb-4">
|
||||||
|
{/* Icon */}
|
||||||
|
<div className="flex items-center justify-center w-12 h-12 mx-auto bg-amber-100 rounded-full mb-4">
|
||||||
|
<svg className="w-6 h-6 text-amber-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="text-center">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900 mb-2">
|
||||||
|
Add an Email Address?
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-600 mb-4">
|
||||||
|
You don't have an email address registered. Adding one will allow you to receive notifications about event changes or cancellations.
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
You'll be directed to your profile page where you can manage your account settings.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<div className="bg-gray-50 px-6 py-4 space-y-2">
|
||||||
|
<button
|
||||||
|
onClick={onAddEmail}
|
||||||
|
className="w-full bg-blue-600 text-white py-2.5 px-4 rounded-lg hover:bg-blue-700 transition-colors font-medium"
|
||||||
|
>
|
||||||
|
Add Email Address
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={onContinue}
|
||||||
|
className="w-full bg-white text-gray-700 py-2.5 px-4 rounded-lg border border-gray-300 hover:bg-gray-50 transition-colors font-medium"
|
||||||
|
>
|
||||||
|
Continue Without Email
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,4 +6,5 @@ export { EventDetailModal } from './EventDetailModal';
|
|||||||
export { EventCompletionForm } from './EventCompletionForm';
|
export { EventCompletionForm } from './EventCompletionForm';
|
||||||
export { AttendeeManagement } from './AttendeeManagement';
|
export { AttendeeManagement } from './AttendeeManagement';
|
||||||
export { CompletedEventDetails } from './CompletedEventDetails';
|
export { CompletedEventDetails } from './CompletedEventDetails';
|
||||||
|
export { EmailPromptModal } from './EmailPromptModal';
|
||||||
export type { EventFilterState } from './EventFilters';
|
export type { EventFilterState } from './EventFilters';
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useUser } from '@clerk/clerk-react';
|
import { useUser } from '@clerk/clerk-react';
|
||||||
import { EventForm, EventList, EventFilters, EventDetailModal } from '@/components/event';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { EventForm, EventList, EventFilters, EventDetailModal, EmailPromptModal } from '@/components/event';
|
||||||
import type { EventFilterState } from '@/components/event';
|
import type { EventFilterState } from '@/components/event';
|
||||||
import { eventService } from '@/services/eventService';
|
import { eventService } from '@/services/eventService';
|
||||||
import type { CleanupEvent } from '@/types';
|
import type { CleanupEvent } from '@/types';
|
||||||
|
|
||||||
export function EventsPage() {
|
export function EventsPage() {
|
||||||
const { isSignedIn, user } = useUser();
|
const { isSignedIn, user } = useUser();
|
||||||
|
const navigate = useNavigate();
|
||||||
const [events, setEvents] = useState<CleanupEvent[]>([]);
|
const [events, setEvents] = useState<CleanupEvent[]>([]);
|
||||||
const [filteredEvents, setFilteredEvents] = useState<CleanupEvent[]>([]);
|
const [filteredEvents, setFilteredEvents] = useState<CleanupEvent[]>([]);
|
||||||
const [filters, setFilters] = useState<EventFilterState>({
|
const [filters, setFilters] = useState<EventFilterState>({
|
||||||
@@ -19,6 +21,8 @@ export function EventsPage() {
|
|||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [showEmailPrompt, setShowEmailPrompt] = useState(false);
|
||||||
|
const [pendingEventId, setPendingEventId] = useState<string | null>(null);
|
||||||
|
|
||||||
// Load events
|
// Load events
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -148,6 +152,18 @@ export function EventsPage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if user has an email address
|
||||||
|
const hasEmail = user?.primaryEmailAddress?.emailAddress;
|
||||||
|
if (!hasEmail) {
|
||||||
|
setPendingEventId(eventId);
|
||||||
|
setShowEmailPrompt(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await performRegistration(eventId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const performRegistration = async (eventId: string) => {
|
||||||
try {
|
try {
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
@@ -162,6 +178,22 @@ export function EventsPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddEmail = () => {
|
||||||
|
setShowEmailPrompt(false);
|
||||||
|
// Navigate to user profile where they can manage their Clerk account
|
||||||
|
if (user?.id) {
|
||||||
|
navigate(`/profile/${user.id}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleContinueWithoutEmail = async () => {
|
||||||
|
setShowEmailPrompt(false);
|
||||||
|
if (pendingEventId) {
|
||||||
|
await performRegistration(pendingEventId);
|
||||||
|
setPendingEventId(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleUnregister = async () => {
|
const handleUnregister = async () => {
|
||||||
if (!selectedEvent) return;
|
if (!selectedEvent) return;
|
||||||
|
|
||||||
@@ -323,6 +355,18 @@ export function EventsPage() {
|
|||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Email Prompt Modal */}
|
||||||
|
{showEmailPrompt && (
|
||||||
|
<EmailPromptModal
|
||||||
|
onClose={() => {
|
||||||
|
setShowEmailPrompt(false);
|
||||||
|
setPendingEventId(null);
|
||||||
|
}}
|
||||||
|
onAddEmail={handleAddEmail}
|
||||||
|
onContinue={handleContinueWithoutEmail}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user