/** * Builds the full HTML string for a CPAS violation PDF document. * Matches the styling of the original HTML violation form. */ const TIERS = [ { min: 0, max: 4, label: 'Tier 0-1 — Elite Standing', color: '#28a745' }, { min: 5, max: 9, label: 'Tier 1 — Realignment', color: '#856404' }, { min: 10, max: 14, label: 'Tier 2 — Administrative Lockdown', color: '#d9534f' }, { min: 15, max: 19, label: 'Tier 3 — Verification', color: '#d9534f' }, { min: 20, max: 24, label: 'Tier 4 — Risk Mitigation', color: '#c0392b' }, { min: 25, max: 29, label: 'Tier 5 — Final Decision', color: '#c0392b' }, { min: 30, max: 999,label: 'Tier 6 — Separation', color: '#721c24' }, ]; function getTier(points) { return TIERS.find(t => points >= t.min && points <= t.max) || TIERS[0]; } function formatDate(d) { if (!d) return '—'; const dt = new Date(d + 'T12:00:00'); return dt.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', timeZone: 'America/Chicago' }); } function formatDateTime(d, t) { const date = formatDate(d); return t ? `${date} at ${t}` : date; } function row(label, value) { return ` ${label} ${value || '—'} `; } function buildHtml(v, score) { const activePts = score.active_points || 0; const tier = getTier(activePts); const newTotal = activePts + v.points; const newTier = getTier(newTotal); const tierChange = tier.label !== newTier.label; const generatedAt = new Date().toLocaleString('en-US', { timeZone: 'America/Chicago', dateStyle: 'full', timeStyle: 'short' }); // Map violation_type to handbook chapter reference (loaded from violations.js in frontend) // Since we're in backend, we'll reconstruct key descriptions from the violation_name // The database already stores violation_name (e.g., "Tardy Core Hours") and category // Build a contextual description block const contextBlock = v.details ? `
Context: ${v.details}
` : ''; return `
Document ID: CPAS-${v.id.toString().padStart(5,'0')}
Generated: ${generatedAt}

CPAS Individual Violation Record

Message Point Media — Confidential HR Document

⚠ CONFIDENTIAL — For authorized HR and management use only
Employee Information
${row('Employee Name', `${v.employee_name}`)} ${row('Department', v.department)} ${row('Supervisor', v.supervisor)} ${row('Witness / Documenting Officer', v.witness_name)}
Violation Details
${row('Violation Type', `${v.violation_name}`)} ${row('Category', v.category)} ${row('Policy Reference', 'Chapter 4, Section 5 — Comprehensive Professional Accountability System (CPAS)')} ${row('Incident Date / Time', formatDateTime(v.incident_date, v.incident_time))} ${v.location ? row('Location / Context', v.location) : ''} ${row('Submitted By', v.submitted_by || 'System')}
${v.details ? `
Incident Details:
${v.details}
` : ''}
CPAS Point Assessment
${v.points}
Points Assessed — This Violation
${activePts}
Active Points (Prior)
${tier.label}
+
${v.points}
Points — This Violation
=
${newTotal}
New Active Total
${newTier.label}
${tierChange ? `
⚠ Tier Escalation: This violation advances the employee from ${tier.label} to ${newTier.label}. Review associated tier consequences per the Employee Handbook.
` : ''}
CPAS Tier Reference
${TIERS.map(t => ` `).join('')}
Points Tier
${t.min === 30 ? '30+' : t.min + '–' + t.max} ${t.label}
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, Chapter 4, Section 5. This document should be reviewed with the employee and signed by all parties.
Acknowledgement & Signatures

By signing below, the employee acknowledges receipt of this violation record. Acknowledgement does not imply agreement. The employee may submit a written response within 5 business days.

Employee Signature
Date
Supervisor / Documenting Officer Signature
Date
`; } module.exports = buildHtml;