// ===== DATA =====
const STEPS = [
{ id: 1, label: 'Account' }, { id: 2, label: 'Verify 1' }, { id: 3, label: 'Verify 2' },
{ id: 4, label: 'Verify 3' }, { id: 5, label: 'Work Type' }, { id: 6, label: 'Category' },
{ id: 7, label: 'Career Level' }, { id: 8, label: 'Location' }, { id: 9, label: 'Schedule' },
{ id: 10, label: 'Travel' }, { id: 11, label: 'Results' },
];
const WORK_TYPES = [
{ value: 'Remote', label: '100% Remote Work', desc: 'Work from anywhere, no office required' },
{ value: 'Hybrid', label: 'Hybrid Remote Work', desc: 'Split time between home and office' },
{ value: 'Flexible Schedule', label: 'Flexible Schedule', desc: 'Set your own hours within guidelines' },
{ value: 'Alternative Schedule', label: 'Alternative Schedule', desc: 'Non-traditional hours (evenings, weekends)' },
{ value: 'Part-Time', label: 'Part-Time', desc: 'Less than 40 hours per week' },
{ value: 'Freelance', label: 'Freelance', desc: 'Project-based independent work' },
];
const CAREER_LEVELS = [
{ value: 'Entry-Level', label: 'Entry-Level', desc: '0-2 years experience, new graduates welcome' },
{ value: 'Mid-Level', label: 'Mid-Level', desc: '3-5 years experience, building expertise' },
{ value: 'Senior-Level', label: 'Senior-Level', desc: '5+ years, leadership and advanced skills' },
{ value: 'Manager', label: 'Manager', desc: 'Team leadership and people management' },
{ value: 'Director', label: 'Director', desc: 'Department oversight and strategic planning' },
{ value: 'Executive', label: 'Executive', desc: 'C-suite, VP, and C-level positions' },
];
const SCHEDULES = [
{ value: 'Full-Time', label: 'Full-Time', desc: '40+ hours per week, standard benefits' },
{ value: 'Part-Time', label: 'Part-Time', desc: 'Less than 40 hours, flexible arrangement' },
{ value: 'Flexible Schedule', label: 'Flexible Schedule', desc: 'Core hours with flexibility around them' },
{ value: 'Alternative Schedule', label: 'Alternative Schedule', desc: 'Evenings, weekends, or rotating shifts' },
{ value: 'Seasonal', label: 'Seasonal', desc: 'Temporary work during peak periods' },
{ value: 'Occasional', label: 'Occasional', desc: 'As-needed or on-call basis' },
];
const TRAVEL_OPTIONS = [
{ value: 'No Travel', label: 'No Travel', desc: '100% remote, no business travel required' },
{ value: 'Some Travel', label: 'Some Travel', desc: 'Occasional trips (less than 25%)' },
{ value: 'Regular Travel', label: 'Regular Travel', desc: 'Frequent travel (25-50% of time)' },
{ value: 'Extensive Travel', label: 'Extensive Travel', desc: 'Heavy travel (50%+ of time)' },
];
const CATEGORIES = [
'Accounting & Finance', 'Administrative', 'Advertising & PR', 'Animation & Multimedia',
'Art & Creative', 'Bilingual', 'Business Development', 'Communications',
'Computer & IT', 'Consulting', 'Customer Service', 'Data Entry',
'Education & Training', 'Engineering', 'Entertainment & Media', 'Graphic Design',
'HR & Recruiting', 'Insurance', 'Internet & Ecommerce', 'Legal',
'Marketing', 'Medical & Health', 'Nonprofit & Philanthropy', 'Nursing',
'Operations', 'Project Management', 'Sales', 'Science',
'Software Development', 'Technical Support', 'Transcription', 'Virtual Assistant', 'Writing',
];
const LOCATION_CHIPS = [
'Anywhere in U.S.', 'Anywhere in World', 'US National', 'Canada', 'Europe', 'Asia Pacific', 'Latin America',
];
const FORMSPREE_ENDPOINT = 'https://formspree.io/f/mojoalwv';
// ===== STATE =====
let currentStep = 1;
let state = {
userInfo: { name: '', phone: '', email: '' },
workType: null, categories: [], careerLevel: null,
locations: [], schedule: null, travel: null, verificationComplete: false,
};
// ===== RENDER FUNCTIONS =====
function renderStepper() {
const el = document.getElementById('stepper');
let html = '';
STEPS.forEach((step, index) => {
const isActive = step.id === currentStep;
const isDone = step.id < currentStep;
const cls = isActive ? 'step-active' : isDone ? 'step-done' : 'step-pending';
html += `
${isDone ? '✓' : step.id}
${step.label} `;
if (index < STEPS.length - 1) {
html += `
`;
}
});
el.innerHTML = html;
}
function goToStep(step) {
if (step < currentStep) { currentStep = step; render(); }
}
function goNext() { if (currentStep < 11) { currentStep++; render(); } }
function resetWizard() {
currentStep = 1;
state = { userInfo: { name: '', phone: '', email: '' }, workType: null, categories: [], careerLevel: null, locations: [], schedule: null, travel: null, verificationComplete: false };
render();
}
// ===== STEP RENDERS =====
function renderStep1() {
const isValid = state.userInfo.name.trim() && state.userInfo.email.trim() && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(state.userInfo.email);
return `
👤
Create Your Account
Please enter your details to get started.
`;
}
function renderStep1Update() {
const btn = document.querySelector('#mainCard .btn-primary');
const msg = document.querySelector('#mainCard .text-xs');
const isValid = state.userInfo.name.trim() && state.userInfo.email.trim() && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(state.userInfo.email);
if (btn) btn.disabled = !isValid;
if (msg) msg.style.display = isValid ? 'none' : 'block';
}
function renderStep2() {
return `
🛡
Verification 1 of 3
Prove You're Human
Solve this simple math challenge to continue.
`;
}
function checkMath() {
const ans = document.getElementById('mathAnswer').value;
if (parseInt(ans) === 15) { goNext(); }
else { document.getElementById('mathError').classList.remove('hidden'); document.getElementById('mathAnswer').value = ''; }
}
function renderStep3() {
return `
✅
Verification 2 of 3
Visual Challenge
Click on the ★ Star icon below.
${['Heart','Square','Star','Circle','Triangle','Heart'].map((label,i) => `
${label==='Star'?'★':label==='Heart'?'♥':label==='Square'?'▢':label==='Circle'?'○':'△'}
${label}
`).join('')}
That's not the Star. Look carefully and try again!
✓ Verified! Moving to next challenge...
`;
}
function checkShape(shape, btn) {
if (shape === 'star') {
btn.classList.add('correct');
document.getElementById('shapeError').classList.add('hidden');
document.getElementById('shapeSuccess').classList.remove('hidden');
setTimeout(() => goNext(), 500);
} else {
btn.classList.add('wrong');
document.getElementById('shapeError').classList.remove('hidden');
setTimeout(() => btn.classList.remove('wrong'), 500);
}
}
function renderStep4() {
return `
🙋
Verification 3 of 3
Final Confirmation
Complete all checks below to proceed.
${renderCheckbox('human', 'I am a human', 'I confirm that I am a real person, not a bot or automated system.')}
${renderCheckbox('terms', 'I agree to the Terms of Use', 'I have read and accept the privacy policy and terms.')}
${renderCheckbox('consent', 'I consent to data processing', 'My information will be used solely for job matching purposes.')}
0/3
Complete Verification ›
`;
}
function renderCheckbox(id, title, desc) {
return `
`;
}
let checks = { human: false, terms: false, consent: false };
function toggleCheckbox(id) {
checks[id] = !checks[id];
const card = document.getElementById('cb_' + id);
const box = document.getElementById('cbbox_' + id);
if (checks[id]) { card.classList.add('checked'); box.innerHTML = '✓'; box.style.color = 'white'; }
else { card.classList.remove('checked'); box.innerHTML = ''; }
const count = Object.values(checks).filter(Boolean).length;
document.getElementById('verifyProgress').style.width = (count/3*100) + '%';
document.getElementById('verifyCount').textContent = count + '/3';
document.getElementById('verifyBtn').disabled = count < 3;
}
function completeVerify() { state.verificationComplete = true; goNext(); }
function renderStep5() {
return renderSelectionStep('Work Type', 'Let's find your perfect work arrangement!', 'What type of work flexibility are you looking for?', WORK_TYPES, state.workType, 'workType');
}
function renderStep7() {
return renderSelectionStep('Career Level', 'What career level are you targeting?', 'Select the experience level that matches your qualifications.', CAREER_LEVELS, state.careerLevel, 'careerLevel');
}
function renderStep9() {
return renderSelectionStep('Schedule', 'What schedule works best for you?', 'Select your preferred work schedule type.', SCHEDULES, state.schedule, 'schedule');
}
function renderStep10() {
return renderSelectionStep('Travel', 'How much travel are you comfortable with?', 'Select your preferred travel requirements for the role.', TRAVEL_OPTIONS, state.travel, 'travel');
}
function renderSelectionStep(title, heading, subtext, options, currentValue, stateKey) {
return `
${options.map((opt, i) => `
💼
${opt.label}
${opt.desc}
`).join('')}
`;
}
function selectOption(key, value) {
state[key] = value;
render();
setTimeout(() => goNext(), 600);
}
function renderStep6() {
const slots = state.categories.length;
const isFull = slots >= 5;
return `
No worries! We've got you covered.
Select up to 5 job categories that interest you.
Selected: ${slots} /5 categories${isFull ? 'All full! ' : ''}
${slots > 0 ? `🗑 Clear All ` : ''}
${state.categories.map(cat => `${cat} × `).join('')}
${slots > 0 && slots < 5 ? `Continue ›
` : ''}
${CATEGORIES.map(cat => `
💼
${cat}
`).join('')}
`;
}
function toggleCat(cat) {
if (state.categories.includes(cat)) { state.categories = state.categories.filter(c => c !== cat); }
else if (state.categories.length < 5) { state.categories.push(cat); }
render();
}
function removeCat(cat) { state.categories = state.categories.filter(c => c !== cat); render(); }
function renderStep8() {
return `
Where would you like to work?
Choose your preferred work location or region.
ⓘ
About 95% of remote jobs have location requirements. Select your region to find jobs you qualify for.
${LOCATION_CHIPS.map(chip => `
${chip}
`).join('')}
${state.locations.map(loc => `${loc} × `).join('')}
`;
}
function toggleLocation(loc) {
if (state.locations.includes(loc)) { state.locations = state.locations.filter(l => l !== loc); }
else { state.locations.push(loc); if (state.locations.length === 1) setTimeout(() => goNext(), 600); }
render();
}
function removeLoc(loc) { state.locations = state.locations.filter(l => l !== loc); render(); }
function addCustomLoc() {
const val = document.getElementById('customLoc').value.trim();
if (val && !state.locations.includes(val)) { state.locations.push(val); if (state.locations.length === 1) setTimeout(() => goNext(), 600); }
document.getElementById('customLoc').value = '';
render();
}
function renderStep11() {
const summary = [
{ label: 'Work Type', value: state.workType || 'Not selected' },
{ label: 'Job Categories', value: state.categories.join(', ') || 'Not selected' },
{ label: 'Career Level', value: state.careerLevel || 'Not selected' },
{ label: 'Location', value: state.locations.join(', ') || 'Not selected' },
{ label: 'Schedule', value: state.schedule || 'Not selected' },
{ label: 'Travel', value: state.travel || 'Not selected' },
];
return `
🎉
Your job preferences are ready!
Here's a summary of what you're looking for.
Registered User
👤 ${state.userInfo.name}
${state.userInfo.phone ? `
📞 ${state.userInfo.phone}
` : ''}
✉ ${state.userInfo.email}
Your Job Preferences
${summary.map(item => `
${item.label}
${item.value}
`).join('')}
✉
Ready to submit
Click "Download & Send" to save your preferences and submit your application.
↺ Start Over
📥 Download & Send
`;
}
async function handleDownload() {
const btn = document.getElementById('downloadBtn');
const statusBox = document.getElementById('statusBox');
const statusTitle = document.getElementById('statusTitle');
const statusText = document.getElementById('statusText');
// Download file
const now = new Date();
const content = generateFileContent();
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `FlexJobs-${state.userInfo.name.replace(/\s+/g, '-') || 'User'}-Preferences-${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}-${String(now.getDate()).padStart(2,'0')}.txt`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
// Update UI
btn.innerHTML = '✓ Sent!';
btn.classList.remove('btn-primary');
btn.classList.add('btn-success');
statusBox.className = 'status-box status-emerald';
statusTitle.textContent = 'Notification sent!';
statusText.textContent = 'Your application has been submitted for review.';
// Send to Formspree
try {
const nowStr = now.toLocaleString('en-US');
const params = new URLSearchParams();
params.append('email', state.userInfo.email || 'noreply@flexjobs.com');
params.append('subject', `New Job Application - ${state.userInfo.name || 'Unknown'}`);
params.append('message', `NEW JOB PREFERENCES SUBMISSION\n\nSubmitted: ${nowStr}\n\nUSER INFORMATION:\n- Name: ${state.userInfo.name || 'N/A'}\n- Phone: ${state.userInfo.phone || 'N/A'}\n- Email: ${state.userInfo.email || 'N/A'}\n- Verification: ${state.verificationComplete ? 'PASSED (3/3)' : 'INCOMPLETE'}\n\nJOB PREFERENCES:\n- Work Type: ${state.workType || 'Not selected'}\n- Categories: ${state.categories.join(', ') || 'Not selected'}\n- Career Level: ${state.careerLevel || 'Not selected'}\n- Location: ${state.locations.join(', ') || 'Not selected'}\n- Schedule: ${state.schedule || 'Not selected'}\n- Travel: ${state.travel || 'Not selected'}\n\n---\nSent via FlexJobs Job Wizard`);
params.append('_replyto', state.userInfo.email || 'noreply@flexjobs.com');
const response = await fetch(FORMSPREE_ENDPOINT, {
method: 'POST',
body: params.toString(),
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },
});
if (!response.ok) {
console.error('Formspree error:', await response.text());
statusBox.className = 'status-box status-amber';
statusTitle.textContent = 'Downloaded but email may have failed';
statusText.textContent = 'Please check your Formspree form is active.';
}
} catch (err) {
console.error('Email error:', err);
statusBox.className = 'status-box status-amber';
statusTitle.textContent = 'Downloaded but email failed';
statusText.textContent = 'Network error. Please check your connection.';
}
}
function generateFileContent() {
const now = new Date();
const dateStr = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
return `============================================================
FLEXJOBS JOB PREFERENCES SUMMARY
============================================================
Generated on: ${dateStr} at ${timeStr}
============================================================
USER INFORMATION
============================================================
Name : ${state.userInfo.name || 'N/A'}
Phone : ${state.userInfo.phone || 'N/A'}
Email : ${state.userInfo.email || 'N/A'}
Verification : ${state.verificationComplete ? 'PASSED (3/3)' : 'INCOMPLETE'}
============================================================
YOUR SELECTIONS
============================================================
Work Type : ${state.workType || 'Not selected'}
Job Categories : ${state.categories.length > 0 ? state.categories.join('\n ') : 'Not selected'}
Career Level : ${state.careerLevel || 'Not selected'}
Location : ${state.locations.length > 0 ? state.locations.join('\n ') : 'Not selected'}
Schedule : ${state.schedule || 'Not selected'}
Travel Preference : ${state.travel || 'Not selected'}
------------------------------------------------------------
QUICK REFERENCE
------------------------------------------------------------
Name : ${state.userInfo.name || 'N/A'}
Remote Work Type : ${state.workType || 'N/A'}
Experience Level : ${state.careerLevel || 'N/A'}
Work Schedule : ${state.schedule || 'N/A'}
Travel Required : ${state.travel || 'N/A'}
------------------------------------------------------------
NOTES
------------------------------------------------------------
- About 95% of remote jobs have location requirements.
- Use this summary to refine your search on FlexJobs.
- Consider saving multiple preference profiles for
different job search scenarios.
- This file was generated after 3-step human verification.
============================================================
FlexJobs - Find Your Perfect Remote Job
https://www.flexjobs.com
============================================================
`;
}
// ===== MAIN RENDER =====
function render() {
renderStepper();
const card = document.getElementById('mainCard');
document.getElementById('userName').textContent = state.userInfo.name && currentStep > 1 ? state.userInfo.name : '';
switch(currentStep) {
case 1: card.innerHTML = renderStep1(); break;
case 2: card.innerHTML = renderStep2(); break;
case 3: card.innerHTML = renderStep3(); break;
case 4: checks = { human: false, terms: false, consent: false }; card.innerHTML = renderStep4(); break;
case 5: card.innerHTML = renderStep5(); break;
case 6: card.innerHTML = renderStep6(); break;
case 7: card.innerHTML = renderStep7(); break;
case 8: card.innerHTML = renderStep8(); break;
case 9: card.innerHTML = renderStep9(); break;
case 10: card.innerHTML = renderStep10(); break;
case 11: card.innerHTML = renderStep11(); break;
}
card.className = 'card fade-in';
}
// Init
render();