feat: custom violation types — persist, manage, and use in violation form
Adds a full CRUD system for user-defined violation types stored in a new violation_types table. Custom types appear in the violation dropdown alongside hardcoded types, grouped by category, with ✦ marker and a green Custom badge. - db/database.js: auto-migration adds violation_types table on startup - server.js: GET/POST/PUT/DELETE /api/violation-types; type_key auto-generated as custom_<slug>; DELETE blocked if any violations reference the type - ViolationTypeModal.jsx: create/edit modal with name, category (datalist autocomplete from existing categories), handbook chapter reference, description/reference text, fixed vs sliding point toggle, context field checkboxes; delete with usage guard - ViolationForm.jsx: fetches custom types on mount; merges into dropdown via mergedGroups memo; resolveViolation() checks hardcoded then custom; '+ Add Type' button above dropdown; 'Edit Type' button appears when a custom type is selected; newly created type auto-selects; all audit calls flow through existing audit() helper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,23 @@ db.exec(`CREATE TABLE IF NOT EXISTS audit_log (
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
|
||||
// ── Feature: Custom Violation Types ──────────────────────────────────────────
|
||||
// Persisted violation type definitions created via the UI. type_key is prefixed
|
||||
// with 'custom_' to prevent collisions with hardcoded violation keys.
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS violation_types (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
type_key TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
category TEXT NOT NULL DEFAULT 'Custom',
|
||||
chapter TEXT,
|
||||
description TEXT,
|
||||
min_points INTEGER NOT NULL DEFAULT 1,
|
||||
max_points INTEGER NOT NULL DEFAULT 1,
|
||||
fields TEXT NOT NULL DEFAULT '["description"]',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
|
||||
// Recreate view so it always filters negated rows
|
||||
db.exec(`DROP VIEW IF EXISTS active_cpas_scores;
|
||||
CREATE VIEW active_cpas_scores AS
|
||||
|
||||
Reference in New Issue
Block a user