jason ecd3810050 feat: add toast notifications to EmployeeModal for all actions
- Toast success/error on PDF download, negate, restore, hard delete
- Toast success on employee edit and violation amendment via modal callbacks
- Error details from API responses included in error toasts
2026-03-07 21:40:36 -06:00
2026-03-06 11:33:32 -06:00
2026-03-06 14:11:19 -06:00
2026-03-06 12:19:55 -06:00

CPAS Violation Tracker

Single-container Dockerized web app for CPAS violation documentation and workforce standing management. Built with React + Vite (frontend), Node.js + Express (backend), SQLite (database), and Puppeteer (PDF generation).


The only requirement on your machine: Docker Desktop

Everything else — Node.js, npm, React build, Chromium for PDF — happens inside Docker.


Quickstart (Local)

# 1. Build the image (installs all deps + compiles React inside Docker)
docker build -t cpas .

# 2. Run it
docker run -d --name cpas \
  -p 3001:3001 \
  -v cpas-data:/data \
  cpas

# 3. Open
# http://localhost:3001

Update After Code Changes

docker build -t cpas .
docker stop cpas && docker rm cpas
docker run -d --name cpas -p 3001:3001 -v cpas-data:/data cpas

Deploying on Unraid

Step 1 — Build and export the image on your dev machine

docker build -t cpas:latest .
docker save cpas:latest | gzip > cpas-latest.tar.gz

Step 2 — Load the image on Unraid

Transfer cpas-latest.tar.gz to your Unraid server, then load it via the Unraid terminal:

docker load < /path/to/cpas-latest.tar.gz

Confirm the image is present:

docker images | grep cpas

Step 3 — Create the appdata directory

mkdir -p /mnt/user/appdata/cpas/db

Step 4 — Run the container

This is the verified working docker run command for Unraid (bridge networking with static IP):

docker run \
  -d \
  --name='cpas' \
  --net='br0' \
  --ip='10.2.0.14' \
  --pids-limit 2048 \
  -e TZ="America/Chicago" \
  -e HOST_OS="Unraid" \
  -e HOST_HOSTNAME="ALPHA" \
  -e HOST_CONTAINERNAME="cpas" \
  -e 'PORT'='3001' \
  -e 'DB_PATH'='/data/cpas.db' \
  -l net.unraid.docker.managed=dockerman \
  -l net.unraid.docker.webui='http://[IP]:[PORT:3001]' \
  -v '/mnt/user/appdata/cpas/db':'/data':'rw' \
  cpas:latest

Access the app at http://10.2.0.14:3001 (or whatever static IP you assigned).

Key settings explained

Setting Value Notes
--net br0 Unraid custom bridge network — gives the container its own LAN IP
--ip 10.2.0.14 Static IP on your LAN — adjust to match your subnet
--pids-limit 2048 Required — Puppeteer/Chromium spawns many processes for PDF generation; default Unraid limit is too low and will cause PDF failures
PORT 3001 Express listen port inside the container
DB_PATH /data/cpas.db SQLite database path inside the container
Volume /mnt/user/appdata/cpas/db/data Persists the database across container restarts and rebuilds

Updating on Unraid

  1. Build and export the new image on your dev machine (Step 1 above)
  2. Load it on Unraid: docker load < cpas-latest.tar.gz
  3. Stop and remove the old container: docker stop cpas && docker rm cpas
  4. Re-run the docker run command from Step 4 — the volume mount preserves all data

Note: The --pids-limit 2048 flag is critical. Without it, Chromium hits Unraid's default PID limit and PDF generation silently fails or crashes the container.


Features

Company Dashboard

  • Live table of all employees sorted by active CPAS points (highest risk first)
  • Summary stat cards: total employees, elite standing (0 pts), with active points, at-risk count, highest active score
  • At-risk badge: flags employees within 2 points of the next tier escalation
  • Search/filter by name, department, or supervisor
  • Click any employee name to open their full profile modal
  • 🔍 Audit Log button — filterable, paginated view of all system write actions

Violation Form

  • Select existing employee or enter new employee by name
  • Employee intelligence: shows current CPAS standing badge and 90-day violation count before submitting
  • Violation type dropdown grouped by category; shows prior 90-day counts inline
  • Recidivist auto-escalation: if an employee has prior violations of the same type, points slider auto-sets to maximum per policy
  • Repeat offense badge with prior count displayed
  • Context-sensitive fields (time, minutes late, amount, location, description) shown only when relevant to violation type
  • Tier crossing warning (TierWarning component): previews what tier the new points would push the employee into before submission
  • Point slider for discretionary adjustments within the violation's min/max range
  • One-click PDF download immediately after submission

