443 lines
22 KiB
TypeScript
443 lines
22 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { Button } from '../ui/button';
|
|
import { Input } from '../ui/input';
|
|
import { Textarea } from '../ui/textarea';
|
|
import { Checkbox } from '../ui/checkbox';
|
|
import { LogIn, ChevronRight, ChevronDown } from 'lucide-react';
|
|
import { toast } from 'sonner';
|
|
import { onboardingService } from '../../services/onboarding.service';
|
|
import { masterService } from '../../services/master.service';
|
|
|
|
interface ApplicationFormPageProps {
|
|
onAdminLogin: () => void;
|
|
}
|
|
|
|
export function ApplicationFormPage({ onAdminLogin }: ApplicationFormPageProps) {
|
|
const [formData, setFormData] = useState({
|
|
country: 'India',
|
|
stateId: '',
|
|
districtId: '',
|
|
name: '',
|
|
interestedCity: '',
|
|
email: '',
|
|
pincode: '',
|
|
mobile: '',
|
|
ownRoyalEnfield: '',
|
|
royalEnfieldModel: '',
|
|
age: '',
|
|
education: '',
|
|
companyName: '',
|
|
source: '',
|
|
existingDealer: '',
|
|
description: '',
|
|
address: '',
|
|
acceptTerms: false
|
|
});
|
|
|
|
const [otpVerified, setOtpVerified] = useState(false);
|
|
const [states, setStates] = useState<any[]>([]);
|
|
const [districts, setDistricts] = useState<any[]>([]);
|
|
|
|
useEffect(() => {
|
|
fetchStates();
|
|
}, []);
|
|
|
|
const fetchStates = async () => {
|
|
try {
|
|
const response: any = await masterService.getStates();
|
|
const statesArray = Array.isArray(response) ? response : (response?.data || response?.states || []);
|
|
setStates(statesArray);
|
|
} catch (error) {
|
|
console.error('Error fetching states:', error);
|
|
}
|
|
};
|
|
|
|
const handleStateChange = async (stateId: string) => {
|
|
if (!stateId) return;
|
|
setFormData(prev => ({ ...prev, stateId, districtId: '' }));
|
|
setDistricts([]);
|
|
try {
|
|
const response: any = await masterService.getDistricts(stateId);
|
|
const districtsArray = Array.isArray(response) ? response : (response?.data || response?.districts || []);
|
|
setDistricts(districtsArray);
|
|
} catch (error) {
|
|
console.error('Error fetching districts:', error);
|
|
}
|
|
};
|
|
|
|
const handleVerifyMobile = () => {
|
|
if (!formData.mobile || formData.mobile.length < 10) {
|
|
toast.error('Please enter a valid mobile number');
|
|
return;
|
|
}
|
|
toast.success('OTP sent to ' + formData.mobile);
|
|
setTimeout(() => {
|
|
setOtpVerified(true);
|
|
toast.success('Mobile number verified');
|
|
}, 1500);
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (!formData.country || !formData.stateId || !formData.districtId || !formData.name ||
|
|
!formData.interestedCity || !formData.email || !formData.pincode || !formData.mobile ||
|
|
!formData.ownRoyalEnfield || !formData.age || !formData.education ||
|
|
!formData.companyName || !formData.source || !formData.existingDealer ||
|
|
!formData.description || !formData.address) {
|
|
toast.error('Please fill in all required fields');
|
|
return;
|
|
}
|
|
|
|
if (!otpVerified) {
|
|
toast.error('Please verify your mobile number');
|
|
return;
|
|
}
|
|
|
|
if (!formData.acceptTerms) {
|
|
toast.error('Please accept the terms and conditions');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const selectedState = states.find((s: any) => s.id === formData.stateId);
|
|
const selectedDistrict = districts.find((d: any) => d.id === formData.districtId);
|
|
const stateName = selectedState?.name || selectedState?.stateName || '';
|
|
const districtName = selectedDistrict?.name || selectedDistrict?.districtName || '';
|
|
|
|
const payload = {
|
|
applicantName: formData.name,
|
|
email: formData.email,
|
|
phone: formData.mobile,
|
|
state: stateName,
|
|
city: formData.interestedCity,
|
|
district: districtName,
|
|
preferredLocation: `${formData.interestedCity}, ${stateName}`,
|
|
businessType: 'Dealership',
|
|
locationType: 'district',
|
|
locationId: formData.districtId,
|
|
address: formData.address,
|
|
pincode: formData.pincode,
|
|
age: formData.age,
|
|
education: formData.education,
|
|
companyName: formData.companyName,
|
|
source: formData.source,
|
|
existingDealer: formData.existingDealer === 'yes',
|
|
ownRoyalEnfield: formData.ownRoyalEnfield === 'yes',
|
|
royalEnfieldModel: formData.royalEnfieldModel,
|
|
description: formData.description,
|
|
experienceYears: 0,
|
|
investmentCapacity: 'Unknown'
|
|
};
|
|
|
|
await onboardingService.submitApplication(payload);
|
|
toast.success('Application submitted successfully');
|
|
|
|
setFormData({
|
|
country: 'India', stateId: '', districtId: '', name: '', interestedCity: '',
|
|
email: '', pincode: '', mobile: '', ownRoyalEnfield: '', royalEnfieldModel: '',
|
|
age: '', education: '', companyName: '', source: '', existingDealer: '',
|
|
description: '', address: '', acceptTerms: false
|
|
});
|
|
setOtpVerified(false);
|
|
} catch (error: any) {
|
|
toast.error(error.response?.data?.message || 'Failed to submit application.');
|
|
}
|
|
};
|
|
|
|
const reModels = [
|
|
"Classic 650", "Scram 440", "Goan Classic 350", "Bear 650", "Guerrilla 450",
|
|
"Shotgun 650", "Himalayan 450", "Bullet 350", "Super Meteor 650", "Hunter 350",
|
|
"Scram 411", "Meteor 350", "Interceptor INT 650", "Continental GT 650",
|
|
"Classic 350", "Other Royal Enfield motorcycle"
|
|
];
|
|
|
|
const sourceOptions = [
|
|
"Existing RE dealer", "Customer", "RE Employee", "News Paper", "Website", "Friends", "Others"
|
|
];
|
|
|
|
return (
|
|
<div className="min-h-screen relative flex flex-col">
|
|
{/* Background Image Wrapper */}
|
|
<div className="fixed inset-0 z-0">
|
|
<img
|
|
src="/assets/images/become_a_dealer.webp"
|
|
alt="Royal Enfield Background"
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
<div className="absolute inset-0 bg-black/20" />
|
|
</div>
|
|
|
|
{/* Navigation Header */}
|
|
<nav className="relative z-10 bg-black px-6 md:px-12 py-4 flex items-center justify-between border-b border-white/10">
|
|
<img src="/assets/images/Re_Logo.png" alt="Royal Enfield" className="h-10 md:h-12 w-auto" />
|
|
<Button variant="ghost" onClick={onAdminLogin} className="text-[10px] uppercase tracking-widest font-bold text-slate-400 hover:text-white transition-colors">
|
|
<LogIn className="w-3.5 h-3.5 mr-2" />
|
|
Login
|
|
</Button>
|
|
</nav>
|
|
|
|
{/* Main Content Area over the background */}
|
|
<main className="relative z-10 flex-grow flex flex-col items-center pt-24 pb-24 px-6">
|
|
{/* Content Container with white background */}
|
|
<div className="w-full max-w-[1240px] bg-white shadow-2xl p-8 md:p-16">
|
|
|
|
{/* Introductory Text */}
|
|
<section className="text-[#333333] mb-12">
|
|
<h1 className="text-[28px] font-bold mb-8 uppercase tracking-wide text-black">Become a Dealer</h1>
|
|
<div className="space-y-6 text-[15px] leading-relaxed">
|
|
<p>
|
|
At Royal Enfield, we endeavour to partner our patrons and customers in their journeys of exploration. Our main objective is to provide an immersive brand and retail experience in addition to ensuring a superior product experience. Our dealers, distributors and network teams are our extended partners in this task.
|
|
</p>
|
|
<p>
|
|
Royal Enfield employs a comprehensive and professional process for Dealership allotment. Dealer selection is done based on a variety of criteria including a personal meeting with the applicant. It is only post completion of the evaluation and selection process, that other formalities are considered.
|
|
</p>
|
|
<p>
|
|
Royal Enfield does not accept or demand money / deposits from prospective partners prior to processing the application or candidature of the Dealership.
|
|
</p>
|
|
<p>
|
|
If you receive any communication offering allotment of Royal Enfield Dealership against payment / transfer of money and / or otherwise, we advise you to seek information / clarifications by writing to us at <b><a href="mailto:support@royalenfield.com" className="text-red-600">support@royalenfield.com</a></b> or by contacting us on our customer care toll-free number <b><a href="tel:18002100008" className="text-red-600">1800 210 0008</a></b>.
|
|
</p>
|
|
<p className="font-bold py-1 mt-8 text-black text-[15px]">
|
|
*This is the ONLY official website and central number for dealership enquiries for Royal Enfield and we do not have any other partner website(s).
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Form */}
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
{/* Country Selection */}
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
<div className="relative">
|
|
<select
|
|
className="w-full h-[44px] px-4 border border-[#cccccc] appearance-none bg-white text-[14px] outline-none"
|
|
value={formData.country}
|
|
onChange={(e) => setFormData({...formData, country: e.target.value})}
|
|
>
|
|
<option value="India">India</option>
|
|
<option value="Other">Others</option>
|
|
</select>
|
|
<ChevronDown className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500 pointer-events-none" />
|
|
</div>
|
|
<div className="relative">
|
|
<select
|
|
className="w-full h-[44px] px-4 border border-[#cccccc] appearance-none bg-white text-[14px] outline-none"
|
|
value={formData.stateId}
|
|
onChange={(e) => handleStateChange(e.target.value)}
|
|
>
|
|
<option value="">Select State*</option>
|
|
{states.map(s => <option key={s.id} value={s.id}>{s.name || s.stateName}</option>)}
|
|
</select>
|
|
<ChevronDown className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500 pointer-events-none" />
|
|
</div>
|
|
<div className="relative">
|
|
<select
|
|
className="w-full h-[44px] px-4 border border-[#cccccc] appearance-none bg-white text-[14px] outline-none"
|
|
value={formData.districtId}
|
|
onChange={(e) => setFormData({...formData, districtId: e.target.value})}
|
|
>
|
|
<option value="">Select District*</option>
|
|
{districts.map(d => <option key={d.id} value={d.id}>{d.name || d.districtName}</option>)}
|
|
</select>
|
|
<ChevronDown className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500 pointer-events-none" />
|
|
</div>
|
|
</div>
|
|
|
|
{/* Personal Details - Two Column */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-10 gap-y-6">
|
|
<Input
|
|
placeholder="Name*"
|
|
className="h-[44px] border-[#cccccc] rounded-none px-4 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999]"
|
|
value={formData.name}
|
|
onChange={(e) => setFormData({...formData, name: e.target.value})}
|
|
/>
|
|
<Input
|
|
placeholder="Interested city for Dealership*"
|
|
className="h-[44px] border-[#cccccc] rounded-none px-4 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999]"
|
|
value={formData.interestedCity}
|
|
onChange={(e) => setFormData({...formData, interestedCity: e.target.value})}
|
|
/>
|
|
<Input
|
|
placeholder="Email Id*"
|
|
className="h-[44px] border-[#cccccc] rounded-none px-4 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999]"
|
|
value={formData.email}
|
|
onChange={(e) => setFormData({...formData, email: e.target.value})}
|
|
/>
|
|
<Input
|
|
placeholder="Pincode*"
|
|
className="h-[44px] border-[#cccccc] rounded-none px-4 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999]"
|
|
value={formData.pincode}
|
|
onChange={(e) => setFormData({...formData, pincode: e.target.value})}
|
|
/>
|
|
|
|
<div className="relative">
|
|
<Input
|
|
placeholder="Mobile No.*"
|
|
className="h-[44px] border-[#cccccc] rounded-none px-4 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999]"
|
|
value={formData.mobile}
|
|
onChange={(e) => setFormData({...formData, mobile: e.target.value})}
|
|
/>
|
|
{!otpVerified ? (
|
|
<button
|
|
type="button"
|
|
onClick={handleVerifyMobile}
|
|
className="absolute right-4 top-1/2 -translate-y-1/2 text-[12px] font-bold text-red-600 hover:text-black transition-colors"
|
|
>
|
|
Verify
|
|
</button>
|
|
) : (
|
|
<span className="absolute right-4 top-1/2 -translate-y-1/2 text-[12px] font-bold text-green-600">Verified</span>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-10 h-[44px]">
|
|
<span className="text-[14px] font-medium text-[#333333]">Own a Royal Enfield?</span>
|
|
<div className="flex gap-8">
|
|
{['yes', 'no'].map(val => (
|
|
<label key={val} className="flex items-center gap-2 cursor-pointer">
|
|
<div className={`w-5 h-5 rounded-full border flex items-center justify-center ${formData.ownRoyalEnfield === val ? 'border-red-600' : 'border-[#cccccc]'}`}>
|
|
{formData.ownRoyalEnfield === val && <div className="w-2.5 h-2.5 rounded-full bg-red-600" />}
|
|
</div>
|
|
<input
|
|
type="radio"
|
|
className="hidden"
|
|
checked={formData.ownRoyalEnfield === val}
|
|
onChange={() => setFormData({...formData, ownRoyalEnfield: val})}
|
|
/>
|
|
<span className="text-[14px] capitalize">{val}</span>
|
|
</label>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<Input
|
|
placeholder="Age*"
|
|
className="h-[44px] border-[#cccccc] rounded-none px-4 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999]"
|
|
value={formData.age}
|
|
onChange={(e) => setFormData({...formData, age: e.target.value})}
|
|
/>
|
|
|
|
<div className="relative">
|
|
<select
|
|
className="w-full h-[44px] px-4 border border-[#cccccc] appearance-none bg-white text-[14px] outline-none disabled:bg-slate-50"
|
|
value={formData.royalEnfieldModel}
|
|
disabled={formData.ownRoyalEnfield !== 'yes'}
|
|
onChange={(e) => setFormData({...formData, royalEnfieldModel: e.target.value})}
|
|
>
|
|
<option value="">Motorcycle Owned</option>
|
|
{reModels.map(m => <option key={m} value={m}>{m}</option>)}
|
|
</select>
|
|
<ChevronDown className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500 pointer-events-none" />
|
|
</div>
|
|
|
|
<Input
|
|
placeholder="Education Qualification*"
|
|
className="h-[44px] border-[#cccccc] rounded-none px-4 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999]"
|
|
value={formData.education}
|
|
onChange={(e) => setFormData({...formData, education: e.target.value})}
|
|
/>
|
|
|
|
<Input
|
|
placeholder="Company Name*"
|
|
className="h-[44px] border-[#cccccc] rounded-none px-4 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999]"
|
|
value={formData.companyName}
|
|
onChange={(e) => setFormData({...formData, companyName: e.target.value})}
|
|
/>
|
|
|
|
<div className="relative">
|
|
<select
|
|
className="w-full h-[44px] px-4 border border-[#cccccc] appearance-none bg-white text-[14px] outline-none"
|
|
value={formData.source}
|
|
onChange={(e) => setFormData({...formData, source: e.target.value})}
|
|
>
|
|
<option value="">Select Source</option>
|
|
{sourceOptions.map(s => <option key={s} value={s}>{s}</option>)}
|
|
</select>
|
|
<ChevronDown className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500 pointer-events-none" />
|
|
</div>
|
|
|
|
<div className="flex flex-col justify-center h-auto min-h-[44px] space-y-1">
|
|
<span className="text-[13px] font-medium text-[#333333]">Are you an existing Dealer / Vendor of Royal Enfield?</span>
|
|
<div className="flex gap-8">
|
|
{['yes', 'no'].map(val => (
|
|
<label key={val} className="flex items-center gap-2 cursor-pointer">
|
|
<div className={`w-5 h-5 rounded-full border flex items-center justify-center ${formData.existingDealer === val ? 'border-red-600' : 'border-[#cccccc]'}`}>
|
|
{formData.existingDealer === val && <div className="w-2.5 h-2.5 rounded-full bg-red-600" />}
|
|
</div>
|
|
<input
|
|
type="radio"
|
|
className="hidden"
|
|
checked={formData.existingDealer === val}
|
|
onChange={() => setFormData({...formData, existingDealer: val})}
|
|
/>
|
|
<span className="text-[14px] capitalize">{val}</span>
|
|
</label>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Full Width Text Areas */}
|
|
<div className="space-y-6 pt-4">
|
|
<Textarea
|
|
placeholder="Description*"
|
|
className="min-h-[120px] border-[#cccccc] rounded-none px-4 py-3 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999] resize-none"
|
|
value={formData.description}
|
|
onChange={(e) => setFormData({...formData, description: e.target.value})}
|
|
/>
|
|
<Textarea
|
|
placeholder="Address*"
|
|
className="min-h-[120px] border-[#cccccc] rounded-none px-4 py-3 text-[14px] focus-visible:ring-1 focus-visible:ring-black placeholder:text-[#999999] resize-none"
|
|
value={formData.address}
|
|
onChange={(e) => setFormData({...formData, address: e.target.value})}
|
|
/>
|
|
</div>
|
|
|
|
{/* Terms & Submit */}
|
|
<div className="pt-6 space-y-8">
|
|
<div className="space-y-6">
|
|
<p className="text-[14px] text-[#666666] leading-relaxed">
|
|
Disclaimer: By signing this form/checking this box, you acknowledge and agree that we may use the information you share with us, to communicate with you through e-mails, text messages, WhatsApp and calls, in order to provide our product or service related information and/or for promotional and marketing purposes. All information provided will be secured and processed as per our <b>privacy policy</b>.
|
|
</p>
|
|
<div className="flex items-center gap-3">
|
|
<Checkbox
|
|
id="terms"
|
|
className="w-5 h-5 border-[#cccccc] rounded-none data-[state=checked]:bg-black data-[state=checked]:border-black"
|
|
checked={formData.acceptTerms}
|
|
onCheckedChange={(checked) => setFormData({...formData, acceptTerms: checked as boolean})}
|
|
/>
|
|
<label htmlFor="terms" className="text-[14px] font-medium cursor-pointer">
|
|
I accept the <b>terms and conditions</b> as well as <b>privacy policy</b>.
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
className="h-12 px-10 bg-black text-white flex items-center gap-3 hover:bg-slate-900 transition-colors"
|
|
>
|
|
<span className="font-bold uppercase tracking-wider text-[14px]">Submit</span>
|
|
<ChevronRight className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</main>
|
|
|
|
{/* Footer */}
|
|
<footer className="relative z-10 bg-black py-16">
|
|
<div className="max-w-[1240px] mx-auto px-6 grid grid-cols-1 md:grid-cols-3 items-center gap-8">
|
|
<img src="/assets/images/Re_Logo.png" alt="Royal Enfield" className="h-10 w-auto opacity-100" />
|
|
<div className="flex justify-center gap-12 text-[10px] font-bold uppercase tracking-widest text-slate-500">
|
|
<a href="#" className="hover:text-white transition-all">Legal</a>
|
|
<a href="#" className="hover:text-white transition-all">Privacy</a>
|
|
<a href="#" className="hover:text-white transition-all">Terms</a>
|
|
</div>
|
|
<p className="text-[10px] text-center md:text-right uppercase tracking-widest text-slate-500 font-bold">
|
|
© 2026 Royal Enfield. All Rights Reserved.
|
|
</p>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
);
|
|
}
|