8.8 KiB
type, status, tags, repo, started, updated, owner
| type | status | tags | repo | started | updated | owner | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| project | active |
|
https://git.alwisp.com/jason/cpas | 2026-03-06 | 2026-05-14 | Jason Stedwell |
CPAS Violation Tracker
Single-container Dockerized web app for CPAS (workforce standing / violation documentation) used internally at Message Point Media. Replaces ad-hoc paper / spreadsheet violation tracking with a structured, auditable system that produces signed PDF records and surfaces tier-escalation risk live.
Repo: git.alwisp.com/jason/cpas · First commit: 2026-03-06 · Deployed on Unraid (
10.2.0.14:3001)
Stack
- Frontend: React + Vite (single-page app, dark theme, mobile-responsive at 375px+)
- Backend: Node.js + Express
- DB: SQLite via
better-sqlite3, WAL mode, auto-migrations on boot - PDF: Puppeteer + bundled Chromium (system install inside container)
- Packaging: One multi-stage Dockerfile — build React, install backend, bundle Chromium, ship as a single image
- Runtime requirement on dev box: Docker Desktop only. No host Node/npm needed.
What it does
It manages employee violations against the CPAS rubric on a rolling 90-day window:
- Company Dashboard — every employee sorted by active CPAS points (highest risk first), with summary stat cards, tier badges, an "at-risk" flag (within 2 pts of next tier), and search + department filter.
- Violation Form — pick an employee, pick a violation type, see prior 90-day count inline; recidivist auto-escalation; pre-submit tier-crossing warning; context-aware fields; one-click PDF download on submit; optional employee acknowledgment block.
- Employee Profile Modal — full violation history, amendment count, edit employee, merge duplicate, negate/restore, hard delete, per-violation PDF, free-text notes/flags ("on PIP", "union member"), and a per-violation 90-day point expiration timeline with projected tier drops.
- Violation Amendment — point value / type / incident date are immutable; non-scoring fields (location, witness, narrative, acknowledgment) are amendable with a field-level diff trail.
- Audit Log — append-only record of every write action (employee CRUD, violation logged/amended/negated/restored/deleted); filterable, paginated panel from the dashboard.
- Toast notification system — global success/error/warning/info, auto-dismiss with progress bar.
- PDF generation — Puppeteer, snapshot of prior active points at incident time, optional employee acknowledgment block on the signature page.
- Stakeholder demo —
/demostatic route with synthetic data, served before the SPA catch-all; no auth required.
CPAS Tier System
| Points | Tier | Label |
|---|---|---|
| 0–4 | 0–1 | Elite Standing |
| 5–9 | 1 | Realignment |
| 10–14 | 2 | Administrative Lockdown |
| 15–19 | 3 | Verification |
| 20–24 | 4 | Risk Mitigation |
| 25–29 | 5 | Final Decision |
| 30+ | 6 | Separation |
Scores are summed over a rolling 90-day window; negated violations excluded.
Database
Six tables + one view:
employees— id, name, department, supervisor, notesviolations— full incident record, includingprior_active_pointssnapshotviolation_resolutions— soft-delete reason / detailsviolation_amendments— field-level diff log (one row per changed field)audit_log— append-only system action logactive_cpas_scores(view) — 90-day point sum per employee
Auto-migrations in db/database.js add new columns to existing DBs on startup — meaningful here because Jason runs this in production on Unraid, so the schema evolves without losing data.
Deployment notes worth remembering
- Unraid: static IP on
br0bridge (10.2.0.14), DB persisted at/mnt/user/appdata/cpas/db/cpas.db, WebUI on port 3001. --pids-limit 2048is critical — Puppeteer/Chromium spawns many processes for each PDF; Unraid's default cap silently kills PDF generation.- Volume:
/mnt/user/appdata/cpas/db→/data. Database survives rebuilds, image reloads, and container removal. - Updates are a 3-step loop:
docker buildlocally →docker save | gzip→ SMB drop into appdata →docker load+ restart container in Unraid GUI.
Mobile
Responsive design targets 375px+ (iPhone SE and up). At ≤768px the dashboard table swaps to a card-based layout (DashboardMobile.jsx); the nav stacks; tap targets are ≥44px; form inputs use 16px font to prevent iOS focus zoom. No external CSS library — single mobile.css utility sheet + a useMediaQuery hook. Implementation details and testing checklist live in MOBILE_RESPONSIVE.md.
Project structure
cpas/
├── Dockerfile # Multi-stage: React build + Express + Chromium
├── server.js # API + static SPA + /demo route
├── db/
│ ├── schema.sql # Tables + 90-day active score view
│ └── database.js # SQLite + auto-migrations
├── pdf/
│ ├── generator.js # Puppeteer
│ └── template.js # HTML PDF template
├── demo/ # /demo synthetic-data SPA
└── client/ # React + Vite frontend
└── src/
├── App.jsx # Root + footer (copyright, dev ticker, Gitea link)
├── data/
│ ├── violations.js # All CPAS violation definitions
│ └── departments.js
├── hooks/useEmployeeIntelligence.js
└── components/ # Dashboard, ViolationForm, EmployeeModal,
# AmendViolationModal, AuditLog, ToastProvider, etc.
API surface (selected)
GET /api/health— health + build versionGET /api/dashboard— all employees with active points + violation countsGET /api/employees/:id/expiration— roll-off timeline with days remainingPOST /api/violations— log violation (acceptsacknowledged_by,acknowledged_date)PATCH /api/violations/:id/amend— non-scoring field amendment + diff logPATCH /api/violations/:id/negated//restore— soft delete + restoreGET /api/violations/:id/pdf— PDF downloadGET /api/audit— paginated audit log
Status
Phase 8 of the public roadmap is complete (stakeholder demo + app footer with live dev ticker). Core HR documentation workflow is shipped: dashboard, violation entry, employee profile, amendments, audit log, expiration timeline, acknowledgment field, toast system, mobile layout.
Notable open ideas (from roadmap)
Quick wins (low effort): column sort on dashboard, department multi-select filter, N keyboard shortcut for new violation, configurable at-risk threshold via env var, version.json injected at build time.
Reporting: violation trend chart (daily/weekly/monthly), department heat map, per-employee sparklines in profile modal.
Workflow: draft/pending violations before finalize, violation templates.
Notifications: tier-escalation alerts (email or in-app) on crossing into Tier 2+.
Infra (higher effort): multi-user auth with roles (currently runs on trusted internal LAN with none), scheduled DB backup, dark/light theme toggle.
Review take
The app is well-scoped and visibly production-shaped, not a hobby project. Strong signals: append-only audit log, field-level amendment diffs, immutable scoring fields, prior_active_points snapshot baked into each violation row so historical PDFs stay accurate, and auto-migrations so the schema can evolve in place on a live Unraid deployment. The Unraid-specific install guide explicitly calls out --pids-limit 2048 for Chromium, which is the kind of footgun that only gets documented after it's been hit in production — that detail alone tells you this is being actually used.
The single biggest gap relative to its current capability is auth. Everything else on the roadmap is incremental polish; multi-user auth with roles is the one change that meaningfully expands who can use the system safely. Worth pairing it with the proposed scheduled DB backup, since the moment more than one supervisor is writing, accidental damage gets more likely.
A secondary observation: the audit log + amendment diff infrastructure is already strong enough to support a "who changed what, when" view per employee — that's basically free reporting if surfaced in the profile modal as a timeline.
Links
- Repo — git.alwisp.com/jason/cpas
- Local install guide —
README.md(in repo) - Unraid install guide —
README_UNRAID_INSTALL.md - Mobile implementation notes —
MOBILE_RESPONSIVE.md