/* ============================================================ jobs.js — Jobs listing, filters, create/edit/view/delete ============================================================ */ window.Views = window.Views || {}; window.Jobs = {}; Views.jobs = function () { const filters = { q: '', dept: '', status: '', type: '' }; let table; function apply() { let rows = DB.jobs.filter(j => { if (filters.dept && j.department !== filters.dept) return false; if (filters.status && j.status !== filters.status) return false; if (filters.type && j.type !== filters.type) return false; if (filters.q) { const q = filters.q.toLowerCase(); if (!(j.title + j.id + j.department + j.manager + j.recruiter + j.location).toLowerCase().includes(q)) return false; } return true; }); table.update(rows); } table = UI.dataTable({ pageSize: 8, rows: DB.jobs, columns: [ { key: 'id', label: 'Job ID', sortable: true, render: j => `${j.id}` }, { key: 'title', label: 'Job Title', sortable: true, render: j => `
${j.title}
${j.businessUnit} · ${j.grade}
` }, { key: 'department', label: 'Department', sortable: true }, { key: 'manager', label: 'Hiring Manager', sortable: true, render: j => `
${UI.avatar(j.manager)}${j.manager}
` }, { key: 'location', label: 'Location', sortable: true, render: j => `${j.location}` }, { key: 'type', label: 'Type', render: j => UI.badge(j.type, 'b-gray') }, { key: 'applications', label: 'Apps', sortable: true, align: 'center', render: j => `${j.applications}` }, { key: 'status', label: 'Status', sortable: true, render: j => UI.badge(j.status) }, { key: 'created', label: 'Created', sortable: true, sortValue: j => j.created.getTime(), render: j => `${DB.fmtShort(j.created)}` }, { key: '_a', label: 'Actions', align: 'right', render: j => `
` } ] }); const deptOpts = [''].concat(DB.departments.map(d => ``)).join(''); const statusOpts = [''].concat(DB.jobStatuses.map(s => ``)).join(''); const typeOpts = [''].concat(DB.empTypes.map(t => ``)).join(''); const html = `

Jobs

${DB.jobs.length} requisitions · ${DB.kpis.openJobs} currently open

${table.html}
`; return { html, onMount() { table.mount(); const s = document.getElementById('jobSearch'); s.oninput = () => { filters.q = s.value; apply(); }; document.getElementById('jobDept').onchange = e => { filters.dept = e.target.value; apply(); }; document.getElementById('jobStatus').onchange = e => { filters.status = e.target.value; apply(); }; document.getElementById('jobType').onchange = e => { filters.type = e.target.value; apply(); }; } }; }; // ---------------- View job ---------------- Jobs.view = function (id) { const j = DB.getJob(id); const body = `
${UI.icon('briefcase')}
${j.title}
${j.id} · ${j.department} · ${j.businessUnit}
${UI.badge(j.status)}
Hiring Manager
${j.manager}
Assigned Recruiter
${j.recruiter} ${(() => { const r = DB.getRecruiterByName(j.recruiter); return r ? `${r.workload}% load` : ''; })()}
Location
${j.location}
Employment Type
${j.type}
Grade
${j.grade}
Vacancies
${j.vacancies}
Salary Range
${DB.moneyK(j.salaryMin)} – ${DB.moneyK(j.salaryMax)}
Experience
${j.experience}
Education
${j.education}
Deadline
${DB.fmtDate(j.deadline)}
Description

${j.description}

Key Responsibilities
Required Skills
${j.skills.map(s => `${s}`).join('')}
Benefits
${j.benefits.map(s => `${s}`).join('')}
Hiring progress
${UI.pbar(j.progress)}
${j.progress}%
`; const footer = ` `; UI.modal({ title: 'Job Details', subtitle: j.id, body, footer, size: 'modal-lg' }); }; Jobs.reassign = function (id) { const j = DB.getJob(id); const opts = DB.recruiters.map(r => ``).join(''); UI.modal({ title: 'Reassign Recruiter', subtitle: j.title, body: `

