// popup.js var PROFESSION_COLORS = { 'Full-Stack Engineer': '#4C6EF5', 'AI Engineer': '#9C36B5', 'Data Scientist': '#0CA678', 'Front-End Engineer': '#E8590C', }; var DEFAULT_PROFESSION_COLOR = '#C9971C'; // Recomputes "X ago" fresh every render from the stored absolute timestamp, // so it's always accurate instead of a stale cached string. function formatTimeAgo(timestamp) { if (!timestamp) return ''; var diffMs = Date.now() - timestamp; if (diffMs < 0) diffMs = 0; var minutes = Math.floor(diffMs / (60 * 1000)); var hours = Math.floor(diffMs / (60 * 60 * 1000)); var days = Math.floor(diffMs / (24 * 60 * 60 * 1000)); var weeks = Math.floor(days / 7); var months = Math.floor(days / 30); if (minutes < 60) return ( 'Posted ' + Math.max(minutes, 1) + ' minute' + (minutes === 1 ? '' : 's') + ' ago' ); if (hours < 24) return 'Posted ' + hours + ' hour' + (hours === 1 ? '' : 's') + ' ago'; if (days < 7) return 'Posted ' + days + ' day' + (days === 1 ? '' : 's') + ' ago'; if (weeks < 5) return 'Posted ' + weeks + ' week' + (weeks === 1 ? '' : 's') + ' ago'; return 'Posted ' + months + ' month' + (months === 1 ? '' : 's') + ' ago'; } function sortJobsById(jobs) { return jobs.slice().sort(function (a, b) { return (parseInt(b.id, 10) || 0) - (parseInt(a.id, 10) || 0); }); } function sortWithNewFirst(jobs, newBadgeUntil, now) { return jobs.slice().sort(function (a, b) { var aNew = newBadgeUntil && newBadgeUntil[a.id] && newBadgeUntil[a.id] > now ? 1 : 0; var bNew = newBadgeUntil && newBadgeUntil[b.id] && newBadgeUntil[b.id] > now ? 1 : 0; if (aNew !== bNew) return bNew - aNew; return (parseInt(b.id, 10) || 0) - (parseInt(a.id, 10) || 0); }); } function renderJob(jobList, job, newBadgeUntil, now) { var jobElement = document.createElement('div'); jobElement.className = 'job-item'; var startInfo = job.posted_date || ''; var postedAgo = formatTimeAgo(job.posted_timestamp); var companyName = typeof job.company === 'object' ? (job.company && job.company.name) || 'Company' : job.company || 'Company'; var profession = job.professionalTitle || 'Full-Stack Engineer'; var professionColor = PROFESSION_COLORS[profession] || DEFAULT_PROFESSION_COLOR; var isNew = newBadgeUntil && newBadgeUntil[job.id] && newBadgeUntil[job.id] > now; var newBadgeHtml = isNew ? 'NEW' : ''; jobElement.innerHTML = '
' + newBadgeHtml + job.title + '
' + '
' + companyName + '
' + '
' + (job.location || 'Remote') + '
' + '
' + startInfo + '' + postedAgo + '
' + '
' + profession + '
'; jobElement.addEventListener('click', function () { var url = job.url || 'https://app.andela.com/job-postings/' + job.id; chrome.tabs.create({ url: url }); }); jobList.appendChild(jobElement); } function updateUI(data) { var lastAndelaJobs = data.lastAndelaJobs || []; var lastAndelaUpdated = data.lastAndelaUpdated; var totalAndelaMatching = data.totalAndelaMatching || 0; var newBadgeUntil = data.newBadgeUntil || {}; var now = Date.now(); document.getElementById('jobCount').textContent = totalAndelaMatching + ' open opportunities'; var jobList = document.getElementById('jobList'); jobList.innerHTML = ''; if (lastAndelaJobs.length === 0) { jobList.innerHTML = '
No jobs found yet. Click "Check Now" to scan for jobs.
'; var status0 = document.getElementById('status'); status0.textContent = lastAndelaUpdated ? 'Last checked: ' + new Date(lastAndelaUpdated).toLocaleString() : 'Not checked yet'; return; } var opportunities = sortWithNewFirst( lastAndelaJobs.filter(function (j) { return j.kind === 'opportunity'; }), newBadgeUntil, now, ); var applied = sortJobsById( lastAndelaJobs.filter(function (j) { return j.kind !== 'opportunity'; }), ); if (opportunities.length) { var oppHeading = document.createElement('div'); oppHeading.className = 'section-heading section-heading-opportunities'; oppHeading.textContent = 'Open Opportunities'; jobList.appendChild(oppHeading); opportunities.slice(0, 30).forEach(function (job) { renderJob(jobList, job, newBadgeUntil, now); }); } if (applied.length) { var appliedHeading = document.createElement('div'); appliedHeading.className = 'section-heading section-heading-applied'; appliedHeading.textContent = 'Applied'; jobList.appendChild(appliedHeading); applied.slice(0, 10).forEach(function (job) { renderJob(jobList, job, newBadgeUntil, now); }); } var status = document.getElementById('status'); status.textContent = lastAndelaUpdated ? 'Last checked: ' + new Date(lastAndelaUpdated).toLocaleString() : 'Not checked yet'; } document.addEventListener('DOMContentLoaded', function () { document.getElementById('checkNow').addEventListener('click', function () { chrome.runtime.sendMessage({ action: 'checkNow' }, function () {}); }); document .getElementById('openSettings') .addEventListener('click', function () { chrome.tabs.create({ url: 'settings.html' }); }); chrome.storage.local.get( [ 'lastAndelaJobs', 'lastAndelaUpdated', 'totalAndelaMatching', 'newBadgeUntil', ], updateUI, ); chrome.storage.onChanged.addListener(function (changes, areaName) { if ( areaName === 'local' && (changes.lastAndelaJobs || changes.lastAndelaUpdated || changes.totalAndelaMatching || changes.newBadgeUntil) ) { chrome.storage.local.get( [ 'lastAndelaJobs', 'lastAndelaUpdated', 'totalAndelaMatching', 'newBadgeUntil', ], updateUI, ); } }); });