Employee Profile Modal

  • Full violation history with resolution status and amendment count badge per record
  • ✎ Edit Employee button — update name, department, supervisor, or notes inline
  • Merge Duplicate tab — reassign all violations from a duplicate record and delete it
  • Amend button per active violation — edit non-scoring fields (location, notes, witness, etc.) with a full field-level diff history
  • Negate / restore individual violations (soft delete with resolution type + notes)
  • Hard delete option for data entry errors
  • PDF download for any historical violation record
  • Notes & Flags — free-text notes (e.g. "on PIP", "union member") with quick-add tag buttons; visible in the profile modal without affecting scoring
  • Point Expiration Timeline — shows when each active violation rolls off the 90-day window, with a progress bar, days-remaining countdown, and projected tier-drop indicators

Audit Log

  • Append-only log of every write action: employee created/edited/merged, violation logged/amended/negated/restored/deleted
  • Filterable by entity type (employee / violation) and action
  • Paginated with load-more; accessible from the Dashboard toolbar

Violation Amendment

  • Edit submitted violations' non-scoring fields without delete-and-resubmit
  • Point values, violation type, and incident date are immutable
  • Every change is stored as a field-level diff (old → new value) with timestamp and actor

In-App Documentation

  • ? Docs button in the navbar opens a slide-in admin reference panel
  • Covers feature map, CPAS tier system, workflow guidance, and roadmap
  • No external link required; always reflects current deployed version

CPAS Tier System

Points Tier Label
04 01 Elite Standing
59 1 Realignment
1014 2 Administrative Lockdown
1519 3 Verification
2024 4 Risk Mitigation
2529 5 Final Decision
30+ 6 Separation

Scores are computed over a rolling 90-day window (negated violations excluded).

PDF Generation

  • Puppeteer + system Chromium (bundled in Docker image)
  • Generated on-demand per violation via GET /api/violations/:id/pdf
  • Filename: CPAS_<EmployeeName>_<IncidentDate>.pdf
  • PDF captures prior active points at the time of the incident (snapshot stored on insert)

API Reference

Method Endpoint Description
GET /api/health Health check
GET /api/employees List all employees (includes notes)
POST /api/employees Create or upsert employee
PATCH /api/employees/:id Edit name, department, supervisor, or notes
POST /api/employees/:id/merge Merge duplicate employee; reassigns all violations
GET /api/employees/:id/score Get active CPAS score for employee
GET /api/employees/:id/expiration Active violation roll-off timeline with days remaining
PATCH /api/employees/:id/notes Save employee notes only (shorthand)
GET /api/dashboard All employees with active points + violation counts
POST /api/violations Log a new violation
GET /api/violations/employee/:id Violation history with resolutions + amendment counts
PATCH /api/violations/:id/negate Negate a violation (soft delete + resolution record)
PATCH /api/violations/:id/restore Restore a negated violation
PATCH /api/violations/:id/amend Amend non-scoring fields with field-level diff logging
GET /api/violations/:id/amendments Get amendment history for a violation
DELETE /api/violations/:id Hard delete a violation
GET /api/violations/:id/pdf Download violation PDF
GET /api/audit Paginated audit log (filterable by entity_type, entity_id)

Project Structure

cpas/
├── Dockerfile                                 # Multi-stage: builds React + runs Express w/ Chromium
├── .dockerignore
├── package.json                               # Backend (Express) deps
├── server.js                                  # API + static file server
├── db/
│   ├── schema.sql                             # Tables + 90-day active score view
│   └── database.js                            # SQLite connection (better-sqlite3) + auto-migrations
├── pdf/
│   └── generator.js                           # Puppeteer PDF generation
└── client/                                    # React frontend (Vite)
    ├── package.json
    ├── vite.config.js
    ├── index.html
    └── src/
        ├── main.jsx
        ├── App.jsx
        ├── data/
        │   └── violations.js                  # All CPAS violation definitions + groups
        ├── hooks/
        │   └── useEmployeeIntelligence.js     # Score + history hook
        └── components/
            ├── CpasBadge.jsx                  # Tier badge + color logic
            ├── TierWarning.jsx                # Pre-submit tier crossing alert
            ├── Dashboard.jsx                  # Company-wide leaderboard + audit log trigger
            ├── ViolationForm.jsx              # Violation entry form
            ├── EmployeeModal.jsx              # Employee profile + history modal
            ├── EditEmployeeModal.jsx          # Employee edit + merge duplicate
            ├── AmendViolationModal.jsx        # Non-scoring field amendment + diff history
            ├── AuditLog.jsx                   # Filterable audit log panel
            ├── NegateModal.jsx                # Negate/resolve violation dialog
            ├── ViolationHistory.jsx           # Violation list component
            ├── ExpirationTimeline.jsx         # Per-violation 90-day roll-off countdown
            ├── EmployeeNotes.jsx              # Inline notes editor with quick-add HR tags
            └── ReadmeModal.jsx                # In-app admin documentation panel

Database Schema

Six tables + one view:

  • employees — id, name, department, supervisor, notes
  • violations — full incident record including prior_active_points snapshot at time of logging
  • violation_resolutions — resolution type, details, resolved_by (linked to violations)
  • violation_amendments — field-level diff log for violation edits; one row per changed field per amendment
  • audit_log — append-only record of every write action (action, entity_type, entity_id, performed_by, details, timestamp)
  • active_cpas_scores (view) — sum of points for non-negated violations in rolling 90 days, grouped by employee

Amendable Fields

Point values, violation type, and incident date are immutable after submission. The following fields can be amended:

Field Notes
incident_time Time of day the incident occurred
location Where the incident took place
details Narrative description
submitted_by Supervisor who submitted
witness_name Witness on record

Roadmap

Completed

Phase Feature Description
1 Container scaffold Docker multi-stage build, Express server, SQLite schema
1 Base violation form Employee fields, violation type, incident date, point submission
2 Employee intelligence Live CPAS standing badge and 90-day count shown before submitting
2 Prior violation highlighting Violation dropdown annotates types with 90-day recurrence counts
2 Recidivist auto-escalation Points slider auto-maximizes on repeat same-type violations
2 Violation history Per-employee history list with resolution status
3 PDF generation Puppeteer/Chromium PDF per violation, downloadable immediately post-submit
3 Prior-points snapshot prior_active_points captured at insert time for accurate historical PDFs
4 Company dashboard Sortable employee table with live tier badges and at-risk flags
4 Stat cards Summary counts: total, clean, active, at-risk, highest score
4 Tier crossing warning Pre-submit alert when new points push employee to next tier
4 Employee profile modal Full history, negate/restore, hard delete, per-record PDF download
4 Negate & restore Soft-delete violations with resolution type + notes, fully reversible
5 Employee edit / merge Update employee name/dept/supervisor; merge duplicate records without losing history
5 Violation amendment Edit non-scoring fields with field-level audit trail
5 Audit log Append-only log of all system writes; filterable panel in the dashboard
6 Employee notes / flags Free-text notes on employee record with quick-add HR tags; does not affect scoring
6 Point expiration timeline Per-violation roll-off countdown with tier-drop projections
6 In-app documentation Admin usage guide and feature map accessible from the navbar

📋 Proposed

Reporting & Analytics

  • Violation trends chart — line/bar chart of violations per day/week/month, filterable by department or supervisor; useful for identifying systemic patterns vs. individual incidents
  • Department heat map — grid view showing violation density and average CPAS score by department; helps supervisors identify team-level risk
  • CSV / Excel export — bulk export of violations or dashboard data for external reporting or payroll integration

Employee Management

  • Supervisor view — scoped dashboard showing only the employees under a given supervisor, useful for multi-supervisor environments

Violation Workflow

  • Acknowledgment signature field — a "received by employee" name/date field on the violation form that prints on the PDF, replacing the blank signature line
  • Draft / pending violations — save a violation as draft before finalizing, useful when incidents need review before being officially logged
  • Bulk violation import — CSV import for migrating historical records from paper logs or a prior system

Notifications & Escalation

  • Tier escalation alerts — email or in-app notification when an employee crosses into Tier 2+ so the relevant supervisor is automatically informed
  • Scheduled summary digest — weekly email to supervisors listing their employees' current standings and any approaching tier thresholds
  • At-risk threshold configuration — make the "at-risk" warning threshold (currently hardcoded at 2 pts) configurable per deployment

Infrastructure & Ops

  • Multi-user auth — simple login with role-based access (admin, supervisor, read-only); currently the app has no auth and is assumed to run on a trusted internal network
  • Automated DB backup — cron job or Docker health hook to snapshot /data/cpas.db to a mounted backup volume or remote location on a schedule
  • Dark/light theme toggle — the UI is currently dark-only; a toggle would improve usability in bright environments

Proposed features are suggestions based on common HR documentation workflows. Priority and implementation order should be driven by actual operational needs.

Description
No description provided
Readme 653 KiB
Languages
JavaScript 98.1%
CSS 1%
Dockerfile 0.7%
HTML 0.2%