Workload is recalculated automatically across the recruiter's assigned requisitions.

`, footer: `` }); }; Jobs._doReassign = function (id) { const j = DB.getJob(id); j.recruiter = document.getElementById('reassignRec').value; UI.closeModal(); UI.toast('Recruiter reassigned', 'success'); if (typeof Router !== 'undefined') Router.reload(); }; // ---------------- Create / Edit form ---------------- Jobs.openCreate = function () { Jobs._form(null); }; Jobs.openEdit = function (id) { Jobs._form(DB.getJob(id)); }; Jobs._form = function (job) { const isEdit = !!job; const opt = (arr, sel) => arr.map(o => ``).join(''); const body = `
Job title is required
Enter a valid amount
Description is required
`; const footer = ` `; UI.modal({ title: isEdit ? 'Edit Job' : 'Create New Job', subtitle: isEdit ? job.id : 'Fill in the details to post a requisition', body, footer, size: 'modal-lg' }); }; Jobs._save = function (id) { const form = document.getElementById('jobForm'); UI.clearErrors(form); const f = Object.fromEntries(new FormData(form)); let ok = true; const req = (name, cond) => { if (!cond) { UI.fieldError(form.querySelector(`[name="${name}"]`), 'Required'); ok = false; } }; req('title', f.title.trim()); req('description', f.description.trim()); req('salaryMin', f.salaryMin && +f.salaryMin > 0); if (!ok) { UI.toast('Please fix the highlighted fields', 'error'); return; } const skills = f.skills.split(',').map(s => s.trim()).filter(Boolean); const benefits = f.benefits.split(',').map(s => s.trim()).filter(Boolean); const responsibilities = f.responsibilities.split('\n').map(s => s.trim()).filter(Boolean); if (id) { const job = DB.getJob(id); Object.assign(job, { title: f.title, department: f.department, businessUnit: f.businessUnit, grade: f.grade, type: f.type, manager: f.manager, recruiter: f.recruiter, salaryMin: +f.salaryMin, salaryMax: +f.salaryMax || +f.salaryMin + 20000, experience: f.experience, education: f.education, location: f.location, vacancies: +f.vacancies || 1, description: f.description, responsibilities, skills: skills.length ? skills : job.skills, benefits: benefits.length ? benefits : job.benefits, status: f.status }); UI.toast('Job updated successfully', 'success'); } else { const newJob = { id: 'JOB-' + (1001 + DB.jobs.length), title: f.title, department: f.department, businessUnit: f.businessUnit, grade: f.grade, manager: f.manager, managerId: '', recruiter: f.recruiter, recruiterId: '', location: f.location, type: f.type, vacancies: +f.vacancies || 1, applications: 0, status: f.status, created: new Date('2026-07-09'), deadline: f.deadline ? new Date(f.deadline) : new Date('2026-08-09'), salaryMin: +f.salaryMin, salaryMax: +f.salaryMax || +f.salaryMin + 20000, experience: f.experience || '3+ years', education: f.education, skills, benefits, description: f.description, responsibilities: responsibilities.length ? responsibilities : ['Own key projects'], progress: 0 }; DB.jobs.unshift(newJob); UI.toast('Job created successfully', 'success'); App.updateBadges(); } UI.closeModal(); Router.reload(); }; Jobs.confirmDelete = function (id) { const j = DB.getJob(id); const body = `
${UI.icon('trash')}

Delete "${j.title}"?

This will permanently remove requisition ${j.id} and its ${j.applications} applications. This action cannot be undone.

`; const footer = ` `; UI.modal({ title: 'Confirm Deletion', body, footer }); }; Jobs._delete = function (id) { const i = DB.jobs.findIndex(j => j.id === id); if (i > -1) DB.jobs.splice(i, 1); UI.closeModal(); UI.toast('Job deleted', 'success'); App.updateBadges(); Router.reload(); };