diff --git a/server.js b/server.js index 01216e2..59ed7d3 100755 --- a/server.js +++ b/server.js @@ -27,7 +27,7 @@ function audit(action, entityType, entityId, performedBy, details) { // Health app.get('/api/health', (req, res) => res.json({ status: 'ok', timestamp: new Date().toISOString() })); -// ── Employees ───────────────────────────────────────────────────────────────── +// ── Employees ──────────────────────────────────────────────────────────────── app.get('/api/employees', (req, res) => { const rows = db.prepare('SELECT id, name, department, supervisor, notes FROM employees ORDER BY name ASC').all(); res.json(rows); @@ -50,7 +50,7 @@ app.post('/api/employees', (req, res) => { res.status(201).json({ id: result.lastInsertRowid, name, department, supervisor }); }); -// ── Employee Edit ───────────────────────────────────────────────────────────── +// ── Employee Edit ──────────────────────────────────────────────────────────── // PATCH /api/employees/:id — update name, department, supervisor, or notes app.patch('/api/employees/:id', (req, res) => { const id = parseInt(req.params.id); @@ -81,7 +81,7 @@ app.patch('/api/employees/:id', (req, res) => { res.json({ id, name: newName, department: newDept, supervisor: newSupervisor, notes: newNotes }); }); -// ── Employee Merge ──────────────────────────────────────────────────────────── +// ── Employee Merge ─────────────────────────────────────────────────────────── // POST /api/employees/:id/merge — reassign all violations from sourceId → id, then delete source app.post('/api/employees/:id/merge', (req, res) => { const targetId = parseInt(req.params.id); @@ -134,7 +134,7 @@ app.get('/api/employees/:id/score', (req, res) => { res.json(row || { employee_id: req.params.id, active_points: 0, violation_count: 0 }); }); -// ── Expiration Timeline ─────────────────────────────────────────────────────── +// ── Expiration Timeline ────────────────────────────────────────────────────── // GET /api/employees/:id/expiration — active violations sorted by roll-off date // Returns each active violation with days_remaining until it exits the 90-day window. app.get('/api/employees/:id/expiration', (req, res) => { @@ -151,7 +151,7 @@ app.get('/api/employees/:id/expiration', (req, res) => { JULIANDAY(DATE(v.incident_date, '+90 days')) - JULIANDAY(DATE('now')) AS INTEGER - ) AS days_remaining + ) AS days_remaining FROM violations v WHERE v.employee_id = ? AND v.negated = 0 @@ -190,7 +190,7 @@ app.get('/api/violations/employee/:id', (req, res) => { res.json(rows); }); -// ── Violation amendment history ─────────────────────────────────────────────── +// ── Violation amendment history ────────────────────────────────────────────── app.get('/api/violations/:id/amendments', (req, res) => { const rows = db.prepare(` SELECT * FROM violation_amendments WHERE violation_id = ? ORDER BY created_at DESC @@ -216,7 +216,8 @@ app.post('/api/violations', (req, res) => { const { employee_id, violation_type, violation_name, category, points, incident_date, incident_time, location, - details, submitted_by, witness_name + details, submitted_by, witness_name, + acknowledged_by, acknowledged_date } = req.body; if (!employee_id || !violation_type || !points || !incident_date) { @@ -231,14 +232,16 @@ app.post('/api/violations', (req, res) => { employee_id, violation_type, violation_name, category, points, incident_date, incident_time, location, details, submitted_by, witness_name, - prior_active_points - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + prior_active_points, + acknowledged_by, acknowledged_date + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `).run( employee_id, violation_type, violation_name || violation_type, category || 'General', ptsInt, incident_date, incident_time || null, location || null, details || null, submitted_by || null, witness_name || null, - priorPts + priorPts, + acknowledged_by || null, acknowledged_date || null ); audit('violation_created', 'violation', result.lastInsertRowid, submitted_by, { @@ -248,9 +251,9 @@ app.post('/api/violations', (req, res) => { res.status(201).json({ id: result.lastInsertRowid }); }); -// ── Violation Amendment (edit) ──────────────────────────────────────────────── +// ── Violation Amendment (edit) ─────────────────────────────────────────────── // PATCH /api/violations/:id/amend — edit mutable fields; logs a diff per changed field -const AMENDABLE_FIELDS = ['incident_time', 'location', 'details', 'submitted_by', 'witness_name']; +const AMENDABLE_FIELDS = ['incident_time', 'location', 'details', 'submitted_by', 'witness_name', 'acknowledged_by', 'acknowledged_date']; app.patch('/api/violations/:id/amend', (req, res) => { const id = parseInt(req.params.id); @@ -295,7 +298,7 @@ app.patch('/api/violations/:id/amend', (req, res) => { res.json(updated); }); -// ── Negate a violation ──────────────────────────────────────────────────────── +// ── Negate a violation ─────────────────────────────────────────────────────── app.patch('/api/violations/:id/negate', (req, res) => { const { resolution_type, details, resolved_by } = req.body; const id = req.params.id; @@ -323,7 +326,7 @@ app.patch('/api/violations/:id/negate', (req, res) => { res.json({ success: true }); }); -// ── Restore a negated violation ─────────────────────────────────────────────── +// ── Restore a negated violation ────────────────────────────────────────────── app.patch('/api/violations/:id/restore', (req, res) => { const id = req.params.id; @@ -337,7 +340,7 @@ app.patch('/api/violations/:id/restore', (req, res) => { res.json({ success: true }); }); -// ── Hard delete a violation ─────────────────────────────────────────────────── +// ── Hard delete a violation ────────────────────────────────────────────────── app.delete('/api/violations/:id', (req, res) => { const id = req.params.id; @@ -353,7 +356,7 @@ app.delete('/api/violations/:id', (req, res) => { res.json({ success: true }); }); -// ── Audit log ───────────────────────────────────────────────────────────────── +// ── Audit log ──────────────────────────────────────────────────────────────── app.get('/api/audit', (req, res) => { const limit = Math.min(parseInt(req.query.limit) || 100, 500); const offset = parseInt(req.query.offset) || 0; @@ -372,7 +375,7 @@ app.get('/api/audit', (req, res) => { res.json(db.prepare(sql).all(...args)); }); -// ── PDF endpoint ────────────────────────────────────────────────────────────── +// ── PDF endpoint ───────────────────────────────────────────────────────────── app.get('/api/violations/:id/pdf', async (req, res) => { try { const violation = db.prepare(` @@ -399,7 +402,7 @@ app.get('/api/violations/:id/pdf', async (req, res) => { res.set({ 'Content-Type': 'application/pdf', 'Content-Disposition': `attachment; filename="CPAS_${safeName}_${violation.incident_date}.pdf"`, - 'Content-Length': pdfBuffer.length, + 'Content-Length': pdfBuffer.length, }); res.end(pdfBuffer); } catch (err) {