Update registration Step
This commit is contained in:
235
src/components/registration/RegistrationStep1.js
Normal file
235
src/components/registration/RegistrationStep1.js
Normal file
@@ -0,0 +1,235 @@
|
||||
import React from 'react';
|
||||
import { Label } from '../ui/label';
|
||||
import { Input } from '../ui/input';
|
||||
import { Checkbox } from '../ui/checkbox';
|
||||
|
||||
const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
|
||||
const leadSourceOptions = [
|
||||
'Current member',
|
||||
'Friend',
|
||||
'OutSmart Magazine',
|
||||
'Search engine (Google etc.)',
|
||||
"I've known about LOAF for a long time",
|
||||
'Other'
|
||||
];
|
||||
|
||||
const handleLeadSourceChange = (source) => {
|
||||
setFormData(prev => {
|
||||
const sources = prev.lead_sources.includes(source)
|
||||
? prev.lead_sources.filter(s => s !== source)
|
||||
: [...prev.lead_sources, source];
|
||||
return { ...prev, lead_sources: sources };
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* Personal Information */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
Personal Information
|
||||
</h2>
|
||||
|
||||
{/* First Name, Last Name */}
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="first_name">First Name *</Label>
|
||||
<Input
|
||||
id="first_name"
|
||||
name="first_name"
|
||||
required
|
||||
value={formData.first_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="first-name-input"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="last_name">Last Name *</Label>
|
||||
<Input
|
||||
id="last_name"
|
||||
name="last_name"
|
||||
required
|
||||
value={formData.last_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="last-name-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Phone, Date of Birth */}
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="phone">Phone *</Label>
|
||||
<Input
|
||||
id="phone"
|
||||
name="phone"
|
||||
type="tel"
|
||||
required
|
||||
value={formData.phone}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="phone-input"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="date_of_birth">Date of Birth *</Label>
|
||||
<Input
|
||||
id="date_of_birth"
|
||||
name="date_of_birth"
|
||||
type="date"
|
||||
required
|
||||
value={formData.date_of_birth}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="dob-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Address */}
|
||||
<div>
|
||||
<Label htmlFor="address">Address *</Label>
|
||||
<Input
|
||||
id="address"
|
||||
name="address"
|
||||
required
|
||||
value={formData.address}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="address-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* City, State, Zipcode */}
|
||||
<div className="grid md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="city">City *</Label>
|
||||
<Input
|
||||
id="city"
|
||||
name="city"
|
||||
required
|
||||
value={formData.city}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="city-input"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="state">State *</Label>
|
||||
<Input
|
||||
id="state"
|
||||
name="state"
|
||||
required
|
||||
value={formData.state}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="state-input"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="zipcode">Zipcode *</Label>
|
||||
<Input
|
||||
id="zipcode"
|
||||
name="zipcode"
|
||||
required
|
||||
value={formData.zipcode}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="zipcode-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* How Did You Hear About Us */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
How Did You Hear About Us? *
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
{leadSourceOptions.map((source) => (
|
||||
<div key={source} className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={source}
|
||||
checked={formData.lead_sources.includes(source)}
|
||||
onCheckedChange={() => handleLeadSourceChange(source)}
|
||||
data-testid={`lead-source-${source.toLowerCase().replace(/\s+/g, '-')}`}
|
||||
/>
|
||||
<Label htmlFor={source} className="text-base cursor-pointer">
|
||||
{source}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Partner Information */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
Partner Information (Optional)
|
||||
</h2>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="partner_first_name">Partner First Name</Label>
|
||||
<Input
|
||||
id="partner_first_name"
|
||||
name="partner_first_name"
|
||||
value={formData.partner_first_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="partner-first-name-input"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="partner_last_name">Partner Last Name</Label>
|
||||
<Input
|
||||
id="partner_last_name"
|
||||
name="partner_last_name"
|
||||
value={formData.partner_last_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="partner-last-name-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="partner_is_member"
|
||||
name="partner_is_member"
|
||||
checked={formData.partner_is_member}
|
||||
onCheckedChange={(checked) =>
|
||||
setFormData(prev => ({ ...prev, partner_is_member: checked }))
|
||||
}
|
||||
data-testid="partner-is-member-checkbox"
|
||||
/>
|
||||
<Label htmlFor="partner_is_member" className="text-base cursor-pointer">
|
||||
Is your partner already a member?
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="partner_plan_to_become_member"
|
||||
name="partner_plan_to_become_member"
|
||||
checked={formData.partner_plan_to_become_member}
|
||||
onCheckedChange={(checked) =>
|
||||
setFormData(prev => ({ ...prev, partner_plan_to_become_member: checked }))
|
||||
}
|
||||
data-testid="partner-plan-member-checkbox"
|
||||
/>
|
||||
<Label htmlFor="partner_plan_to_become_member" className="text-base cursor-pointer">
|
||||
Does your partner plan to become a member?
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegistrationStep1;
|
||||
186
src/components/registration/RegistrationStep2.js
Normal file
186
src/components/registration/RegistrationStep2.js
Normal file
@@ -0,0 +1,186 @@
|
||||
import React from 'react';
|
||||
import { Label } from '../ui/label';
|
||||
import { Input } from '../ui/input';
|
||||
import { Textarea } from '../ui/textarea';
|
||||
import { Checkbox } from '../ui/checkbox';
|
||||
|
||||
const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
|
||||
const volunteerOptions = [
|
||||
'Welcoming new members at events',
|
||||
'Sending out birthday cards',
|
||||
'Care Team Calls',
|
||||
'Sharing ideas for events',
|
||||
'Researching grants',
|
||||
'Applying for grants',
|
||||
'Assisting with TeatherLOAFers',
|
||||
'Assisting with ActiveLOAFers',
|
||||
'Assisting with weekday Lunch Bunch',
|
||||
'Uploading Photos to the Website',
|
||||
'Assisting with eNewsletter',
|
||||
'Other administrative task'
|
||||
];
|
||||
|
||||
const handleVolunteerChange = (option) => {
|
||||
setFormData(prev => {
|
||||
const interests = prev.volunteer_interests.includes(option)
|
||||
? prev.volunteer_interests.filter(i => i !== option)
|
||||
: [...prev.volunteer_interests, option];
|
||||
return { ...prev, volunteer_interests: interests };
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* Newsletter Publication Preferences */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
Newsletter Publication Preferences *
|
||||
</h2>
|
||||
<p className="text-[#6B708D]">
|
||||
Please check what information may be published in LOAF Newsletter
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="newsletter_publish_name"
|
||||
checked={formData.newsletter_publish_name}
|
||||
onCheckedChange={(checked) =>
|
||||
setFormData(prev => ({ ...prev, newsletter_publish_name: checked }))
|
||||
}
|
||||
/>
|
||||
<Label htmlFor="newsletter_publish_name" className="text-base cursor-pointer">
|
||||
Name
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="newsletter_publish_photo"
|
||||
checked={formData.newsletter_publish_photo}
|
||||
onCheckedChange={(checked) =>
|
||||
setFormData(prev => ({ ...prev, newsletter_publish_photo: checked }))
|
||||
}
|
||||
/>
|
||||
<Label htmlFor="newsletter_publish_photo" className="text-base cursor-pointer">
|
||||
Photo (added later in profile)
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="newsletter_publish_birthday"
|
||||
checked={formData.newsletter_publish_birthday}
|
||||
onCheckedChange={(checked) =>
|
||||
setFormData(prev => ({ ...prev, newsletter_publish_birthday: checked }))
|
||||
}
|
||||
/>
|
||||
<Label htmlFor="newsletter_publish_birthday" className="text-base cursor-pointer">
|
||||
Birthday
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="newsletter_publish_none"
|
||||
checked={formData.newsletter_publish_none}
|
||||
onCheckedChange={(checked) =>
|
||||
setFormData(prev => ({ ...prev, newsletter_publish_none: checked }))
|
||||
}
|
||||
/>
|
||||
<Label htmlFor="newsletter_publish_none" className="text-base cursor-pointer">
|
||||
Do not publish any of my information
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Referral */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
Referral
|
||||
</h2>
|
||||
<div>
|
||||
<Label htmlFor="referred_by_member_name">
|
||||
Name of a LOAF Member who already knows you
|
||||
</Label>
|
||||
<Input
|
||||
id="referred_by_member_name"
|
||||
name="referred_by_member_name"
|
||||
value={formData.referred_by_member_name}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Enter member name or email"
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="referral-input"
|
||||
/>
|
||||
<p className="text-sm text-[#6B708D] mt-2">
|
||||
If referred by a current member, you may skip the event attendance requirement.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Volunteer Interests */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
Volunteer Interests (Optional)
|
||||
</h2>
|
||||
<p className="text-[#6B708D]">
|
||||
I may at some time be interested in volunteering with LOAF in the following ways (training is provided)
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
{volunteerOptions.map((option) => (
|
||||
<div key={option} className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={`volunteer_${option}`}
|
||||
checked={formData.volunteer_interests.includes(option)}
|
||||
onCheckedChange={() => handleVolunteerChange(option)}
|
||||
/>
|
||||
<Label htmlFor={`volunteer_${option}`} className="text-base cursor-pointer">
|
||||
{option}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scholarship Request */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="scholarship_requested"
|
||||
checked={formData.scholarship_requested}
|
||||
onCheckedChange={(checked) =>
|
||||
setFormData(prev => ({ ...prev, scholarship_requested: checked }))
|
||||
}
|
||||
/>
|
||||
<Label htmlFor="scholarship_requested" className="text-base cursor-pointer font-semibold">
|
||||
I am requesting for scholarship
|
||||
</Label>
|
||||
</div>
|
||||
<p className="text-sm text-[#6B708D]">
|
||||
Scholarship information is kept confidential
|
||||
</p>
|
||||
|
||||
{formData.scholarship_requested && (
|
||||
<div className="mt-4">
|
||||
<Label htmlFor="scholarship_reason">
|
||||
Please explain your situation *
|
||||
</Label>
|
||||
<Textarea
|
||||
id="scholarship_reason"
|
||||
name="scholarship_reason"
|
||||
value={formData.scholarship_reason}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Tell us why you're requesting a scholarship..."
|
||||
rows={4}
|
||||
className="rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegistrationStep2;
|
||||
174
src/components/registration/RegistrationStep3.js
Normal file
174
src/components/registration/RegistrationStep3.js
Normal file
@@ -0,0 +1,174 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Label } from '../ui/label';
|
||||
import { Input } from '../ui/input';
|
||||
import { Textarea } from '../ui/textarea';
|
||||
|
||||
const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
|
||||
// Pre-fill directory fields when user opts in
|
||||
useEffect(() => {
|
||||
if (formData.show_in_directory && !formData.directory_email) {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
directory_email: prev.email || '',
|
||||
directory_address: prev.address || '',
|
||||
directory_phone: prev.phone || '',
|
||||
directory_dob: prev.date_of_birth || '',
|
||||
directory_partner_name: prev.partner_first_name && prev.partner_last_name
|
||||
? `${prev.partner_first_name} ${prev.partner_last_name}`
|
||||
: ''
|
||||
}));
|
||||
}
|
||||
}, [formData.show_in_directory]);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
Members Directory
|
||||
</h2>
|
||||
|
||||
<p className="text-[#6B708D]">
|
||||
Would you like to be displayed on our private members directory? (optional and you can change the answer later)
|
||||
</p>
|
||||
|
||||
{/* Directory Opt-in Radio Buttons */}
|
||||
<div className="space-y-3">
|
||||
<div
|
||||
className={`
|
||||
p-4 rounded-xl border-2 cursor-pointer transition-all
|
||||
${formData.show_in_directory
|
||||
? 'border-[#E07A5F] bg-[#E07A5F]/5'
|
||||
: 'border-[#EAE0D5] hover:border-[#6B708D]'
|
||||
}
|
||||
`}
|
||||
onClick={() => setFormData(prev => ({ ...prev, show_in_directory: true }))}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className={`
|
||||
w-5 h-5 rounded-full border-2 flex items-center justify-center
|
||||
${formData.show_in_directory ? 'border-[#E07A5F]' : 'border-[#EAE0D5]'}
|
||||
`}>
|
||||
{formData.show_in_directory && (
|
||||
<div className="w-3 h-3 rounded-full bg-[#E07A5F]" />
|
||||
)}
|
||||
</div>
|
||||
<span className="font-medium text-[#3D405B]">
|
||||
Yes, include me in the Members Directory
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`
|
||||
p-4 rounded-xl border-2 cursor-pointer transition-all
|
||||
${!formData.show_in_directory
|
||||
? 'border-[#E07A5F] bg-[#E07A5F]/5'
|
||||
: 'border-[#EAE0D5] hover:border-[#6B708D]'
|
||||
}
|
||||
`}
|
||||
onClick={() => setFormData(prev => ({ ...prev, show_in_directory: false }))}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className={`
|
||||
w-5 h-5 rounded-full border-2 flex items-center justify-center
|
||||
${!formData.show_in_directory ? 'border-[#E07A5F]' : 'border-[#EAE0D5]'}
|
||||
`}>
|
||||
{!formData.show_in_directory && (
|
||||
<div className="w-3 h-3 rounded-full bg-[#E07A5F]" />
|
||||
)}
|
||||
</div>
|
||||
<span className="font-medium text-[#3D405B]">
|
||||
No, don't include me in the Members Directory
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Conditional Directory Fields */}
|
||||
{formData.show_in_directory && (
|
||||
<div className="space-y-4 mt-6 p-6 bg-[#FDFCF8] rounded-xl border border-[#EAE0D5]">
|
||||
<p className="text-[#6B708D] text-sm">
|
||||
Below, choose what information you would like include in the Members Only Directory.
|
||||
(If you ever want to update this information, remember the Directory Section and Account Section are separate)
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="directory_email">Your preferred contact email</Label>
|
||||
<Input
|
||||
id="directory_email"
|
||||
name="directory_email"
|
||||
type="email"
|
||||
value={formData.directory_email}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="directory_bio">Bio for my Member Profile</Label>
|
||||
<Textarea
|
||||
id="directory_bio"
|
||||
name="directory_bio"
|
||||
value={formData.directory_bio}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Tell other members about yourself..."
|
||||
rows={4}
|
||||
className="rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="directory_address">Preferred address</Label>
|
||||
<Input
|
||||
id="directory_address"
|
||||
name="directory_address"
|
||||
value={formData.directory_address}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="directory_phone">Preferred contact phone number</Label>
|
||||
<Input
|
||||
id="directory_phone"
|
||||
name="directory_phone"
|
||||
type="tel"
|
||||
value={formData.directory_phone}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="directory_dob">Date of birth</Label>
|
||||
<Input
|
||||
id="directory_dob"
|
||||
name="directory_dob"
|
||||
type="date"
|
||||
value={formData.directory_dob}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="directory_partner_name">
|
||||
Enter your partner's name as you'd like it to appear in the Members Directory
|
||||
</Label>
|
||||
<Input
|
||||
id="directory_partner_name"
|
||||
name="directory_partner_name"
|
||||
value={formData.directory_partner_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegistrationStep3;
|
||||
78
src/components/registration/RegistrationStep4.js
Normal file
78
src/components/registration/RegistrationStep4.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import { Label } from '../ui/label';
|
||||
import { Input } from '../ui/input';
|
||||
|
||||
const RegistrationStep4 = ({ formData, handleInputChange }) => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
Account Credentials
|
||||
</h2>
|
||||
|
||||
<p className="text-[#6B708D]">
|
||||
Your email is also your username that you can use to login.
|
||||
Please note you can only login after your application is approved.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="email">Email *</Label>
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
required
|
||||
value={formData.email}
|
||||
onChange={handleInputChange}
|
||||
placeholder="your.email@example.com"
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="email-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="password">Password *</Label>
|
||||
<Input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
required
|
||||
minLength={6}
|
||||
value={formData.password}
|
||||
onChange={handleInputChange}
|
||||
placeholder="At least 6 characters"
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="password-input"
|
||||
/>
|
||||
<p className="text-sm text-[#6B708D] mt-2">
|
||||
Must be at least 6 characters long
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="confirmPassword">Repeat Password *</Label>
|
||||
<Input
|
||||
id="confirmPassword"
|
||||
name="confirmPassword"
|
||||
type="password"
|
||||
required
|
||||
value={formData.confirmPassword}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Re-enter your password"
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="confirm-password-input"
|
||||
/>
|
||||
{formData.confirmPassword && formData.password !== formData.confirmPassword && (
|
||||
<p className="text-sm text-red-500 mt-2">
|
||||
Passwords do not match
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegistrationStep4;
|
||||
62
src/components/registration/RegistrationStepIndicator.js
Normal file
62
src/components/registration/RegistrationStepIndicator.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
|
||||
const RegistrationStepIndicator = ({ currentStep, totalSteps = 4 }) => {
|
||||
const steps = [
|
||||
{ number: 1, title: 'Personal Info' },
|
||||
{ number: 2, title: 'Preferences' },
|
||||
{ number: 3, title: 'Directory' },
|
||||
{ number: 4, title: 'Account' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="mb-8">
|
||||
{/* Progress Bar */}
|
||||
<div className="flex items-center justify-between relative">
|
||||
{steps.map((step, index) => (
|
||||
<React.Fragment key={step.number}>
|
||||
{/* Step Circle */}
|
||||
<div className="flex flex-col items-center relative z-10">
|
||||
<div className={`
|
||||
w-12 h-12 rounded-full flex items-center justify-center font-semibold text-lg
|
||||
transition-all duration-300
|
||||
${currentStep === step.number
|
||||
? 'bg-[#E07A5F] text-white scale-110 shadow-lg'
|
||||
: currentStep > step.number
|
||||
? 'bg-[#81B29A] text-white'
|
||||
: 'bg-[#EAE0D5] text-[#6B708D]'
|
||||
}
|
||||
`}>
|
||||
{currentStep > step.number ? '✓' : step.number}
|
||||
</div>
|
||||
<span className={`
|
||||
text-sm mt-2 font-medium transition-colors
|
||||
${currentStep === step.number ? 'text-[#E07A5F]' : 'text-[#6B708D]'}
|
||||
`}>
|
||||
{step.title}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Connecting Line */}
|
||||
{index < steps.length - 1 && (
|
||||
<div className="flex-1 h-1 mx-2 relative -top-6 bg-[#EAE0D5]">
|
||||
<div
|
||||
className={`
|
||||
h-full transition-all duration-500
|
||||
${currentStep > step.number ? 'bg-[#81B29A] w-full' : 'bg-transparent w-0'}
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Step Counter */}
|
||||
<p className="text-center text-[#6B708D] mt-6 text-lg">
|
||||
Step <span className="font-semibold text-[#E07A5F]">{currentStep}</span> of {totalSteps}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegistrationStepIndicator;
|
||||
@@ -2,45 +2,61 @@ import React, { useState } from 'react';
|
||||
import { useNavigate, Link } from 'react-router-dom';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import { Button } from '../components/ui/button';
|
||||
import { Input } from '../components/ui/input';
|
||||
import { Label } from '../components/ui/label';
|
||||
import { Card } from '../components/ui/card';
|
||||
import { Checkbox } from '../components/ui/checkbox';
|
||||
import { toast } from 'sonner';
|
||||
import Navbar from '../components/Navbar';
|
||||
import { ArrowRight, ArrowLeft } from 'lucide-react';
|
||||
import RegistrationStepIndicator from '../components/registration/RegistrationStepIndicator';
|
||||
import RegistrationStep1 from '../components/registration/RegistrationStep1';
|
||||
import RegistrationStep2 from '../components/registration/RegistrationStep2';
|
||||
import RegistrationStep3 from '../components/registration/RegistrationStep3';
|
||||
import RegistrationStep4 from '../components/registration/RegistrationStep4';
|
||||
|
||||
const Register = () => {
|
||||
const navigate = useNavigate();
|
||||
const { register } = useAuth();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [currentStep, setCurrentStep] = useState(1);
|
||||
const [formData, setFormData] = useState({
|
||||
email: '',
|
||||
password: '',
|
||||
// Step 1: Personal & Partner Information
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
phone: '',
|
||||
date_of_birth: '',
|
||||
address: '',
|
||||
city: '',
|
||||
state: '',
|
||||
zipcode: '',
|
||||
date_of_birth: '',
|
||||
lead_sources: [],
|
||||
partner_first_name: '',
|
||||
partner_last_name: '',
|
||||
partner_is_member: false,
|
||||
partner_plan_to_become_member: false,
|
||||
referred_by_member_name: ''
|
||||
});
|
||||
|
||||
const leadSourceOptions = [
|
||||
'Current member',
|
||||
'Friend',
|
||||
'OutSmart Magazine',
|
||||
'Search engine (Google etc.)',
|
||||
"I've known about LOAF for a long time",
|
||||
'Other'
|
||||
];
|
||||
// Step 2: Newsletter, Volunteer & Scholarship
|
||||
referred_by_member_name: '',
|
||||
newsletter_publish_name: false,
|
||||
newsletter_publish_photo: false,
|
||||
newsletter_publish_birthday: false,
|
||||
newsletter_publish_none: false,
|
||||
volunteer_interests: [],
|
||||
scholarship_requested: false,
|
||||
scholarship_reason: '',
|
||||
|
||||
// Step 3: Directory Settings
|
||||
show_in_directory: false,
|
||||
directory_email: '',
|
||||
directory_bio: '',
|
||||
directory_address: '',
|
||||
directory_phone: '',
|
||||
directory_dob: '',
|
||||
directory_partner_name: '',
|
||||
|
||||
// Step 4: Account Credentials
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value, type, checked } = e.target;
|
||||
@@ -50,28 +66,113 @@ const Register = () => {
|
||||
}));
|
||||
};
|
||||
|
||||
const handleLeadSourceChange = (source) => {
|
||||
setFormData(prev => {
|
||||
const sources = prev.lead_sources.includes(source)
|
||||
? prev.lead_sources.filter(s => s !== source)
|
||||
: [...prev.lead_sources, source];
|
||||
return { ...prev, lead_sources: sources };
|
||||
});
|
||||
const validateStep1 = () => {
|
||||
const required = ['first_name', 'last_name', 'phone', 'date_of_birth',
|
||||
'address', 'city', 'state', 'zipcode'];
|
||||
for (const field of required) {
|
||||
if (!formData[field]?.trim()) {
|
||||
toast.error('Please fill in all required fields');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (formData.lead_sources.length === 0) {
|
||||
toast.error('Please select at least one option for how you heard about us');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const validateStep2 = () => {
|
||||
const { newsletter_publish_name, newsletter_publish_photo,
|
||||
newsletter_publish_birthday, newsletter_publish_none } = formData;
|
||||
|
||||
if (!newsletter_publish_name && !newsletter_publish_photo &&
|
||||
!newsletter_publish_birthday && !newsletter_publish_none) {
|
||||
toast.error('Please select at least one newsletter publication preference');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (formData.scholarship_requested && !formData.scholarship_reason?.trim()) {
|
||||
toast.error('Please explain your scholarship request');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const validateStep3 = () => {
|
||||
return true; // No required fields
|
||||
};
|
||||
|
||||
const validateStep4 = () => {
|
||||
if (!formData.email || !formData.password || !formData.confirmPassword) {
|
||||
toast.error('Please fill in all account fields');
|
||||
return false;
|
||||
}
|
||||
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(formData.email)) {
|
||||
toast.error('Please enter a valid email address');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (formData.password.length < 6) {
|
||||
toast.error('Password must be at least 6 characters');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (formData.password !== formData.confirmPassword) {
|
||||
toast.error('Passwords do not match');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
let isValid = false;
|
||||
|
||||
switch (currentStep) {
|
||||
case 1: isValid = validateStep1(); break;
|
||||
case 2: isValid = validateStep2(); break;
|
||||
case 3: isValid = validateStep3(); break;
|
||||
default: isValid = false;
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
setCurrentStep(prev => Math.min(prev + 1, 4));
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
setCurrentStep(prev => Math.max(prev - 1, 1));
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Final validation
|
||||
if (!validateStep4()) return;
|
||||
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
// Convert date to ISO format
|
||||
const dataToSubmit = {
|
||||
...formData,
|
||||
date_of_birth: new Date(formData.date_of_birth).toISOString()
|
||||
// Remove confirmPassword (client-side only)
|
||||
const { confirmPassword, ...dataToSubmit } = formData;
|
||||
|
||||
// Convert date fields to ISO format
|
||||
const submitData = {
|
||||
...dataToSubmit,
|
||||
date_of_birth: new Date(dataToSubmit.date_of_birth).toISOString(),
|
||||
directory_dob: dataToSubmit.directory_dob
|
||||
? new Date(dataToSubmit.directory_dob).toISOString()
|
||||
: null
|
||||
};
|
||||
|
||||
await register(dataToSubmit);
|
||||
toast.success('Registration successful! Please check your email to verify your account.');
|
||||
await register(submitData);
|
||||
toast.success('Please check your email for a confirmation email.');
|
||||
navigate('/login');
|
||||
} catch (error) {
|
||||
toast.error(error.response?.data?.detail || 'Registration failed. Please try again.');
|
||||
@@ -103,281 +204,83 @@ const Register = () => {
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-8" data-testid="register-form">
|
||||
{/* Account Information */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
Account Information
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="email">Email *</Label>
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
required
|
||||
value={formData.email}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="email-input"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="password">Password *</Label>
|
||||
<Input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
required
|
||||
minLength={6}
|
||||
value={formData.password}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="password-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<RegistrationStepIndicator currentStep={currentStep} />
|
||||
|
||||
{currentStep === 1 && (
|
||||
<RegistrationStep1
|
||||
formData={formData}
|
||||
setFormData={setFormData}
|
||||
handleInputChange={handleInputChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentStep === 2 && (
|
||||
<RegistrationStep2
|
||||
formData={formData}
|
||||
setFormData={setFormData}
|
||||
handleInputChange={handleInputChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentStep === 3 && (
|
||||
<RegistrationStep3
|
||||
formData={formData}
|
||||
setFormData={setFormData}
|
||||
handleInputChange={handleInputChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentStep === 4 && (
|
||||
<RegistrationStep4
|
||||
formData={formData}
|
||||
handleInputChange={handleInputChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Navigation Buttons */}
|
||||
<div className="flex justify-between items-center pt-6">
|
||||
{currentStep > 1 ? (
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleBack}
|
||||
variant="outline"
|
||||
className="rounded-full px-6 py-6 text-lg border-2 border-[#EAE0D5] hover:border-[#6B708D] text-[#3D405B]"
|
||||
>
|
||||
<ArrowLeft className="mr-2 h-5 w-5" />
|
||||
Back
|
||||
</Button>
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
|
||||
{currentStep < 4 ? (
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleNext}
|
||||
className="bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full px-6 py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform"
|
||||
>
|
||||
Next
|
||||
<ArrowRight className="ml-2 h-5 w-5" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full px-6 py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
data-testid="submit-register-button"
|
||||
>
|
||||
{loading ? 'Creating Account...' : 'Create Account'}
|
||||
<ArrowRight className="ml-2 h-5 w-5" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Personal Information */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
Personal Information
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="first_name">First Name *</Label>
|
||||
<Input
|
||||
id="first_name"
|
||||
name="first_name"
|
||||
required
|
||||
value={formData.first_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="first-name-input"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="last_name">Last Name *</Label>
|
||||
<Input
|
||||
id="last_name"
|
||||
name="last_name"
|
||||
required
|
||||
value={formData.last_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="last-name-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="phone">Phone *</Label>
|
||||
<Input
|
||||
id="phone"
|
||||
name="phone"
|
||||
type="tel"
|
||||
required
|
||||
value={formData.phone}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="phone-input"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="date_of_birth">Date of Birth *</Label>
|
||||
<Input
|
||||
id="date_of_birth"
|
||||
name="date_of_birth"
|
||||
type="date"
|
||||
required
|
||||
value={formData.date_of_birth}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="dob-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="address">Address *</Label>
|
||||
<Input
|
||||
id="address"
|
||||
name="address"
|
||||
required
|
||||
value={formData.address}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="address-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="city">City *</Label>
|
||||
<Input
|
||||
id="city"
|
||||
name="city"
|
||||
required
|
||||
value={formData.city}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="city-input"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="state">State *</Label>
|
||||
<Input
|
||||
id="state"
|
||||
name="state"
|
||||
required
|
||||
value={formData.state}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="state-input"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="zipcode">Zipcode *</Label>
|
||||
<Input
|
||||
id="zipcode"
|
||||
name="zipcode"
|
||||
required
|
||||
value={formData.zipcode}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="zipcode-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* How Did You Hear About Us */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
How Did You Hear About Us? *
|
||||
</h2>
|
||||
<div className="space-y-3">
|
||||
{leadSourceOptions.map((source) => (
|
||||
<div key={source} className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={source}
|
||||
checked={formData.lead_sources.includes(source)}
|
||||
onCheckedChange={() => handleLeadSourceChange(source)}
|
||||
data-testid={`lead-source-${source.toLowerCase().replace(/\s+/g, '-')}`}
|
||||
/>
|
||||
<Label htmlFor={source} className="text-base cursor-pointer">
|
||||
{source}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Partner Information */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
Partner Information (Optional)
|
||||
</h2>
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="partner_first_name">Partner First Name</Label>
|
||||
<Input
|
||||
id="partner_first_name"
|
||||
name="partner_first_name"
|
||||
value={formData.partner_first_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="partner-first-name-input"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="partner_last_name">Partner Last Name</Label>
|
||||
<Input
|
||||
id="partner_last_name"
|
||||
name="partner_last_name"
|
||||
value={formData.partner_last_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="partner-last-name-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="partner_is_member"
|
||||
name="partner_is_member"
|
||||
checked={formData.partner_is_member}
|
||||
onCheckedChange={(checked) =>
|
||||
setFormData(prev => ({ ...prev, partner_is_member: checked }))
|
||||
}
|
||||
data-testid="partner-is-member-checkbox"
|
||||
/>
|
||||
<Label htmlFor="partner_is_member" className="text-base cursor-pointer">
|
||||
Is your partner already a member?
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="partner_plan_to_become_member"
|
||||
name="partner_plan_to_become_member"
|
||||
checked={formData.partner_plan_to_become_member}
|
||||
onCheckedChange={(checked) =>
|
||||
setFormData(prev => ({ ...prev, partner_plan_to_become_member: checked }))
|
||||
}
|
||||
data-testid="partner-plan-member-checkbox"
|
||||
/>
|
||||
<Label htmlFor="partner_plan_to_become_member" className="text-base cursor-pointer">
|
||||
Does your partner plan to become a member?
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Referral */}
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]">
|
||||
Referral (Optional)
|
||||
</h2>
|
||||
<div>
|
||||
<Label htmlFor="referred_by_member_name">Referred by Member (Name or Email)</Label>
|
||||
<Input
|
||||
id="referred_by_member_name"
|
||||
name="referred_by_member_name"
|
||||
value={formData.referred_by_member_name}
|
||||
onChange={handleInputChange}
|
||||
placeholder="If a current member referred you, enter their name or email"
|
||||
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]"
|
||||
data-testid="referral-input"
|
||||
/>
|
||||
<p className="text-sm text-[#6B708D] mt-2">
|
||||
If referred by a current member, you may skip the event attendance requirement.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="pt-6">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={loading || formData.lead_sources.length === 0}
|
||||
className="w-full bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
data-testid="submit-register-button"
|
||||
>
|
||||
{loading ? 'Creating Account...' : 'Create Account'}
|
||||
<ArrowRight className="ml-2 h-5 w-5" />
|
||||
</Button>
|
||||
<p className="text-center text-[#6B708D] mt-4">
|
||||
Already have an account?{' '}
|
||||
<Link to="/login" className="text-[#E07A5F] hover:underline font-medium">
|
||||
Login here
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-center text-[#6B708D] mt-4">
|
||||
Already have an account?{' '}
|
||||
<Link to="/login" className="text-[#E07A5F] hover:underline font-medium">
|
||||
Login here
|
||||
</Link>
|
||||
</p>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user