Fixed issues and bugs
This commit is contained in:
parent
9429caacd6
commit
aecb4af2ac
|
|
@ -476,3 +476,42 @@ body {
|
|||
margin-bottom: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.step-error-container {
|
||||
background-color: #fef2f2;
|
||||
border: 1px solid #fecaca;
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 20px;
|
||||
animation: slideDown 0.3s ease-out;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #dc2626;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.error-message::before {
|
||||
content: "⚠";
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.field-error {
|
||||
border: 1px solid #dc2626 !important;
|
||||
background-color: #fef2f2;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -109,7 +109,22 @@
|
|||
|
||||
|
||||
// Add input event listeners to handle label animation
|
||||
document.querySelectorAll('.input-field').forEach(input => {
|
||||
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';
|
||||
|
|
@ -139,6 +154,13 @@
|
|||
|
||||
// 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';
|
||||
|
|
@ -228,10 +250,14 @@
|
|||
return maxStep;
|
||||
}
|
||||
|
||||
async function handleNextStep(step) {
|
||||
await nextStep(step);
|
||||
}
|
||||
|
||||
// Function to navigate to next step
|
||||
function nextStep(step) {
|
||||
async function nextStep(step) {
|
||||
// Validate current step before proceeding
|
||||
if (!validateStep(currentStep)) {
|
||||
if (!(await validateStep(currentStep))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -306,10 +332,14 @@
|
|||
}
|
||||
|
||||
// Function to validate current step
|
||||
function validateStep(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) {
|
||||
|
|
@ -319,21 +349,31 @@
|
|||
|
||||
// Email validation
|
||||
if (!validateEmail(email)) {
|
||||
alert('Please enter a valid email address');
|
||||
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) {
|
||||
alert('Password must be at least 8 characters long');
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -342,15 +382,18 @@
|
|||
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)) {
|
||||
alert('You must be 18 years or older to register');
|
||||
showError('You must be 18 years or older to register', 'dob', step);
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
|
|
@ -359,12 +402,12 @@
|
|||
const state = document.getElementById('state').value;
|
||||
|
||||
if (!country) {
|
||||
alert('Please select your country');
|
||||
showError('Please select your country', 'country', step);
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (!state) {
|
||||
alert('Please select your state/region');
|
||||
showError('Please select your state/region', 'state', step);
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
|
|
@ -373,9 +416,40 @@
|
|||
requiredFields.forEach(field => {
|
||||
if (!field.value.trim()) {
|
||||
isValid = false;
|
||||
field.style.border = '1px solid #ef4444';
|
||||
field.classList.add('field-error');
|
||||
if (isValid === false) {
|
||||
showError(`Please fill in ${field.labels[0]?.textContent || 'this field'}`, field.id, step);
|
||||
}
|
||||
} else {
|
||||
field.style.border = '';
|
||||
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');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -384,20 +458,20 @@
|
|||
if (step === 4) {
|
||||
const selectedPayment = document.querySelector('.payment-option.selected');
|
||||
if (!selectedPayment) {
|
||||
alert('Please select a payment method');
|
||||
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) {
|
||||
alert('Please enter your Payoneer account number');
|
||||
showError('Please enter your Payoneer account number', 'payoneerAccount', step);
|
||||
isValid = false;
|
||||
}
|
||||
} else if (paymentMethod === 'veem') {
|
||||
const veemAccount = document.getElementById('veemAccount').value;
|
||||
if (!veemAccount) {
|
||||
alert('Please enter your Veem account number');
|
||||
showError('Please enter your Veem account number', 'veemAccount', step);
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -410,9 +484,9 @@
|
|||
requiredFields.forEach(field => {
|
||||
if (!field.value.trim()) {
|
||||
isValid = false;
|
||||
field.style.border = '1px solid #ef4444';
|
||||
field.classList.add('field-error');
|
||||
} else {
|
||||
field.style.border = '';
|
||||
field.classList.remove('field-error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -598,12 +672,12 @@
|
|||
}, 1000);
|
||||
} else {
|
||||
// Handle server errors
|
||||
alert('There was an error submitting your registration. Please try again.');
|
||||
showError('There was an error submitting your registration. Please try again.', null, 4);
|
||||
}
|
||||
} catch (error) {
|
||||
// Handle network errors
|
||||
console.error('Error:', error);
|
||||
alert('There was a network error. Please check your connection and try again.');
|
||||
showError('There was a network error. Please check your connection and try again.', null, 4);
|
||||
} finally {
|
||||
// Restore button state
|
||||
submitBtn.textContent = originalText;
|
||||
|
|
@ -611,3 +685,218 @@
|
|||
}
|
||||
}
|
||||
|
||||
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');
|
||||
ageInput.style.border = '1px solid #ef4444';
|
||||
} else if (age > 100) {
|
||||
alert('Please enter a valid 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();
|
||||
|
||||
// Payoneer typically uses email format or alphanumeric IDs
|
||||
// Adjust validation rules based on Payoneer's actual format requirements
|
||||
if (value.length > 0 && (value.length < 5 || value.length > 20)) {
|
||||
payoneerError.textContent = 'Payoneer account must be 5-20 characters';
|
||||
payoneerError.style.display = 'block';
|
||||
input.style.border = '1px solid #ef4445';
|
||||
return false;
|
||||
} else {
|
||||
payoneerError.style.display = 'none';
|
||||
input.style.border = '';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to validate Veem account number
|
||||
function validateVeemAccount(input) {
|
||||
const veemError = document.getElementById('veem-error');
|
||||
const value = input.value.trim();
|
||||
|
||||
// Veem account numbers are typically longer alphanumeric strings
|
||||
if (value.length > 0 && (value.length < 8 || value.length > 25)) {
|
||||
veemError.textContent = 'Veem account must be 8-25 characters';
|
||||
veemError.style.display = 'block';
|
||||
input.style.border = '1px solid #ef4445';
|
||||
return false;
|
||||
} else {
|
||||
veemError.style.display = 'none';
|
||||
input.style.border = '';
|
||||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import { ClientModel } from "../../models/vaModel.js";
|
||||
|
||||
// Verify VA Email
|
||||
async function client_verify_email(req, res) {
|
||||
try {
|
||||
// const payload = req.body ? JSON.parse(req.body) : {};
|
||||
const payload = req.body || {};
|
||||
const { email } = payload;
|
||||
|
||||
if (!email) {
|
||||
return res.status(400).json({ success: false, message: "Please provide email" });
|
||||
}
|
||||
|
||||
// Find user by email
|
||||
const user = await ClientModel.findOne({ "userDetails.email": email });
|
||||
if (!user) {
|
||||
return res.status(404).json({ success: false, message: "Email not found" });
|
||||
}
|
||||
|
||||
// Return accessToken and user info
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
email: user.userDetails?.email,
|
||||
message: "Email ID is already registerd"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("va_register error:", error);
|
||||
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
||||
}
|
||||
}
|
||||
|
||||
export { client_verify_email };
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import { VAModel } from "../../../models/vaModel.js";
|
||||
|
||||
// Verify VA Email
|
||||
async function va_verify_email(req, res) {
|
||||
try {
|
||||
// const payload = req.body ? JSON.parse(req.body) : {};
|
||||
const payload = req.body || {};
|
||||
const { email } = payload;
|
||||
|
||||
if (!email) {
|
||||
return res.status(400).json({ success: false, message: "Please provide email" });
|
||||
}
|
||||
|
||||
// Find user by email
|
||||
const user = await VAModel.findOne({ "userDetails.email": email });
|
||||
if (!user) {
|
||||
return res.status(404).json({ success: false, message: "Invalid Email ID" });
|
||||
}
|
||||
|
||||
// Return accessToken and user info
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
email: user.userDetails?.email,
|
||||
message: "Email verified successfully"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("va_register error:", error);
|
||||
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
||||
}
|
||||
}
|
||||
|
||||
export { va_verify_email };
|
||||
|
|
@ -9,13 +9,15 @@ import { client_refresh_token_callback } from "../../controllers/client/clientRe
|
|||
import { client_request_password_reset } from "../../controllers/passwordReset.controllers.js";
|
||||
import { clientAuthValidator } from "../../middlewares/auth.middleware.js";
|
||||
import { client_dashboard } from "./../../controllers/client/dashboard/clientDashboard.controller.js";
|
||||
import { client_dashboard_va_log } from "../../controllers/client/dashboard/clientDashboardVaLog.controller.js"
|
||||
import { client_dashboard_va_log } from "../../controllers/client/dashboard/clientDashboardVaLog.controller.js";
|
||||
import { client_verify_email } from "../../controllers/client/vaClientEmailVerify.controller.js";
|
||||
|
||||
// clients routes
|
||||
clientRoutes.post("/register", va_register_client); // Register a new VA
|
||||
clientRoutes.post("/login", va_login_client); // Login Client
|
||||
clientRoutes.get("/refreshToken", client_refresh_token_callback); // Client Refresh Token
|
||||
clientRoutes.post("/forgot-password", client_request_password_reset);
|
||||
clientRoutes.post("/verify-client-email", client_verify_email);
|
||||
|
||||
// clientRoutes.get("/:clientID?", va_clients_callback); // VA Clients Callback for getting all VA's or specific client by ID (NC)
|
||||
clientRoutes.get("/:clientId/projects/:projectId", va_clients_projects_callback); // VA Clients Projects Callback (NC)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { va_timesheet_summary_function } from "../../controllers/va/vaTimesheetA
|
|||
import { va_timesheet_adjustment_function } from "../../controllers/va/vaTimesheetAdjustment/vaTimesheetAdjustment.controller.js";
|
||||
import { get_adjustment_requests_list } from "../../controllers/va/vaTimesheetAdjustment/vaAllTimesheetAdjustmentReuqest.controller.js"
|
||||
import { va_timesheet_manage_adjustment_request } from "../../controllers/va/vaTimesheetAdjustment/vaManageTimesheetAdjustment.controller.js"
|
||||
import { va_verify_email } from "../../controllers/va/vaAuth/vaVerifyEmail.controller.js";
|
||||
|
||||
vaRoutes.get("/", va_callback); // VA Callback for getting active VA
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ vaRoutes.get("/logout", authValidator, va_logout_callback); // VA Callback for l
|
|||
vaRoutes.post("/refreshToken", va_refresh_token_callback); // VA Callback for refresh token
|
||||
vaRoutes.post("/register", va_register); // Register a new VA
|
||||
vaRoutes.post("/forgot-password", va_request_password_reset);
|
||||
vaRoutes.post("/verify-va-email", va_verify_email);
|
||||
|
||||
vaRoutes.get("/profile", authValidator, va_profile_callback); // Get VA Profile
|
||||
vaRoutes.patch("/profile", authValidator, va_profile_update_callback); // Update VA Profile
|
||||
|
|
|
|||
|
|
@ -92,12 +92,18 @@
|
|||
<!-- Step 1: Create Account -->
|
||||
<div class="form-step active" id="step-1">
|
||||
<h2 class="form-title">Create Your Account</h2>
|
||||
<div class="step-error-container" id="step-1-error" style="display: none;">
|
||||
<div class="error-message" id="step-1-error-message"></div>
|
||||
</div>
|
||||
<form id="account-form">
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="company-name" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="company-name" class="input-field" placeholder=" " oninput="validateCompanyName(this)" required>
|
||||
<label for="company-name" class="input-label">Company / Client Name *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="company-name-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Company name should only contain letters, numbers, spaces, hyphens, apostrophes, ampersands, and periods
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
|
|
@ -107,7 +113,7 @@
|
|||
</div>
|
||||
<div class="form-actions">
|
||||
<div></div> <!-- Empty div for spacing -->
|
||||
<button type="button" class="btn btn-primary" onclick="nextStep(2)">Continue</button>
|
||||
<button type="button" class="btn btn-primary" onclick="handleNextStep(2)">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -115,6 +121,9 @@
|
|||
<!-- Step 2: Business Information -->
|
||||
<div class="form-step" id="step-2">
|
||||
<h2 class="form-title">Business Information</h2>
|
||||
<div class="step-error-container" id="step-2-error" style="display: none;">
|
||||
<div class="error-message" id="step-2-error-message"></div>
|
||||
</div>
|
||||
<form id="business-form">
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
|
|
@ -124,9 +133,12 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="city" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="city" class="input-field" placeholder=" " oninput="validateCity(this)" onkeypress="return preventInvalidCityChars(event)" required>
|
||||
<label for="city" class="input-label">City *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="city-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
City name should contain letters and cannot be only numbers or special characters
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
|
|
@ -141,13 +153,16 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="zip" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="zip" class="input-field" placeholder=" " oninput="validateZipCode(this)" onkeypress="return preventNonNumericZip(event)" maxlength="10" required>
|
||||
<label for="zip" class="input-label">ZIP *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="zip-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
ZIP code should contain only numbers
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" onclick="prevStep(1)">Back</button>
|
||||
<button type="button" class="btn btn-primary" onclick="nextStep(3)">Continue</button>
|
||||
<button type="button" class="btn btn-primary" onclick="handleNextStep(3)">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -155,6 +170,9 @@
|
|||
<!-- Step 3: Billing Address -->
|
||||
<div class="form-step" id="step-3">
|
||||
<h2 class="form-title">Billing Address</h2>
|
||||
<div class="step-error-container" id="step-3-error" style="display: none;">
|
||||
<div class="error-message" id="step-3-error-message"></div>
|
||||
</div>
|
||||
<form id="billing-form">
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="use-business-address">
|
||||
|
|
@ -174,9 +192,12 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="billing-city" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="billing-city" class="input-field" placeholder=" " oninput="validateCity(this)" onkeypress="return preventInvalidCityChars(event)" required>
|
||||
<label for="billing-city" class="input-label">City *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="billing-city-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
City name should contain letters and cannot be only numbers or special characters
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
|
|
@ -190,9 +211,12 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="billing-zip" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="billing-zip" class="input-field" placeholder=" " oninput="validateZipCode(this)" onkeypress="return preventNonNumericZip(event)" maxlength="10" required>
|
||||
<label for="billing-zip" class="input-label">ZIP *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="billing-zip-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
ZIP code should contain only numbers
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="use-account-email">
|
||||
|
|
@ -206,7 +230,7 @@
|
|||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" onclick="prevStep(2)">Back</button>
|
||||
<button type="button" class="btn btn-primary" onclick="nextStep(4)">Continue</button>
|
||||
<button type="button" class="btn btn-primary" onclick="handleNextStep(4)">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -214,21 +238,30 @@
|
|||
<!-- Step 4: Authorized Signatory -->
|
||||
<div class="form-step" id="step-4">
|
||||
<h2 class="form-title">Authorized Signatory</h2>
|
||||
<div class="step-error-container" id="step-4-error" style="display: none;">
|
||||
<div class="error-message" id="step-4-error-message"></div>
|
||||
</div>
|
||||
<p style="margin-bottom: 20px; color: #6b7280; font-size: 14px;">
|
||||
We will use the details below in the agreement that we will send to your prior to entering the project.
|
||||
</p>
|
||||
<form id="signatory-form">
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="position" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="position" class="input-field" placeholder=" " oninput="validatePosition(this)" onkeypress="return preventInvalidPositionChars(event)" required>
|
||||
<label for="position" class="input-label">Position (Owner / CEO / Manager / etc.) *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="position-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Position should only contain letters, spaces, and common punctuation
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="signatory-name" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="signatory-name" class="input-field" placeholder=" " oninput="validateSignatoryName(this)" onkeypress="return preventNumbers(event)" required>
|
||||
<label for="signatory-name" class="input-label">Name *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="signatory-name-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Name should not contain numbers
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
|
|
@ -238,7 +271,7 @@
|
|||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" onclick="prevStep(3)">Back</button>
|
||||
<button type="button" class="btn btn-primary" onclick="nextStep(5)">Continue</button>
|
||||
<button type="button" class="btn btn-primary" onclick="handleNextStep(5)">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -246,6 +279,9 @@
|
|||
<!-- Step 5: Payment Details -->
|
||||
<div class="form-step" id="step-5">
|
||||
<h2 class="form-title">Payment Details</h2>
|
||||
<div class="step-error-container" id="step-5-error" style="display: none;">
|
||||
<div class="error-message" id="step-5-error-message"></div>
|
||||
</div>
|
||||
<form id="payment-form">
|
||||
<div class="payment-option" onclick="selectPaymentOption('card')">
|
||||
<h4>Credit or Debit Card</h4>
|
||||
|
|
@ -267,16 +303,22 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="security-code" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="security-code" class="input-field" placeholder=" " oninput="validateCVVVV(this)" nkeypress="return preventInvalidCVVChars(event)" maxlength="4" required>
|
||||
<label for="security-code" class="input-label">Security Code *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="security-code-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Please enter a valid security code
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="card-zip" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="card-zip" class="input-field" placeholder=" " oninput="validateZipCode(this)" onkeypress="return preventNonNumericZip(event)" maxlength="10" required>
|
||||
<label for="card-zip" class="input-label">ZIP Code *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="card-zip-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
ZIP code should contain only numbers
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="payment-option" onclick="selectPaymentOption('bank')">
|
||||
|
|
@ -286,15 +328,21 @@
|
|||
<div id="bank-details" class="payment-details">
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="bank-name" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="bank-name" class="input-field" placeholder=" " oninput="validateBankName(this)" onkeypress="return preventInvalidBankNameChars(event)" required>
|
||||
<label for="bank-name" class="input-label">Bank Name *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="bank-name-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Bank name should contain letters and cannot be only numbers or special characters
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="account-number" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="account-number" class="input-field" placeholder=" " oninput="validateAccountNumber(this)" onkeypress="return preventInvalidAccountNumberChars(event)" required>
|
||||
<label for="account-number" class="input-label">Account Number *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="account-number-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Please enter a valid account number
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
|
|
@ -314,9 +362,12 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="account-holder-name" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="account-holder-name" class="input-field" placeholder=" " oninput="validateAccountHolderName(this)" onkeypress="return preventInvalidAccountHolderChars(event)" required>
|
||||
<label for="account-holder-name" class="input-label">Account Holder Name *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="account-holder-name-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Account holder name should only contain letters and spaces
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
|
|
|
|||
|
|
@ -165,17 +165,20 @@
|
|||
<!-- Step 1: Account Setup -->
|
||||
<div class="form-step active" id="step-1">
|
||||
<h2 class="form-title">Account Setup</h2>
|
||||
<div class="step-error-container" id="step-1-error" style="display: none;">
|
||||
<div class="error-message" id="step-1-error-message"></div>
|
||||
</div>
|
||||
<form id="account-form">
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="email" id="email" class="input-field" placeholder=" " required>
|
||||
<input type="email" id="email" class="input-field" placeholder=" " required autocomplete="off">
|
||||
<label for="email" class="input-label">Email Address *</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="password" id="password" class="input-field" placeholder=" " required>
|
||||
<input type="password" id="password" class="input-field" placeholder=" " required autocomplete="off">
|
||||
<label for="password" class="input-label">Create Password *</label>
|
||||
</div>
|
||||
<div class="password-actions">
|
||||
|
|
@ -186,7 +189,7 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="password" id="confirmPassword" class="input-field" placeholder=" " required>
|
||||
<input type="password" id="confirmPassword" class="input-field" placeholder=" " required autocomplete="off">
|
||||
<label for="confirmPassword" class="input-label">Confirm Password *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="password-match-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">Passwords do not match</div>
|
||||
|
|
@ -194,7 +197,7 @@
|
|||
|
||||
<div class="form-actions">
|
||||
<div></div>
|
||||
<button type="button" class="btn btn-primary" onclick="nextStep(2)">Continue</button>
|
||||
<button type="button" class="btn btn-primary" onclick="handleNextStep(2)">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -202,7 +205,9 @@
|
|||
<!-- Step 2: Personal Information -->
|
||||
<div class="form-step" id="step-2">
|
||||
<h2 class="form-title">Personal Information</h2>
|
||||
|
||||
<div class="step-error-container" id="step-2-error" style="display: none;">
|
||||
<div class="error-message" id="step-2-error-message"></div>
|
||||
</div>
|
||||
<div class="profile-incomplete" id="profile-incomplete" style="display: none;">
|
||||
Profile is not yet complete, first name is missing
|
||||
</div>
|
||||
|
|
@ -232,7 +237,7 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="date" id="dob" class="input-field" placeholder=" ">
|
||||
<input type="date" id="dob" class="input-field dob" placeholder=" " onchange="handleDOBChange()">
|
||||
<label for="dob" class="input-label">Date of Birth</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -274,7 +279,7 @@
|
|||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" onclick="prevStep(1)">Back</button>
|
||||
<button type="button" class="btn btn-primary" onclick="nextStep(3)">Continue</button>
|
||||
<button type="button" class="btn btn-primary" onclick="handleNextStep(3)">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -282,13 +287,19 @@
|
|||
<!-- Step 3: Contact Information -->
|
||||
<div class="form-step" id="step-3">
|
||||
<h2 class="form-title">Contact Information</h2>
|
||||
<div class="step-error-container" id="step-3-error" style="display: none;">
|
||||
<div class="error-message" id="step-3-error-message"></div>
|
||||
</div>
|
||||
<form id="contact-form">
|
||||
<div class="form-group">
|
||||
<label for="phone" class="input-label">Phone Number *</label>
|
||||
<div class="input-container">
|
||||
<input type="tel" id="phone" class="input-field" placeholder=" " required>
|
||||
<input type="tel" id="phone" class="input-field" placeholder=" " pattern="[0-9]*" minlength="10" maxlength="15" oninput="validatePhoneNumber(this)" required>
|
||||
<label for="phone" class="input-label">+ Phone Number</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="phone-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Please enter a valid phone number (10-15 digits)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
@ -300,7 +311,7 @@
|
|||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" onclick="prevStep(2)">Back</button>
|
||||
<button type="button" class="btn btn-primary" onclick="nextStep(4)">Continue</button>
|
||||
<button type="button" class="btn btn-primary" onclick="handleNextStep(4)">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -308,6 +319,9 @@
|
|||
<!-- Step 4: Payment Details -->
|
||||
<div class="form-step" id="step-4">
|
||||
<h2 class="form-title">Payment Details</h2>
|
||||
<div class="step-error-container" id="step-4-error" style="display: none;">
|
||||
<div class="error-message" id="step-4-error-message"></div>
|
||||
</div>
|
||||
<form id="payment-form">
|
||||
<div class="form-group">
|
||||
<div class="payment-option" onclick="selectPaymentMethod('payoneer')">
|
||||
|
|
@ -317,9 +331,12 @@
|
|||
<div id="payoneer-details" class="payment-details">
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="payoneerAccount" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="payoneerAccount" class="input-field" placeholder=" " minlength="5" maxlength="20" oninput="validatePayoneerAccount(this)" required>
|
||||
<label for="payoneerAccount" class="input-label">Payoneer Account Number *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="payoneer-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Account number must be 5-20 characters
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -330,9 +347,12 @@
|
|||
<div id="veem-details" class="payment-details">
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="veemAccount" class="input-field" placeholder=" " required>
|
||||
<input type="text" id="veemAccount" class="input-field" placeholder=" " minlength="8" maxlength="25" oninput="validateVeemAccount(this)" required>
|
||||
<label for="veemAccount" class="input-label">Veem Account Number *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="veem-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Account number must be 8-25 characters
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue