211 lines
6.1 KiB
JavaScript
211 lines
6.1 KiB
JavaScript
// 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 ? '<span class="new-badge">NEW</span>' : '';
|
|
|
|
jobElement.innerHTML =
|
|
'<div class="job-title">' +
|
|
newBadgeHtml +
|
|
job.title +
|
|
'</div>' +
|
|
'<div class="job-company">' +
|
|
companyName +
|
|
'</div>' +
|
|
'<div class="job-location">' +
|
|
(job.location || 'Remote') +
|
|
'</div>' +
|
|
'<div class="job-posted-row"><span>' +
|
|
startInfo +
|
|
'</span><span>' +
|
|
postedAgo +
|
|
'</span></div>' +
|
|
'<div class="profession-tag" style="color:' +
|
|
professionColor +
|
|
'">' +
|
|
profession +
|
|
'</div>';
|
|
|
|
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 =
|
|
'<div class="no-jobs">No jobs found yet. Click "Check Now" to scan for jobs.</div>';
|
|
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,
|
|
);
|
|
}
|
|
});
|
|
});
|