const fs = require('fs'); const path = require('path'); // Load logo from disk once at startup and convert to base64 data URI // In Docker: /app/client/dist/static/mpm-logo.png // In dev: ./client/public/static/mpm-logo.png (or dist after build) let LOGO_DATA_URI = ''; const logoPaths = [ path.join(__dirname, '..', 'client', 'dist', 'static', 'mpm-logo.png'), path.join(__dirname, '..', 'client', 'public', 'static', 'mpm-logo.png'), ]; for (const p of logoPaths) { try { const buf = fs.readFileSync(p); LOGO_DATA_URI = `data:image/png;base64,${buf.toString('base64')}`; console.log('[PDF] Logo loaded from', p); break; } catch (_) { /* try next path */ } } if (!LOGO_DATA_URI) console.warn('[PDF] Logo not found — PDF header will have no logo'); const TIERS = [ { min: 0, max: 4, label: 'Tier 0\u20131 \u2014 Elite Standing', color: '#16a34a', bg: '#f0fdf4' }, { min: 5, max: 9, label: 'Tier 1 \u2014 Realignment', color: '#854d0e', bg: '#fefce8' }, { min: 10, max: 14, label: 'Tier 2 \u2014 Administrative Lockdown', color: '#b45309', bg: '#fff7ed' }, { min: 15, max: 19, label: 'Tier 3 \u2014 Verification', color: '#c2410c', bg: '#fff7ed' }, { min: 20, max: 24, label: 'Tier 4 \u2014 Risk Mitigation', color: '#b91c1c', bg: '#fef2f2' }, { min: 25, max: 29, label: 'Tier 5 \u2014 Final Decision', color: '#991b1b', bg: '#fef2f2' }, { min: 30, max: 999, label: 'Tier 6 \u2014 Separation', color: '#ffffff', bg: '#7f1d1d' }, ]; function getTier(pts) { return TIERS.find(t => pts >= t.min && pts <= t.max) || TIERS[0]; } function fmt(d) { if (!d) return '\u2014'; return new Date(d + 'T12:00:00').toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', timeZone: 'America/Chicago', }); } function fmtDT(d, t) { return t ? `${fmt(d)} at ${t}` : fmt(d); } function buildHtml(v, score) { const priorPts = score.active_points || 0; const priorTier = getTier(priorPts); const newTotal = priorPts + v.points; const newTier = getTier(newTotal); const escalated = priorTier.label !== newTier.label; const genAt = new Date().toLocaleString('en-US', { timeZone: 'America/Chicago', dateStyle: 'full', timeStyle: 'short', }); const docId = `CPAS-${v.id.toString().padStart(5, '0')}`; // Acknowledgment: if acknowledged_by is set, show filled data instead of blank sig line const hasAck = !!v.acknowledged_by; const ackName = v.acknowledged_by || ''; const ackDate = v.acknowledged_date ? fmt(v.acknowledged_date) : ''; const logoTag = LOGO_DATA_URI ? `` : ''; return `
${logoTag}
CPAS Violation Record
Comprehensive Professional Accountability System
${docId}
Generated ${genAt}
\u26D1 Confidential \u2014 Authorized HR & Management Use Only
Employee Information
Employee Name
${v.employee_name}
Department
${v.department || '\u2014'}
Supervisor
${v.supervisor || '\u2014'}
Witness / Documenting Officer
${v.witness_name || '\u2014'}
Violation Details
Violation
${v.violation_name}
Category
${v.category}
Incident Date / Time
${fmtDT(v.incident_date, v.incident_time)}
Submitted By
${v.submitted_by || 'System'}
${v.location ? `
Location / Context
${v.location}
` : ''}
${v.details ? `
Incident Notes
${v.details}
` : ''}
CPAS Point Assessment
${v.points}
Points Assessed This violation
${priorPts}
Prior Active Points
${priorTier.label}
+
${v.points}
This Violation
=
${newTotal}
New Active Total
${newTier.label}
${escalated ? `
\u26A0
Tier Escalation: This violation advances the employee from ${priorTier.label} to ${newTier.label}.
` : ''}
CPAS Tier Reference
${TIERS.map(t => { const active = newTotal >= t.min && newTotal <= t.max; const range = t.min === 30 ? '30+' : `${t.min}\u2013${t.max}`; return ``; }).join('')}
Points Tier & Standing
${active ? '\u25B6 ' : ''}${range} ${t.label} ${active ? ' \u2190 Current' : ''}
Employee Notice: CPAS points remain active for a rolling 90-day period from the date of each incident. Accumulation of points may result in tier escalation and associated consequences as outlined in the Employee Handbook. The employee may submit a written response within 5 business days of receiving this document.
Acknowledgement & Signatures${hasAck ? 'Acknowledged' : ''}

By signing below, the employee acknowledges receipt of this violation record. Acknowledgement does not imply agreement with the violation as documented.

${hasAck ? `
${ackName}
` : '
'}
Employee Signature
${hasAck && ackDate ? `
${ackDate}
` : ''}
Date
Supervisor / Documenting Officer Signature
Date
`; } module.exports = buildHtml;