From 995e6070036ec773ed51de99e427dd5eb1fbd051 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 00:38:38 -0600 Subject: [PATCH 01/28] fix: remove default browser body margin causing white border --- client/index.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/index.html b/client/index.html index 2a311d7..a72bce6 100755 --- a/client/index.html +++ b/client/index.html @@ -4,6 +4,11 @@ CPAS Violation Tracker +
-- 2.52.0 From 87cf48e77e29bbd773dce5801164418132c9ed0c Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 00:42:48 -0600 Subject: [PATCH 02/28] Update README.md --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 891be75..db9a258 100755 --- a/README.md +++ b/README.md @@ -353,7 +353,6 @@ Effort ratings: 🟢 Low · 🟡 Medium · 🔴 High | Column sort on dashboard | 🟢 | Click `Tier`, `Active Points`, or `Department` headers to sort in-place; one `useState` + comparator, no API changes | | Department filter on dashboard | 🟢 | Multi-select dropdown to scope the employee table by department; `DEPARTMENTS` constant already exists | | Keyboard shortcut: New Violation | 🟢 | `N` key triggers tab switch to the violation form; ~5 lines of code | -| CSV export of dashboard | 🟢 | Client-side Blob download of the current filtered employee view; no backend changes needed | #### Reporting & Analytics @@ -362,7 +361,6 @@ Effort ratings: 🟢 Low · 🟡 Medium · 🔴 High | Violation trend chart | 🟡 | Line/bar chart of violations per day/week/month, filterable by department or supervisor; useful for identifying systemic patterns | | Department heat map | 🟡 | Grid view showing violation density and average CPAS score by department; helps supervisors identify team-level risk | | Violation sparklines per employee | 🟡 | Tiny inline bar chart of points over the last 6 months in the employee modal | -| CSV / Excel bulk export | 🟡 | Full export of violations or dashboard data for external reporting or payroll integration | #### Employee Management @@ -376,14 +374,12 @@ Effort ratings: 🟢 Low · 🟡 Medium · 🔴 High | Feature | Effort | Description | |---------|--------|-------------| | 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 | | Violation templates | 🟢 | Pre-fill the form with a saved violation type + common details for frequently logged incidents | #### Notifications & Escalation | Feature | Effort | Description | |---------|--------|-------------| -| Scheduled expiration digest | 🟡 | Weekly or daily email listing violations rolling off in the next 7 days; `nodemailer` + cron on the Node server | | Tier escalation alerts | 🟡 | Email or in-app notification when an employee crosses into Tier 2+ so the relevant supervisor is automatically informed | | At-risk threshold config | 🟢 | Make the "at-risk" warning threshold (currently hardcoded at 2 pts) configurable per deployment via an env var | | version.json / build badge | 🟢 | Inject git SHA + build timestamp into a static file during `docker build`; surfaced in the footer and `/api/health` | -- 2.52.0 From b02026f8a92f2e1e6ecd18f82e36cdaf920b4620 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 00:45:49 -0600 Subject: [PATCH 03/28] feat: inject git SHA + build timestamp into version.json during docker build --- Dockerfile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 18091ea..5aa27ad 100755 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,15 @@ RUN cd client && npm install COPY client/ ./client/ RUN cd client && npm run build +# ── Version metadata ────────────────────────────────────────────────────────── +# Pass these at build time: +# docker build --build-arg GIT_SHA=$(git rev-parse HEAD) \ +# --build-arg BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) . +ARG GIT_SHA=dev +ARG BUILD_TIME=unknown +RUN echo "{\"sha\":\"${GIT_SHA}\",\"shortSha\":\"${GIT_SHA:0:7}\",\"buildTime\":\"${BUILD_TIME}\"}" \ + > /build/client/dist/version.json + FROM node:20-alpine AS production RUN apk add --no-cache chromium nss freetype harfbuzz ca-certificates ttf-freefont ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true @@ -25,5 +34,6 @@ COPY demo/ ./demo/ COPY client/public/static ./client/dist/static RUN mkdir -p /data EXPOSE 3001 -HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD wget -qO- http://localhost:3001/api/health || exit 1 +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD wget -qO- http://localhost:3001/api/health || exit 1 CMD ["node", "server.js"] -- 2.52.0 From 20be30318f30c16b98a595f7f3202e71f6e6a974 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 00:45:53 -0600 Subject: [PATCH 04/28] feat: add local dev fallback version.json stub --- client/public/version.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 client/public/version.json diff --git a/client/public/version.json b/client/public/version.json new file mode 100644 index 0000000..d23d125 --- /dev/null +++ b/client/public/version.json @@ -0,0 +1,5 @@ +{ + "sha": "dev", + "shortSha": "dev", + "buildTime": null +} -- 2.52.0 From 51bf176f96c4d298c201544ded31fc2df17a1512 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 00:54:58 -0600 Subject: [PATCH 05/28] feat: load version.json at startup, expose via /api/health --- server.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/server.js b/server.js index d1dc66b..e4a220e 100755 --- a/server.js +++ b/server.js @@ -29,8 +29,19 @@ function audit(action, entityType, entityId, performedBy, details) { } } +// ── Version info (written by Dockerfile at build time) ─────────────────────── +// Falls back to { sha: 'dev' } when running outside a Docker build (local dev). +let BUILD_VERSION = { sha: 'dev', shortSha: 'dev', buildTime: null }; +try { + BUILD_VERSION = require('./client/dist/version.json'); +} catch (_) { /* pre-build or local dev — stub values are fine */ } + // Health -app.get('/api/health', (req, res) => res.json({ status: 'ok', timestamp: new Date().toISOString() })); +app.get('/api/health', (req, res) => res.json({ + status: 'ok', + timestamp: new Date().toISOString(), + version: BUILD_VERSION, +})); // ── Employees ──────────────────────────────────────────────────────────────── app.get('/api/employees', (req, res) => { -- 2.52.0 From f4ed8c49ce79d564e3f5f46469ccb3d977f1baa7 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 00:55:44 -0600 Subject: [PATCH 06/28] feat: fetch version.json on mount, show short SHA + commit link in footer --- client/src/App.jsx | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/client/src/App.jsx b/client/src/App.jsx index 37cb06d..0055663 100755 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -42,8 +42,13 @@ function GiteaIcon() { ); } -function AppFooter() { +function AppFooter({ version }) { const year = new Date().getFullYear(); + const sha = version?.shortSha || null; + const built = version?.buildTime + ? new Date(version.buildTime).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + : null; + return ( <>
- © {year} Jason Stedwell - · + © {year} Jason Stedwell + · - · + · cpas + {sha && sha !== 'dev' && ( + <> + · + + {sha} + + + )}
); @@ -129,6 +148,14 @@ const sf = { export default function App() { const [tab, setTab] = useState('dashboard'); const [showReadme, setShowReadme] = useState(false); + const [version, setVersion] = useState(null); + + useEffect(() => { + fetch('/version.json') + .then(r => r.ok ? r.json() : null) + .then(v => { if (v) setVersion(v); }) + .catch(() => {}); + }, []); return ( @@ -156,7 +183,7 @@ export default function App() { - + {showReadme && setShowReadme(false)} />} -- 2.52.0 From 602f371d67c90e684e6cfa04e310a85a0e431d91 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 22:02:10 -0500 Subject: [PATCH 07/28] Add mobile-responsive CSS utility file --- client/src/styles/mobile.css | 113 +++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 client/src/styles/mobile.css diff --git a/client/src/styles/mobile.css b/client/src/styles/mobile.css new file mode 100644 index 0000000..89c02f4 --- /dev/null +++ b/client/src/styles/mobile.css @@ -0,0 +1,113 @@ +/* Mobile-Responsive Utilities for CPAS Tracker */ +/* Target: Standard phones 375px+ with graceful degradation */ + +/* Base responsive utilities */ +@media (max-width: 768px) { + /* Hide scrollbars but keep functionality */ + * { + -webkit-overflow-scrolling: touch; + } + + /* Touch-friendly tap targets (min 44px) */ + button, a, input, select { + min-height: 44px; + } + + /* Improve form input sizing on mobile */ + input, select, textarea { + font-size: 16px !important; /* Prevents iOS zoom on focus */ + } +} + +/* Tablet and below */ +@media (max-width: 1024px) { + .hide-tablet { + display: none !important; + } +} + +/* Mobile portrait and landscape */ +@media (max-width: 768px) { + .hide-mobile { + display: none !important; + } + + .mobile-full-width { + width: 100% !important; + } + + .mobile-text-center { + text-align: center !important; + } + + .mobile-no-padding { + padding: 0 !important; + } + + .mobile-small-padding { + padding: 12px !important; + } + + /* Stack flex containers vertically */ + .mobile-stack { + flex-direction: column !important; + } + + /* Allow horizontal scroll for tables */ + .mobile-scroll-x { + overflow-x: auto !important; + -webkit-overflow-scrolling: touch; + } + + /* Card-based layout helpers */ + .mobile-card { + display: block !important; + padding: 16px; + margin-bottom: 12px; + border-radius: 8px; + background: #181924; + border: 1px solid #2a2b3a; + } + + .mobile-card-row { + display: flex; + justify-content: space-between; + padding: 8px 0; + border-bottom: 1px solid #1c1d29; + } + + .mobile-card-row:last-child { + border-bottom: none; + } + + .mobile-card-label { + font-weight: 600; + color: #9ca0b8; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + .mobile-card-value { + font-weight: 600; + color: #f8f9fa; + text-align: right; + } +} + +/* Small mobile phones */ +@media (max-width: 480px) { + .hide-small-mobile { + display: none !important; + } +} + +/* Utility for sticky positioning on mobile */ +@media (max-width: 768px) { + .mobile-sticky-top { + position: sticky; + top: 0; + z-index: 100; + background: #000000; + } +} -- 2.52.0 From 74492142a14a15b1d1eb810634b164119f67d051 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 8 Mar 2026 22:04:05 -0500 Subject: [PATCH 08/28] Update App.jsx with mobile-responsive navigation and layout --- client/src/App.jsx | 105 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 12 deletions(-) diff --git a/client/src/App.jsx b/client/src/App.jsx index 0055663..2b02499 100755 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -3,6 +3,7 @@ import ViolationForm from './components/ViolationForm'; import Dashboard from './components/Dashboard'; import ReadmeModal from './components/ReadmeModal'; import ToastProvider from './components/ToastProvider'; +import './styles/mobile.css'; const REPO_URL = 'https://git.alwisp.com/jason/cpas'; const PROJECT_START = new Date('2026-03-06T11:33:32-06:00'); @@ -56,8 +57,19 @@ function AppFooter({ version }) { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.4; transform: scale(0.75); } } + + /* Mobile-specific footer adjustments */ + @media (max-width: 768px) { + .footer-content { + flex-wrap: wrap; + justify-content: center; + font-size: 10px; + padding: 10px 16px; + gap: 8px; + } + } `} -