/* ============================================================ offers.js — Offer management ============================================================ */ window.Views = window.Views || {}; window.Offers = {}; Views.offers = function () { const filters = { q: '', status: '' }; let table; const stats = { sent: DB.offers.filter(o => o.status !== 'Draft').length, accepted: DB.offers.filter(o => o.status === 'Accepted').length, pending: DB.offers.filter(o => ['Sent', 'Negotiating'].includes(o.status)).length, rate: Math.round(DB.offers.filter(o => o.status === 'Accepted').length / (DB.offers.filter(o => ['Accepted', 'Declined'].includes(o.status)).length || 1) * 100) }; function apply() { const rows = DB.offers.filter(o => { if (filters.status && o.status !== filters.status) return false; if (filters.q && !(o.candidate + o.jobTitle + o.recruiter).toLowerCase().includes(filters.q.toLowerCase())) return false; return true; }); table.update(rows); } table = UI.dataTable({ pageSize: 8, rows: DB.offers, columns: [ { key: 'candidate', label: 'Candidate', sortable: true, render: o => `
${UI.avatar(o.candidate, o.initials, o.color)}
${o.candidate}
${o.jobTitle}
` }, { key: 'department', label: 'Department', sortable: true }, { key: 'base', label: 'Base Salary', sortable: true, align: 'right', render: o => `${DB.money(o.base)}` }, { key: 'equity', label: 'Equity', render: o => `${o.equity}` }, { key: 'bonus', label: 'Bonus', align: 'center', render: o => `${o.bonus}` }, { key: 'sent', label: 'Sent', sortable: true, sortValue: o => o.sent.getTime(), render: o => `${DB.fmtShort(o.sent)}` }, { key: 'status', label: 'Status', sortable: true, render: o => UI.badge(o.status) }, { key: '_a', label: 'Actions', align: 'right', render: o => `
` } ] }); const statusOpts = [''].concat(['Sent', 'Accepted', 'Negotiating', 'Declined', 'Draft', 'Expired'].map(s => ``)).join(''); const statCard = (label, val, icn, cls) => `
${label}${UI.icon(icn)}
${val}
`; const html = `

Offers

Track offer letters and acceptance

${statCard('Offers Sent', stats.sent, 'send', 'i-indigo')} ${statCard('Accepted', stats.accepted, 'check-circle', 'i-green')} ${statCard('Awaiting Response', stats.pending, 'clock', 'i-amber')} ${statCard('Acceptance Rate', stats.rate + '%', 'trending-up', 'i-teal')}
${table.html}
`; return { html, onMount() { table.mount(); const s = document.getElementById('ofSearch'); s.oninput = () => { filters.q = s.value; apply(); }; document.getElementById('ofStatus').onchange = e => { filters.status = e.target.value; apply(); }; } }; }; Offers.view = function (id) { const o = DB.offers.find(x => x.id === id); const total = o.base + Math.round(o.base * parseInt(o.bonus) / 100); const body = `
${UI.avatar(o.candidate, o.initials, o.color, 'avatar-lg')}
${o.candidate}
${o.jobTitle} · ${o.department}
${UI.badge(o.status)}
Compensation Package
Base Salary
${DB.money(o.base)}
Annual Bonus
${o.bonus}
Equity
${o.equity}
Est. Total Cash
${DB.money(total)}
Sent On
${DB.fmtDate(o.sent)}
Expires
${DB.fmtDate(o.expires)}
Recruiter
${o.recruiter}
Offer ID
${o.id}
`; const footer = ` `; UI.modal({ title: 'Offer Details', subtitle: o.id, body, footer, size: 'modal-lg' }); }; Offers.create = function () { const opt = arr => arr.map(o => ``).join(''); const body = `
Required
`; const footer = ` `; UI.modal({ title: 'Create Offer', subtitle: 'Generate and send an offer letter', body, footer }); }; Offers._save = function () { const form = document.getElementById('offerForm'); UI.clearErrors(form); const f = Object.fromEntries(new FormData(form)); if (!f.base || +f.base <= 0) { UI.fieldError(form.querySelector('[name=base]'), 'Required'); UI.toast('Enter a base salary', 'error'); return; } const cand = DB.candidates.find(c => c.name === f.candidate) || DB.candidates[0]; DB.offers.unshift({ id: 'OFR-' + (9001 + DB.offers.length), candidate: cand.name, candidateId: cand.id, initials: cand.initials, color: cand.color, jobTitle: cand.jobTitle, department: cand.department, status: 'Sent', base: +f.base, equity: f.equity || '10k RSU', bonus: (f.bonus || 10) + '%', sent: new Date('2026-07-09'), expires: f.expires ? new Date(f.expires) : new Date('2026-07-23'), recruiter: cand.recruiter }); UI.closeModal(); UI.toast('Offer sent successfully', 'success'); Router.reload(); };