1160 lines
46 KiB
JavaScript
1160 lines
46 KiB
JavaScript
|
|
// Form data object
|
|
const formData = {
|
|
userDetails: {}
|
|
};
|
|
|
|
// Current step tracking
|
|
let currentStep = 1;
|
|
|
|
// Countries and states data
|
|
const countries = [
|
|
{ code: 'US', name: 'United States' },
|
|
{ code: 'CA', name: 'Canada' },
|
|
{ code: 'GB', name: 'United Kingdom' },
|
|
{ code: 'AU', name: 'Australia' },
|
|
{ code: 'IN', name: 'India' },
|
|
{ code: 'DE', name: 'Germany' },
|
|
{ code: 'FR', name: 'France' },
|
|
{ code: 'BR', name: 'Brazil' },
|
|
{ code: 'JP', name: 'Japan' },
|
|
{ code: 'CN', name: 'China' },
|
|
{ code: 'MX', name: 'Mexico' },
|
|
{ code: 'ES', name: 'Spain' },
|
|
{ code: 'IT', name: 'Italy' },
|
|
{ code: 'NL', name: 'Netherlands' },
|
|
{ code: 'SE', name: 'Sweden' },
|
|
{ code: 'NO', name: 'Norway' },
|
|
{ code: 'DK', name: 'Denmark' },
|
|
{ code: 'FI', name: 'Finland' },
|
|
{ code: 'BE', name: 'Belgium' },
|
|
{ code: 'CH', name: 'Switzerland' },
|
|
{ code: 'AT', name: 'Austria' },
|
|
{ code: 'PT', name: 'Portugal' },
|
|
{ code: 'IE', name: 'Ireland' },
|
|
{ code: 'PL', name: 'Poland' },
|
|
{ code: 'CZ', name: 'Czech Republic' },
|
|
{ code: 'HU', name: 'Hungary' },
|
|
{ code: 'RO', name: 'Romania' },
|
|
{ code: 'GR', name: 'Greece' },
|
|
{ code: 'TR', name: 'Turkey' },
|
|
{ code: 'SA', name: 'Saudi Arabia' },
|
|
{ code: 'AE', name: 'United Arab Emirates' },
|
|
{ code: 'ZA', name: 'South Africa' },
|
|
{ code: 'NG', name: 'Nigeria' },
|
|
{ code: 'EG', name: 'Egypt' },
|
|
{ code: 'KE', name: 'Kenya' },
|
|
{ code: 'AR', name: 'Argentina' },
|
|
{ code: 'CO', name: 'Colombia' },
|
|
{ code: 'PE', name: 'Peru' },
|
|
{ code: 'CL', name: 'Chile' },
|
|
{ code: 'VE', name: 'Venezuela' },
|
|
{ code: 'KR', name: 'South Korea' },
|
|
{ code: 'SG', name: 'Singapore' },
|
|
{ code: 'MY', name: 'Malaysia' },
|
|
{ code: 'TH', name: 'Thailand' },
|
|
{ code: 'ID', name: 'Indonesia' },
|
|
{ code: 'PH', name: 'Philippines' },
|
|
{ code: 'VN', name: 'Vietnam' },
|
|
{ code: 'PK', name: 'Pakistan' },
|
|
{ code: 'BD', name: 'Bangladesh' },
|
|
{ code: 'LK', name: 'Sri Lanka' },
|
|
{ code: 'NP', name: 'Nepal' },
|
|
{ code: 'RU', name: 'Russia' },
|
|
{ code: 'UA', name: 'Ukraine' },
|
|
{ code: 'IL', name: 'Israel' },
|
|
{ code: 'NZ', name: 'New Zealand' },
|
|
{ code: 'HK', name: 'Hong Kong' },
|
|
{ code: 'TW', name: 'Taiwan' }
|
|
];
|
|
|
|
// const states = {
|
|
// 'US': [
|
|
// { code: 'CA', name: 'California' }, { code: 'TX', name: 'Texas' }, { code: 'FL', name: 'Florida' },
|
|
// { code: 'NY', name: 'New York' }, { code: 'IL', name: 'Illinois' }, { code: 'PA', name: 'Pennsylvania' },
|
|
// { code: 'OH', name: 'Ohio' }, { code: 'GA', name: 'Georgia' }, { code: 'NC', name: 'North Carolina' },
|
|
// { code: 'MI', name: 'Michigan' }
|
|
// ],
|
|
// 'CA': [
|
|
// { code: 'ON', name: 'Ontario' }, { code: 'QC', name: 'Quebec' }, { code: 'BC', name: 'British Columbia' },
|
|
// { code: 'AB', name: 'Alberta' }, { code: 'MB', name: 'Manitoba' }, { code: 'SK', name: 'Saskatchewan' }
|
|
// ],
|
|
// 'GB': [
|
|
// { code: 'ENG', name: 'England' }, { code: 'SCT', name: 'Scotland' }, { code: 'WLS', name: 'Wales' },
|
|
// { code: 'NIR', name: 'Northern Ireland' }
|
|
// ],
|
|
// 'AU': [
|
|
// { code: 'NSW', name: 'New South Wales' }, { code: 'VIC', name: 'Victoria' }, { code: 'QLD', name: 'Queensland' },
|
|
// { code: 'WA', name: 'Western Australia' }, { code: 'SA', name: 'South Australia' }, { code: 'TAS', name: 'Tasmania' }
|
|
// ],
|
|
// 'IN': [
|
|
// { code: 'MH', name: 'Maharashtra' }, { code: 'TN', name: 'Tamil Nadu' }, { code: 'UP', name: 'Uttar Pradesh' },
|
|
// { code: 'KA', name: 'Karnataka' }, { code: 'GJ', name: 'Gujarat' }, { code: 'RJ', name: 'Rajasthan' }
|
|
// ],
|
|
// 'DE': [
|
|
// { code: 'BW', name: 'Baden-Württemberg' }, { code: 'BY', name: 'Bavaria' }, { code: 'BE', name: 'Berlin' },
|
|
// { code: 'HB', name: 'Bremen' }, { code: 'HH', name: 'Hamburg' }, { code: 'HE', name: 'Hesse' }
|
|
// ],
|
|
// 'FR': [
|
|
// { code: 'ARA', name: 'Auvergne-Rhône-Alpes' }, { code: 'BFC', name: 'Bourgogne-Franche-Comté' },
|
|
// { code: 'BRE', name: 'Brittany' }, { code: 'CVL', name: 'Centre-Val de Loire' },
|
|
// { code: 'COR', name: 'Corsica' }, { code: 'GES', name: 'Grand Est' }
|
|
// ],
|
|
// 'BR': [
|
|
// { code: 'SP', name: 'São Paulo' }, { code: 'RJ', name: 'Rio de Janeiro' }, { code: 'MG', name: 'Minas Gerais' },
|
|
// { code: 'BA', name: 'Bahia' }, { code: 'RS', name: 'Rio Grande do Sul' }, { code: 'PR', name: 'Paraná' }
|
|
// ],
|
|
// 'JP': [
|
|
// { code: 'TK', name: 'Tokyo' }, { code: 'OS', name: 'Osaka' }, { code: 'KY', name: 'Kyoto' },
|
|
// { code: 'HK', name: 'Hokkaido' }, { code: 'OK', name: 'Okinawa' }, { code: 'KN', name: 'Kanagawa' }
|
|
// ],
|
|
// 'CN': [
|
|
// { code: 'BJ', name: 'Beijing' }, { code: 'SH', name: 'Shanghai' }, { code: 'TJ', name: 'Tianjin' },
|
|
// { code: 'CQ', name: 'Chongqing' }, { code: 'GD', name: 'Guangdong' }, { code: 'JS', name: 'Jiangsu' }
|
|
// ]
|
|
// };
|
|
|
|
// Initialize the form
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Populate country dropdown
|
|
const countrySelect = document.getElementById('country');
|
|
countrySelect.innerHTML = '<option value="" disabled selected></option>';
|
|
|
|
countries.forEach(country => {
|
|
const option = document.createElement('option');
|
|
option.value = country.code;
|
|
option.textContent = country.name;
|
|
countrySelect.appendChild(option);
|
|
});
|
|
|
|
autoDetectCountry();
|
|
|
|
// Add event listener for country change
|
|
// countrySelect.addEventListener('change', function() {
|
|
// const countryCode = this.value;
|
|
// const stateSelect = document.getElementById('state');
|
|
|
|
// // Show loading
|
|
// document.getElementById('state-loading').style.display = 'block';
|
|
|
|
// // Clear current states
|
|
// stateSelect.innerHTML = '<option value="" disabled selected></option>';
|
|
|
|
// // Simulate API delay
|
|
// setTimeout(() => {
|
|
// if (countryCode && states[countryCode]) {
|
|
// states[countryCode].forEach(state => {
|
|
// const option = document.createElement('option');
|
|
// option.value = state.code;
|
|
// option.textContent = state.name;
|
|
// stateSelect.appendChild(option);
|
|
// });
|
|
// }
|
|
|
|
// // Hide loading
|
|
// document.getElementById('state-loading').style.display = 'none';
|
|
// }, 500);
|
|
// });
|
|
|
|
|
|
// Add input event listeners to handle label animation
|
|
document.querySelectorAll('.input-field:not(.dob)').forEach(input => {
|
|
setTimeout(() => {
|
|
if (input.value) {
|
|
input.nextElementSibling.style.top = '4px';
|
|
input.nextElementSibling.style.fontSize = '12px';
|
|
input.nextElementSibling.style.transform = 'none';
|
|
}
|
|
}, 100);
|
|
|
|
input.addEventListener('animationstart', function() {
|
|
if (this.value) {
|
|
this.nextElementSibling.style.top = '4px';
|
|
this.nextElementSibling.style.fontSize = '12px';
|
|
this.nextElementSibling.style.transform = 'none';
|
|
}
|
|
});
|
|
input.addEventListener('input', function() {
|
|
if (this.value) {
|
|
this.nextElementSibling.style.top = '4px';
|
|
this.nextElementSibling.style.fontSize = '12px';
|
|
this.nextElementSibling.style.transform = 'none';
|
|
} else {
|
|
this.nextElementSibling.style.top = '50%';
|
|
this.nextElementSibling.style.fontSize = '14px';
|
|
this.nextElementSibling.style.transform = 'translateY(-50%)';
|
|
}
|
|
});
|
|
|
|
input.addEventListener('focus', function() {
|
|
this.nextElementSibling.style.top = '4px';
|
|
this.nextElementSibling.style.fontSize = '12px';
|
|
this.nextElementSibling.style.transform = 'none';
|
|
});
|
|
|
|
input.addEventListener('blur', function() {
|
|
if (!this.value) {
|
|
this.nextElementSibling.style.top = '50%';
|
|
this.nextElementSibling.style.fontSize = '14px';
|
|
this.nextElementSibling.style.transform = 'translateY(-50%)';
|
|
}
|
|
});
|
|
});
|
|
|
|
// Handle select field label animation
|
|
document.querySelectorAll('.select-field').forEach(select => {
|
|
setTimeout(() => {
|
|
if (select.value) {
|
|
select.nextElementSibling.style.top = '4px';
|
|
select.nextElementSibling.style.fontSize = '12px';
|
|
select.nextElementSibling.style.transform = 'none';
|
|
}
|
|
}, 100);
|
|
select.addEventListener('change', function() {
|
|
if (this.value) {
|
|
this.nextElementSibling.style.top = '4px';
|
|
this.nextElementSibling.style.fontSize = '12px';
|
|
this.nextElementSibling.style.transform = 'none';
|
|
} else {
|
|
this.nextElementSibling.style.top = '50%';
|
|
this.nextElementSibling.style.fontSize = '14px';
|
|
this.nextElementSibling.style.transform = 'translateY(-50%)';
|
|
}
|
|
});
|
|
|
|
select.addEventListener('focus', function() {
|
|
this.nextElementSibling.style.top = '4px';
|
|
this.nextElementSibling.style.fontSize = '12px';
|
|
this.nextElementSibling.style.transform = 'none';
|
|
});
|
|
|
|
select.addEventListener('blur', function() {
|
|
if (!this.value) {
|
|
this.nextElementSibling.style.top = '50%';
|
|
this.nextElementSibling.style.fontSize = '14px';
|
|
this.nextElementSibling.style.transform = 'translateY(-50%)';
|
|
}
|
|
});
|
|
});
|
|
|
|
// Step navigation for desktop
|
|
document.querySelectorAll('.step').forEach(step => {
|
|
step.addEventListener('click', function() {
|
|
const stepNum = parseInt(this.getAttribute('data-step'));
|
|
if (stepNum <= currentStep || stepNum === getMaxCompletedStep() + 1) {
|
|
goToStep(stepNum);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Step navigation for mobile
|
|
document.querySelectorAll('.mobile-step').forEach(step => {
|
|
step.addEventListener('click', function() {
|
|
const stepNum = parseInt(this.getAttribute('data-step'));
|
|
if (stepNum <= currentStep || stepNum === getMaxCompletedStep() + 1) {
|
|
goToStep(stepNum);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
async function autoDetectCountry() {
|
|
const countrySelect = document.getElementById('country');
|
|
// const loadingElement = document.getElementById('country-loading');
|
|
|
|
try {
|
|
// loadingElement.style.display = 'block';
|
|
|
|
// Using ipapi.co API (free tier available)
|
|
const response = await fetch('https://ipapi.co/json/');
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Geolocation service unavailable');
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
if (data && data.country_code) {
|
|
const detectedCountryCode = data.country_code;
|
|
const detectedCountryName = data.country_name;
|
|
|
|
// Find if the detected country is in our list
|
|
const countryExists = countries.find(country =>
|
|
country.code === detectedCountryCode
|
|
);
|
|
|
|
if (countryExists) {
|
|
// Set the country in dropdown
|
|
countrySelect.value = detectedCountryCode;
|
|
|
|
// Trigger the change event to update UI
|
|
countrySelect.dispatchEvent(new Event('change'));
|
|
|
|
// Show success message
|
|
console.log(`Auto-detected country: ${detectedCountryName}`);
|
|
} else {
|
|
console.log('Detected country not in supported list');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.log('Could not auto-detect country:', error.message);
|
|
// Fallback: Use browser's geolocation API as backup
|
|
fallbackGeolocation();
|
|
} finally {
|
|
// loadingElement.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
// Fallback geolocation using browser's navigator.geolocation
|
|
function fallbackGeolocation() {
|
|
if (!navigator.geolocation) {
|
|
console.log('Geolocation is not supported by this browser');
|
|
return;
|
|
}
|
|
|
|
navigator.geolocation.getCurrentPosition(
|
|
async function(position) {
|
|
try {
|
|
const { latitude, longitude } = position.coords;
|
|
|
|
// Use OpenStreetMap Nominatim API for reverse geocoding
|
|
const response = await fetch(
|
|
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}`
|
|
);
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
if (data && data.address && data.address.country_code) {
|
|
const countryCode = data.address.country_code.toUpperCase();
|
|
const countrySelect = document.getElementById('country');
|
|
|
|
const countryExists = countries.find(country =>
|
|
country.code === countryCode
|
|
);
|
|
|
|
if (countryExists) {
|
|
countrySelect.value = countryCode;
|
|
countrySelect.dispatchEvent(new Event('change'));
|
|
console.log(`Fallback geolocation detected: ${data.address.country}`);
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.log('Fallback geolocation failed:', error);
|
|
}
|
|
},
|
|
function(error) {
|
|
console.log('Browser geolocation error:', error.message);
|
|
},
|
|
{
|
|
enableHighAccuracy: false,
|
|
timeout: 10000,
|
|
maximumAge: 600000 // 10 minutes
|
|
}
|
|
);
|
|
}
|
|
|
|
// Function to navigate to any step (for tab navigation)
|
|
function goToStep(step) {
|
|
// Only allow navigation to completed steps or the next step
|
|
if (step > currentStep && step > getMaxCompletedStep() + 1) {
|
|
return;
|
|
}
|
|
|
|
// Hide current step
|
|
document.getElementById(`step-${currentStep}`).classList.remove('active');
|
|
document.querySelectorAll(`.step[data-step="${currentStep}"]`).forEach(el => {
|
|
el.classList.remove('active');
|
|
});
|
|
|
|
document.querySelectorAll(`.mobile-step[data-step="${currentStep}"]`).forEach(el => {
|
|
el.classList.remove('active');
|
|
});
|
|
|
|
// Show target step
|
|
document.getElementById(`step-${step}`).classList.add('active');
|
|
document.querySelectorAll(`.step[data-step="${step}"]`).forEach(el => {
|
|
el.classList.add('active');
|
|
});
|
|
|
|
document.querySelectorAll(`.mobile-step[data-step="${step}"]`).forEach(el => {
|
|
el.classList.add('active');
|
|
});
|
|
|
|
currentStep = step;
|
|
}
|
|
|
|
// Function to get the maximum completed step
|
|
function getMaxCompletedStep() {
|
|
const completedSteps = document.querySelectorAll('.step.completed');
|
|
let maxStep = 0;
|
|
completedSteps.forEach(step => {
|
|
const stepNum = parseInt(step.getAttribute('data-step'));
|
|
if (stepNum > maxStep) maxStep = stepNum;
|
|
});
|
|
return maxStep;
|
|
}
|
|
|
|
async function handleNextStep(step) {
|
|
await nextStep(step);
|
|
}
|
|
|
|
// Function to navigate to next step
|
|
async function nextStep(step) {
|
|
// Validate current step before proceeding
|
|
if (!(await validateStep(currentStep))) {
|
|
return;
|
|
}
|
|
|
|
// Save current step data
|
|
saveStepData(currentStep);
|
|
|
|
// Mark current step as completed
|
|
document.querySelectorAll(`.step[data-step="${currentStep}"]`).forEach(el => {
|
|
el.classList.add('completed');
|
|
const stepNumber = el.querySelector('.step-number');
|
|
stepNumber.textContent = '✓';
|
|
stepNumber.style.backgroundColor = '#000000';
|
|
stepNumber.style.borderColor = '#000000';
|
|
stepNumber.style.color = 'white';
|
|
});
|
|
|
|
document.querySelectorAll(`.mobile-step[data-step="${currentStep}"]`).forEach(el => {
|
|
el.classList.add('completed');
|
|
const stepNumber = el.querySelector('.step-number');
|
|
stepNumber.textContent = '✓';
|
|
stepNumber.style.backgroundColor = '#000000';
|
|
stepNumber.style.borderColor = '#000000';
|
|
stepNumber.style.color = 'white';
|
|
});
|
|
|
|
// Hide current step
|
|
document.getElementById(`step-${currentStep}`).classList.remove('active');
|
|
document.querySelectorAll(`.step[data-step="${currentStep}"]`).forEach(el => {
|
|
el.classList.remove('active');
|
|
});
|
|
|
|
document.querySelectorAll(`.mobile-step[data-step="${currentStep}"]`).forEach(el => {
|
|
el.classList.remove('active');
|
|
});
|
|
|
|
// Show next step
|
|
document.getElementById(`step-${step}`).classList.add('active');
|
|
document.querySelectorAll(`.step[data-step="${step}"]`).forEach(el => {
|
|
el.classList.add('active');
|
|
});
|
|
|
|
document.querySelectorAll(`.mobile-step[data-step="${step}"]`).forEach(el => {
|
|
el.classList.add('active');
|
|
});
|
|
|
|
currentStep = step;
|
|
}
|
|
|
|
// Function to navigate to previous step
|
|
function prevStep(step) {
|
|
// Hide current step
|
|
document.getElementById(`step-${currentStep}`).classList.remove('active');
|
|
document.querySelectorAll(`.step[data-step="${currentStep}"]`).forEach(el => {
|
|
el.classList.remove('active');
|
|
});
|
|
|
|
document.querySelectorAll(`.mobile-step[data-step="${currentStep}"]`).forEach(el => {
|
|
el.classList.remove('active');
|
|
});
|
|
|
|
// Show previous step
|
|
document.getElementById(`step-${step}`).classList.add('active');
|
|
document.querySelectorAll(`.step[data-step="${step}"]`).forEach(el => {
|
|
el.classList.add('active');
|
|
});
|
|
|
|
document.querySelectorAll(`.mobile-step[data-step="${step}"]`).forEach(el => {
|
|
el.classList.add('active');
|
|
});
|
|
|
|
currentStep = step;
|
|
}
|
|
|
|
// Function to validate current step
|
|
async function validateStep(step) {
|
|
let isValid = true;
|
|
const form = document.getElementById(getFormId(step));
|
|
|
|
// Hide any previous errors for this step
|
|
hideError(step);
|
|
clearFieldErrors();
|
|
|
|
if (form) {
|
|
// For account setup step
|
|
if (step === 1) {
|
|
const email = document.getElementById('email').value;
|
|
const password = document.getElementById('password').value;
|
|
const confirmPassword = document.getElementById('confirmPassword').value;
|
|
|
|
if(!email && !password) {
|
|
showError('', 'email', step);
|
|
showError('', 'password', step);
|
|
showError('Please enter a valid email address and password', 'confirmPassword', step);
|
|
isValid = false;
|
|
return isValid;
|
|
}
|
|
|
|
// Email validation
|
|
if (!validateEmail(email)) {
|
|
showError('Please enter a valid email address', 'email', step);
|
|
isValid = false;
|
|
} else {
|
|
// Email verification - wait for the result
|
|
const isEmailVerified = await verifyEmail(email);
|
|
if (!isEmailVerified) {
|
|
showError('Email already registered. Please use a different email address.', 'email', step);
|
|
isValid = false;
|
|
}
|
|
}
|
|
|
|
// Password validation
|
|
if (password.length < 8) {
|
|
showError('Password must be at least 8 characters long', 'password', step);
|
|
isValid = false;
|
|
}
|
|
|
|
if (password !== confirmPassword) {
|
|
document.getElementById('password-match-error').style.display = 'block';
|
|
document.getElementById('confirmPassword').classList.add('field-error');
|
|
showError('Passwords do not match', 'confirmPassword', step);
|
|
isValid = false;
|
|
} else {
|
|
document.getElementById('password-match-error').style.display = 'none';
|
|
document.getElementById('confirmPassword').classList.remove('field-error');
|
|
}
|
|
}
|
|
|
|
// For personal information step
|
|
if (step === 2) {
|
|
const firstName = document.getElementById('firstName').value;
|
|
if (!firstName) {
|
|
document.getElementById('profile-incomplete').style.display = 'block';
|
|
document.getElementById('firstName').classList.add('field-error');
|
|
showError('First name is required', 'firstName', step);
|
|
isValid = false;
|
|
} else {
|
|
document.getElementById('profile-incomplete').style.display = 'none';
|
|
document.getElementById('firstName').classList.remove('field-error');
|
|
}
|
|
|
|
// Age validation
|
|
const age = document.getElementById('age').value;
|
|
if (age && (age < 18 || age > 100)) {
|
|
showError('You age must be 18 years or older to register', 'age', step);
|
|
isValid = false;
|
|
}
|
|
|
|
// Country and state validation
|
|
const country = document.getElementById('country').value;
|
|
const state = document.getElementById('state').value;
|
|
|
|
if (!country) {
|
|
showError('Please select your country', 'country', step);
|
|
isValid = false;
|
|
}
|
|
|
|
if (!state) {
|
|
showError('Please select your state/region', 'state', step);
|
|
isValid = false;
|
|
}
|
|
|
|
// Validate other required fields
|
|
const requiredFields = form.querySelectorAll('[required]');
|
|
requiredFields.forEach(field => {
|
|
if (!field.value.trim()) {
|
|
isValid = false;
|
|
field.classList.add('field-error');
|
|
if (isValid === false) {
|
|
showError(`Please fill in ${field.labels[0]?.textContent || 'this field'}`, field.id, step);
|
|
}
|
|
} else {
|
|
field.classList.remove('field-error');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (step === 3) {
|
|
// Phone number validation
|
|
const phone = document.getElementById('phone').value;
|
|
if (!phone) {
|
|
showError('Please enter your phone number', 'phone', step);
|
|
isValid = false;
|
|
} else if (!validatePhoneNumberStrict(phone)) {
|
|
document.getElementById('phone-error').style.display = 'block';
|
|
document.getElementById('phone').classList.add('field-error');
|
|
showError('Please enter a valid phone number (10-15 digits)', 'phone', step);
|
|
isValid = false;
|
|
} else {
|
|
document.getElementById('phone-error').style.display = 'none';
|
|
document.getElementById('phone').classList.remove('field-error');
|
|
}
|
|
|
|
// Validate other required fields in step 3
|
|
const requiredFields = form.querySelectorAll('[required]');
|
|
requiredFields.forEach(field => {
|
|
if (!field.value.trim()) {
|
|
isValid = false;
|
|
field.classList.add('field-error');
|
|
} else {
|
|
field.classList.remove('field-error');
|
|
}
|
|
});
|
|
}
|
|
|
|
// For payment step
|
|
if (step === 4) {
|
|
const selectedPayment = document.querySelector('.payment-option.selected');
|
|
if (!selectedPayment) {
|
|
showError('Please select a payment method', null, step);
|
|
isValid = false;
|
|
} else {
|
|
const paymentMethod = selectedPayment.getAttribute('onclick').match(/'([^']+)'/)[1];
|
|
if (paymentMethod === 'payoneer') {
|
|
const payoneerAccount = document.getElementById('payoneerAccount').value;
|
|
if (!payoneerAccount) {
|
|
showError('Please enter your Payoneer account number', 'payoneerAccount', step);
|
|
isValid = false;
|
|
}
|
|
} else if (paymentMethod === 'veem') {
|
|
const veemAccount = document.getElementById('veemAccount').value;
|
|
if (!veemAccount) {
|
|
showError('Please enter your Veem account number', 'veemAccount', step);
|
|
isValid = false;
|
|
}
|
|
} else if (paymentMethod === 'wise') {
|
|
const wiseEmail = document.getElementById('wiseEmail').value;
|
|
if (!wiseEmail) {
|
|
showError('Please enter your Wise email address', 'wiseEmail', step);
|
|
isValid = false;
|
|
} else if (!validateEmail(wiseEmail)) {
|
|
showError('Please enter a valid email address for Wise', 'wiseEmail', step);
|
|
isValid = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// For other steps
|
|
if (step !== 1 && step !== 2 && step !== 4) {
|
|
const requiredFields = form.querySelectorAll('[required]');
|
|
requiredFields.forEach(field => {
|
|
if (!field.value.trim()) {
|
|
isValid = false;
|
|
field.classList.add('field-error');
|
|
} else {
|
|
field.classList.remove('field-error');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
// Email validation function
|
|
function validateEmail(email) {
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
return emailRegex.test(email);
|
|
}
|
|
|
|
// Function to get form ID based on step
|
|
function getFormId(step) {
|
|
switch(step) {
|
|
case 1: return 'account-form';
|
|
case 2: return 'personal-form';
|
|
case 3: return 'contact-form';
|
|
case 4: return 'payment-form';
|
|
default: return '';
|
|
}
|
|
}
|
|
|
|
// Function to save step data to formData object
|
|
function saveStepData(step) {
|
|
switch(step) {
|
|
case 1:
|
|
formData.userDetails.email = document.getElementById('email').value;
|
|
formData.userDetails.password = document.getElementById('password').value;
|
|
break;
|
|
case 2:
|
|
formData.userDetails.firstName = document.getElementById('firstName').value;
|
|
formData.userDetails.lastName = document.getElementById('lastName').value;
|
|
formData.userDetails.age = document.getElementById('age').value;
|
|
formData.userDetails.dob = document.getElementById('dob').value;
|
|
formData.userDetails.address = document.getElementById('address').value;
|
|
formData.userDetails.city = document.getElementById('city').value;
|
|
formData.userDetails.state = document.getElementById('state').value;
|
|
formData.userDetails.country = document.getElementById('country').value;
|
|
formData.status = document.getElementById('status').value;
|
|
break;
|
|
case 3:
|
|
formData.userDetails.phone = document.getElementById('phone').value;
|
|
// formData.userDetails.skype = document.getElementById('skype').value;
|
|
break;
|
|
case 4:
|
|
const selectedPayment = document.querySelector('.payment-option.selected');
|
|
if (selectedPayment) {
|
|
const paymentMethod = selectedPayment.getAttribute('onclick').match(/'([^']+)'/)[1];
|
|
formData.userDetails.paymentMethod = paymentMethod;
|
|
|
|
if (document.getElementById('payoneerAccount').value) {
|
|
formData.userDetails.payoneerAccount = document.getElementById('payoneerAccount').value;
|
|
}
|
|
if (document.getElementById('veemAccount').value) {
|
|
formData.userDetails.veemAccount = document.getElementById('veemAccount').value;
|
|
}
|
|
if (paymentMethod === 'wise') {
|
|
formData.userDetails.wiseEmail = document.getElementById('wiseEmail').value;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Function to select payment method
|
|
function selectPaymentMethod(method) {
|
|
// Remove selected class from all options
|
|
document.querySelectorAll('.payment-option').forEach(option => {
|
|
option.classList.remove('selected');
|
|
});
|
|
|
|
// Hide all payment details
|
|
document.querySelectorAll('.payment-details').forEach(details => {
|
|
details.style.display = 'none';
|
|
});
|
|
|
|
// Add selected class to clicked option
|
|
event.currentTarget.classList.add('selected');
|
|
|
|
// Show appropriate payment details
|
|
if (method === 'payoneer') {
|
|
document.getElementById('payoneer-details').style.display = 'block';
|
|
} else if (method === 'veem') {
|
|
document.getElementById('veem-details').style.display = 'block';
|
|
} else if (method === 'wise') {
|
|
document.getElementById('wise-details').style.display = 'block';
|
|
}
|
|
}
|
|
|
|
function validateWiseEmail(input) {
|
|
const wiseError = document.getElementById('wise-email-error');
|
|
const value = input.value.trim();
|
|
|
|
// Remove any validation styling first
|
|
input.style.border = '';
|
|
wiseError.style.display = 'none';
|
|
|
|
if (value.length > 0) {
|
|
// Check if it's a valid email
|
|
if (!validateEmail(value)) {
|
|
wiseError.textContent = 'Please enter a valid email address for Wise';
|
|
wiseError.style.display = 'block';
|
|
input.style.border = '1px solid #ef4444';
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Function to generate a strong password
|
|
function generatePassword() {
|
|
const length = 12;
|
|
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
|
|
let password = "";
|
|
|
|
// Ensure at least one of each required character type
|
|
password += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[Math.floor(Math.random() * 26)];
|
|
password += "abcdefghijklmnopqrstuvwxyz"[Math.floor(Math.random() * 26)];
|
|
password += "0123456789"[Math.floor(Math.random() * 10)];
|
|
password += "!@#$%^&*"[Math.floor(Math.random() * 8)];
|
|
|
|
// Fill the rest randomly
|
|
for (let i = password.length; i < length; i++) {
|
|
password += charset.charAt(Math.floor(Math.random() * charset.length));
|
|
}
|
|
|
|
// Shuffle the password
|
|
password = password.split('').sort(() => 0.5 - Math.random()).join('');
|
|
|
|
// Set the password field
|
|
document.getElementById('password').value = password;
|
|
document.getElementById('confirmPassword').value = password;
|
|
|
|
// Trigger input events to update UI
|
|
document.getElementById('password').dispatchEvent(new Event('input'));
|
|
document.getElementById('confirmPassword').dispatchEvent(new Event('input'));
|
|
}
|
|
|
|
// Function to toggle password visibility
|
|
function togglePasswordVisibility() {
|
|
const passwordField = document.getElementById('password');
|
|
const confirmField = document.getElementById('confirmPassword');
|
|
const toggleButton = document.querySelector('.password-toggle');
|
|
|
|
if (passwordField.type === 'password') {
|
|
passwordField.type = 'text';
|
|
confirmField.type = 'text';
|
|
toggleButton.textContent = 'Hide Password';
|
|
} else {
|
|
passwordField.type = 'password';
|
|
confirmField.type = 'password';
|
|
toggleButton.textContent = 'Show Password';
|
|
}
|
|
}
|
|
|
|
// Function to submit the form
|
|
async function submitForm() {
|
|
// Validate current step
|
|
if (!validateStep(currentStep)) {
|
|
return;
|
|
}
|
|
|
|
// Save current step data
|
|
saveStepData(currentStep);
|
|
|
|
// Show loading state
|
|
const submitBtn = document.querySelector('#payment-form .btn-primary');
|
|
const originalText = submitBtn.textContent;
|
|
submitBtn.textContent = 'Processing...';
|
|
submitBtn.disabled = true;
|
|
|
|
try {
|
|
// Send data to server using fetch
|
|
// console.log('Submitting form data:', formData);
|
|
const response = await fetch('/api/v1/va/register', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(formData)
|
|
});
|
|
|
|
if (response.ok) {
|
|
// Success - move to success step
|
|
// Mark current step as completed
|
|
document.querySelectorAll(`.step[data-step="${currentStep}"]`).forEach(el => {
|
|
el.classList.add('completed');
|
|
const stepNumber = el.querySelector('.step-number');
|
|
stepNumber.textContent = '✓';
|
|
stepNumber.style.backgroundColor = '#000000';
|
|
stepNumber.style.borderColor = '#000000';
|
|
stepNumber.style.color = 'white';
|
|
});
|
|
|
|
document.querySelectorAll(`.mobile-step[data-step="${currentStep}"]`).forEach(el => {
|
|
el.classList.add('completed');
|
|
const stepNumber = el.querySelector('.step-number');
|
|
stepNumber.textContent = '✓';
|
|
stepNumber.style.backgroundColor = '#000000';
|
|
stepNumber.style.borderColor = '#000000';
|
|
stepNumber.style.color = 'white';
|
|
});
|
|
|
|
nextStep(5);
|
|
setTimeout(() => {
|
|
window.location.href = '/download-tracker';
|
|
}, 1000);
|
|
} else {
|
|
// Handle server errors
|
|
showError('There was an error submitting your registration. Please try again.', null, 4);
|
|
}
|
|
} catch (error) {
|
|
// Handle network errors
|
|
console.error('Error:', error);
|
|
showError('There was a network error. Please check your connection and try again.', null, 4);
|
|
} finally {
|
|
// Restore button state
|
|
submitBtn.textContent = originalText;
|
|
submitBtn.disabled = false;
|
|
}
|
|
}
|
|
|
|
async function verifyEmail(email) {
|
|
|
|
try {
|
|
const response = await fetch('/api/v1/va/verify-va-email', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({"email": email})
|
|
});
|
|
|
|
if (response.ok) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
} catch (error) {
|
|
// Handle network errors
|
|
console.error('Error:', error);
|
|
showError('There was a network error. Please check your connection and try again.', null, 1);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Function to calculate age from date of birth
|
|
function calculateAge(dob) {
|
|
if (!dob) return '';
|
|
|
|
const birthDate = new Date(dob);
|
|
const today = new Date();
|
|
|
|
let age = today.getFullYear() - birthDate.getFullYear();
|
|
const monthDiff = today.getMonth() - birthDate.getMonth();
|
|
|
|
// Adjust age if birthday hasn't occurred this year yet
|
|
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
|
|
age--;
|
|
}
|
|
|
|
return age;
|
|
}
|
|
|
|
// Function to handle date of birth change and update age
|
|
function handleDOBChange() {
|
|
const dob = document.getElementById('dob').value;
|
|
const ageInput = document.getElementById('age');
|
|
|
|
if (dob) {
|
|
const age = calculateAge(dob);
|
|
ageInput.value = age;
|
|
|
|
// Trigger input event to update the label position
|
|
ageInput.dispatchEvent(new Event('input'));
|
|
|
|
// Validate age
|
|
if (age < 18) {
|
|
// alert('You must be 18 years or older to register');
|
|
// showError('You Age must be 18 years or older to register', 'dob');
|
|
showError('You Age must be 18 years or older to register', 'age');
|
|
ageInput.style.border = '1px solid #ef4444';
|
|
} else if (age > 100) {
|
|
// alert('Please enter a valid age');
|
|
// showError('Please enter a valid age', 'dob');
|
|
showError('Please enter a valid age', 'age');
|
|
ageInput.style.border = '1px solid #ef4444';
|
|
} else {
|
|
ageInput.style.border = '';
|
|
}
|
|
} else {
|
|
// Clear age if DOB is cleared
|
|
ageInput.value = '';
|
|
ageInput.dispatchEvent(new Event('input'));
|
|
}
|
|
}
|
|
|
|
// Function to handle age manual input and update DOB (optional)
|
|
function handleAgeChange() {
|
|
const age = document.getElementById('age').value;
|
|
const dobInput = document.getElementById('dob');
|
|
|
|
if (age && age >= 18 && age <= 100) {
|
|
// Calculate approximate DOB based on age
|
|
const today = new Date();
|
|
const birthYear = today.getFullYear() - age;
|
|
const approximateDOB = `${birthYear}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
|
|
|
|
// You can set this as the DOB or just leave it for reference
|
|
// dobInput.value = approximateDOB;
|
|
// dobInput.dispatchEvent(new Event('change'));
|
|
}
|
|
}
|
|
|
|
// Function to validate phone number in real-time
|
|
function validatePhoneNumber(input) {
|
|
const phoneError = document.getElementById('phone-error');
|
|
const value = input.value;
|
|
|
|
// Remove any non-digit characters
|
|
const digitsOnly = value.replace(/\D/g, '');
|
|
|
|
// Update the input value with digits only
|
|
if (value !== digitsOnly) {
|
|
input.value = digitsOnly;
|
|
}
|
|
|
|
// Validate length
|
|
if (digitsOnly.length > 0 && (digitsOnly.length < 10 || digitsOnly.length > 15)) {
|
|
phoneError.style.display = 'block';
|
|
input.style.border = '1px solid #ef4444';
|
|
return false;
|
|
} else {
|
|
phoneError.style.display = 'none';
|
|
input.style.border = '';
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Function to validate phone number on form submission
|
|
function validatePhoneNumberStrict(phone) {
|
|
const phoneRegex = /^[0-9]{10,15}$/;
|
|
return phoneRegex.test(phone);
|
|
}
|
|
|
|
// Function to validate Payoneer account number
|
|
function validatePayoneerAccount(input) {
|
|
const payoneerError = document.getElementById('payoneer-error');
|
|
const value = input.value.trim();
|
|
|
|
// Remove any validation styling first
|
|
input.style.border = '';
|
|
payoneerError.style.display = 'none';
|
|
|
|
if (value.length > 0) {
|
|
// Check length
|
|
if (value.length < 5 || value.length > 20) {
|
|
payoneerError.textContent = 'Payoneer account must be 5-20 digits';
|
|
payoneerError.style.display = 'block';
|
|
input.style.border = '1px solid #ef4444';
|
|
return false;
|
|
}
|
|
|
|
// Check if contains only digits
|
|
if (!/^\d+$/.test(value)) {
|
|
payoneerError.textContent = 'Payoneer account must contain digits only';
|
|
payoneerError.style.display = 'block';
|
|
input.style.border = '1px solid #ef4444';
|
|
return false;
|
|
}
|
|
|
|
// Check if all zeros
|
|
if (/^0+$/.test(value)) {
|
|
payoneerError.textContent = 'Payoneer account cannot be all zeros';
|
|
payoneerError.style.display = 'block';
|
|
input.style.border = '1px solid #ef4444';
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Function to validate Veem account number
|
|
function validateVeemAccount(input) {
|
|
const veemError = document.getElementById('veem-error');
|
|
const value = input.value.trim();
|
|
|
|
// Remove any validation styling first
|
|
input.style.border = '';
|
|
veemError.style.display = 'none';
|
|
|
|
if (value.length > 0) {
|
|
// Check length
|
|
if (value.length < 8 || value.length > 25) {
|
|
veemError.textContent = 'Veem account must be 8-25 digits';
|
|
veemError.style.display = 'block';
|
|
input.style.border = '1px solid #ef4444';
|
|
return false;
|
|
}
|
|
|
|
// Check if contains only digits
|
|
if (!/^\d+$/.test(value)) {
|
|
veemError.textContent = 'Veem account must contain digits only';
|
|
veemError.style.display = 'block';
|
|
input.style.border = '1px solid #ef4444';
|
|
return false;
|
|
}
|
|
|
|
// Check if all zeros
|
|
if (/^0+$/.test(value)) {
|
|
veemError.textContent = 'Veem account cannot be all zeros';
|
|
veemError.style.display = 'block';
|
|
input.style.border = '1px solid #ef4444';
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function showError(message, fieldId = null, step = null) {
|
|
// Determine the current step if not provided
|
|
const currentStep = step || getCurrentStep();
|
|
const errorContainer = document.getElementById(`step-${currentStep}-error`);
|
|
const errorMessage = document.getElementById(`step-${currentStep}-error-message`);
|
|
|
|
if (errorContainer && errorMessage) {
|
|
errorMessage.textContent = message;
|
|
errorContainer.style.display = 'block';
|
|
|
|
// Scroll to error container
|
|
errorContainer.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
|
|
// Highlight specific field if provided
|
|
if (fieldId) {
|
|
const field = document.getElementById(fieldId);
|
|
if (field) {
|
|
field.classList.add('field-error');
|
|
// Remove error after 5 seconds
|
|
setTimeout(() => {
|
|
field.classList.remove('field-error');
|
|
}, 5000);
|
|
}
|
|
}
|
|
|
|
// Auto-hide error after 8 seconds
|
|
setTimeout(() => {
|
|
hideError(currentStep);
|
|
}, 8000);
|
|
}
|
|
}
|
|
|
|
function hideError(step = null) {
|
|
if (step) {
|
|
// Hide specific step error
|
|
const errorContainer = document.getElementById(`step-${step}-error`);
|
|
if (errorContainer) {
|
|
errorContainer.style.display = 'none';
|
|
}
|
|
} else {
|
|
// Hide all step errors
|
|
for (let i = 1; i <= 5; i++) {
|
|
const errorContainer = document.getElementById(`step-${i}-error`);
|
|
if (errorContainer) {
|
|
errorContainer.style.display = 'none';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function clearFieldErrors() {
|
|
document.querySelectorAll('.field-error').forEach(field => {
|
|
field.classList.remove('field-error');
|
|
});
|
|
}
|
|
|
|
function getCurrentStep() {
|
|
return currentStep;
|
|
}
|
|
|
|
function handleChangeSelect(selectElement) {
|
|
const selectedValue = selectElement.value;
|
|
let option = selectElement.querySelector('option:disabled');
|
|
|
|
if (selectedValue !== "") {
|
|
option.style.display = "none";
|
|
} else {
|
|
option.style.display = "";
|
|
}
|
|
}
|
|
|
|
function validateSkypeId(input) {
|
|
const value = input.value.trim();
|
|
|
|
if (value && !/^[a-zA-Z][a-zA-Z0-9\.,\-_]+$/.test(value)) {
|
|
input.style.border = '1px solid #ef4444';
|
|
showError('Invalid Skype ID', 'skype', 3);
|
|
return false;
|
|
} else {
|
|
input.style.border = '';
|
|
return true;
|
|
}
|
|
} |