/* ============================================================ import.js — Manual CV Import (drag&drop, parse, match, dedupe) ============================================================ */ window.Views = window.Views || {}; window.CVImport = {}; Views.import = function () { const queue = []; // {name, size, status, atsScore, matchedJob, duplicate} const html = `

CV Import

Upload resumes — we parse, score, match, and dedupe automatically

AI Resume Parser · Ready
${UI.icon('upload')}

Drag & drop resumes here

or click to browse — PDF, DOC, DOCX and ZIP supported · up to 20 files

${['PDF', 'DOC', 'DOCX', 'ZIP'].map(t => `${t}`).join('')}
Files are processed locally in this demo

Auto-Processing

What happens on upload
${[ { i: 'file', t: 'Resume parsing', d: 'Extract name, contact, experience, skills & education' }, { i: 'target', t: 'ATS scoring', d: 'Generate a match score against the requisition' }, { i: 'briefcase', t: 'Job matching', d: 'Suggest the best-matching open roles' }, { i: 'users', t: 'Duplicate detection', d: 'Flag candidates already in the system' }, { i: 'user-plus', t: 'Profile creation', d: 'Create a candidate profile in Applied stage' } ].map(s => `
${UI.icon(s.i)}
${s.t}
${s.d}
`).join('')}
`; CVImport._queue = queue; return { html, onMount() { const dz = document.getElementById('dropzone'); const browse = document.getElementById('browseBtn'); dz.addEventListener('click', () => CVImport.simulate(DB.int(2, 4))); browse.addEventListener('click', e => { e.stopPropagation(); CVImport.simulate(DB.int(2, 4)); }); dz.addEventListener('dragover', e => { e.preventDefault(); dz.classList.add('drag'); }); dz.addEventListener('dragleave', () => dz.classList.remove('drag')); dz.addEventListener('drop', e => { e.preventDefault(); dz.classList.remove('drag'); CVImport.simulate(e.dataTransfer.files.length || DB.int(2, 4)); }); CVImport._renderQueue(); } }; }; CVImport.simulate = function (count, isZip) { const n = isZip ? 8 : count; const jobs = DB.jobs.filter(j => j.status === 'Open'); for (let k = 0; k < n; k++) { const name = DB.pick(['Olivia', 'Liam', 'Emma', 'Noah', 'Ava', 'Ethan', 'Sophia', 'Mason', 'Priya', 'Diego', 'Yuki', 'Omar']) + ' ' + DB.pick(['Chen', 'Patel', 'Kim', 'Garcia', 'Silva', 'Ahmed', 'Novak', 'Reyes', 'Khan', 'Costa']); const item = { id: 'UP-' + Math.random().toString(36).slice(2, 8), name, file: name.split(' ')[0] + '_Resume.' + DB.pick(['pdf', 'docx', 'doc']), size: DB.int(120, 620) + ' KB', progress: 0, status: 'Uploading', atsScore: null, job: DB.pick(jobs.length ? jobs : DB.jobs), duplicate: Math.random() < 0.18, imported: false }; CVImport._queue.push(item); CVImport._process(item); } document.getElementById('queueCard').style.display = ''; UI.toast(isZip ? 'ZIP extracted — 8 resumes queued' : n + ' file(s) uploaded', 'info'); CVImport._renderQueue(); }; CVImport._process = function (item) { const tick = setInterval(() => { item.progress += DB.int(12, 30); if (item.progress >= 100) { item.progress = 100; clearInterval(tick); item.status = 'Parsing'; CVImport._renderQueue(); setTimeout(() => { item.status = 'Ready'; item.atsScore = DB.int(52, 96); CVImport._renderQueue(); }, 700 + DB.int(0, 500)); } CVImport._renderQueue(); }, 220); }; CVImport._renderQueue = function () { const el = document.getElementById('queueList'); if (!el) return; const q = CVImport._queue; document.getElementById('queueSub').textContent = q.length + ' file' + (q.length === 1 ? '' : 's') + ' · ' + q.filter(i => i.imported).length + ' imported'; el.innerHTML = q.map(i => `
${UI.icon('file')}
${i.name} ${i.duplicate ? 'DUPLICATE' : ''}
${i.file} · ${i.size}
${i.status === 'Uploading' || i.status === 'Parsing' ? `
` : `
Best match: ${i.job.title}
`}
${i.status === 'Ready' ? UI.scoreChip(i.atsScore) : `${i.status}${i.status === 'Uploading' ? ' ' + i.progress + '%' : ''}`}
${i.imported ? `Imported` : i.status === 'Ready' ? `` : ``}
`).join(''); }; CVImport.importOne = function (id) { const i = CVImport._queue.find(x => x.id === id); if (!i || i.imported) return; if (i.duplicate) { UI.modal({ title: 'Duplicate Detected', subtitle: i.name, body: `
${UI.icon('users')}

A similar candidate already exists

${i.name} matches an existing profile (95% similarity on name + email). Importing will create a duplicate.

`, footer: ` ` }); return; } CVImport._doImport(id); }; CVImport._doImport = function (id) { const i = CVImport._queue.find(x => x.id === id); const job = i.job; DB.candidates.unshift({ id: 'CAN-' + (5001 + DB.candidates.length), name: i.name, initials: DB.initials(i.name), color: DB.avatarColor(i.name), email: i.name.toLowerCase().replace(/ /g, '.') + '@email.com', phone: '+1 (555) 000-0000', jobId: job.id, jobTitle: job.title, department: job.department, experience: DB.int(2, 12), currentCompany: DB.pick(DB.companies), currentTitle: job.title, location: DB.pick(DB.locations), stage: 'Applied', status: 'Applied', aiScore: i.atsScore, source: 'Manual CV Upload', recruiter: DB.pick(DB.recruiters).name, recruiterId: '', applied: new Date('2026-07-09'), education: "Bachelor's Degree", skills: job.skills.slice(0, 4), rating: '4.0', salary: DB.int(90, 180) * 1000, matchedSkills: job.skills.slice(0, 3), missingSkills: job.skills.slice(3), recommendation: i.atsScore >= 82 ? 'Strong Match' : 'Potential Match', subScores: { skills: i.atsScore, experience: 80, education: 80, keywords: i.atsScore, location: 100, salary: 90 }, noticePeriod: '1 month', availability: '2 weeks', certifications: [], favorite: false, interviewStatus: 'Not Scheduled' }); i.imported = true; CVImport._renderQueue(); App.updateBadges(); UI.toast(`${i.name} imported → ${job.title}`, 'success'); }; CVImport.importAll = function () { const ready = CVImport._queue.filter(i => i.status === 'Ready' && !i.imported && !i.duplicate); if (!ready.length) { UI.toast('No files ready to import', 'warning'); return; } ready.forEach(i => CVImport._doImport(i.id)); UI.toast(`${ready.length} candidates imported`, 'success'); };