Fixed multi-job scrape, and added labels and single job scrape

This commit is contained in:
2026-07-10 00:56:26 +00:00
parent 8155b121da
commit a1399b60db
5 changed files with 680 additions and 378 deletions

View File

@@ -1,15 +1,94 @@
// popup.js
var TITLE_ORDER = [
'Full-Stack Engineer',
'AI Engineer',
'Data Scientist',
'Front-End Engineer',
];
var PROFESSION_BADGES = {
'Full-Stack Engineer': { label: 'FS', color: '#4C6EF5' },
'AI Engineer': { label: 'AI', color: '#9C36B5' },
'Data Scientist': { label: 'DS', color: '#0CA678' },
'Front-End Engineer': { label: 'FE', color: '#E8590C' },
};
function sortJobsById(jobs) {
return jobs.slice().sort(function (a, b) {
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 = job.posted_ago || '';
var companyName =
typeof job.company === 'object'
? (job.company && job.company.name) || 'Company'
: job.company || 'Company';
var isNew =
newBadgeUntil && newBadgeUntil[job.id] && newBadgeUntil[job.id] > now;
var badgeHtml = '';
if (isNew) {
var profession = job.professionalTitle || 'Full-Stack Engineer';
var badgeInfo = PROFESSION_BADGES[profession] || {
label: 'NEW',
color: '#555555',
};
badgeHtml =
'<span class="new-badge" style="background-color:' +
badgeInfo.color +
'">' +
badgeInfo.label +
' \u00B7 NEW</span>';
}
jobElement.innerHTML =
'<div class="job-title">' +
badgeHtml +
job.title +
'</div>' +
'<div class="job-company">' +
companyName +
'</div>' +
'<div class="job-location">' +
(job.location || 'Remote') +
'</div>' +
'<div class="job-posted">' +
startInfo +
(postedAgo ? ' - ' + postedAgo : '') +
'</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 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 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';
@@ -20,67 +99,62 @@ function updateUI(data) {
if (lastAndelaJobs.length === 0) {
jobList.innerHTML =
'<div class="no-jobs">No jobs found yet. Click "Check Now" to scan for jobs.</div>';
} else {
var opportunities = sortJobsById(
lastAndelaJobs.filter(function (j) {
return j.kind === 'opportunity';
}),
);
var applied = sortJobsById(
lastAndelaJobs.filter(function (j) {
return j.kind !== 'opportunity';
}),
);
var status0 = document.getElementById('status');
status0.textContent = lastAndelaUpdated
? 'Last checked: ' + new Date(lastAndelaUpdated).toLocaleString()
: 'Not checked yet';
return;
}
function renderJob(job) {
var jobElement = document.createElement('div');
jobElement.className = 'job-item';
var opportunities = lastAndelaJobs.filter(function (j) {
return j.kind === 'opportunity';
});
var applied = sortJobsById(
lastAndelaJobs.filter(function (j) {
return j.kind !== 'opportunity';
}),
);
var startInfo = job.posted_date || '';
var postedAgo = job.posted_ago || '';
var companyName =
typeof job.company === 'object'
? (job.company && job.company.name) || 'Company'
: job.company || 'Company';
if (opportunities.length) {
var oppHeading = document.createElement('div');
oppHeading.className = 'section-heading section-heading-opportunities';
oppHeading.textContent = 'Open Opportunities';
jobList.appendChild(oppHeading);
jobElement.innerHTML =
'<div class="job-title">' +
job.title +
'</div>' +
'<div class="job-company">' +
companyName +
'</div>' +
'<div class="job-location">' +
(job.location || 'Remote') +
'</div>' +
'<div class="job-posted">' +
startInfo +
(postedAgo ? ' · ' + postedAgo : '') +
'</div>';
TITLE_ORDER.forEach(function (title) {
var group = sortWithNewFirst(
opportunities.filter(function (j) {
return (j.professionalTitle || 'Full-Stack Engineer') === title;
}),
newBadgeUntil,
now,
);
if (!group.length) return;
jobElement.addEventListener('click', function () {
var url = job.url || 'https://app.andela.com/job-postings/' + job.id;
chrome.tabs.create({ url: url });
var badgeInfo = PROFESSION_BADGES[title] || { color: '#C9971C' };
var titleHeading = document.createElement('h3');
titleHeading.className = 'profession-heading';
titleHeading.innerHTML =
'<span class="profession-dot" style="background-color:' +
badgeInfo.color +
'"></span>' +
title;
jobList.appendChild(titleHeading);
group.slice(0, 10).forEach(function (job) {
renderJob(jobList, job, newBadgeUntil, now);
});
});
}
jobList.appendChild(jobElement);
}
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, 10).forEach(renderJob);
}
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(renderJob);
}
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');
@@ -91,10 +165,7 @@ function updateUI(data) {
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('checkNow').addEventListener('click', function () {
console.log('Manual check triggered');
chrome.runtime.sendMessage({ action: 'checkNow' }, function (response) {
console.log('Check initiated');
});
chrome.runtime.sendMessage({ action: 'checkNow' }, function () {});
});
document
@@ -104,10 +175,13 @@ document.addEventListener('DOMContentLoaded', function () {
});
chrome.storage.local.get(
['lastAndelaJobs', 'lastAndelaUpdated', 'totalAndelaMatching'],
function (result) {
updateUI(result);
},
[
'lastAndelaJobs',
'lastAndelaUpdated',
'totalAndelaMatching',
'newBadgeUntil',
],
updateUI,
);
chrome.storage.onChanged.addListener(function (changes, areaName) {
@@ -115,14 +189,17 @@ document.addEventListener('DOMContentLoaded', function () {
areaName === 'local' &&
(changes.lastAndelaJobs ||
changes.lastAndelaUpdated ||
changes.totalAndelaMatching)
changes.totalAndelaMatching ||
changes.newBadgeUntil)
) {
console.log('Storage updated, refreshing UI...');
chrome.storage.local.get(
['lastAndelaJobs', 'lastAndelaUpdated', 'totalAndelaMatching'],
function (result) {
updateUI(result);
},
[
'lastAndelaJobs',
'lastAndelaUpdated',
'totalAndelaMatching',
'newBadgeUntil',
],
updateUI,
);
}
});