New feature: multi-file per-vertical compliance xlsx upload with scoped resolution logic, executive-level aggregated reporting, and drill-down by vertical and metric. Supports daily upload cadence and batch commit. Backend: - Migration: add vertical column to compliance_items/uploads, create vcl_multi_vertical_summary table - New route module: routes/vclMultiVertical.js with preview, commit, stats, trend, metric drill-down, device list, and burndown endpoints - New helpers: parseVerticalFilename(), computeVerticalBurndown() - Vertical-scoped resolution: uploading one vertical never resolves items from other verticals Frontend: - CCPMetricsPage with stats bar, trend chart, donut, vertical table - Drill-down: vertical -> metrics by category -> device list - Per-vertical burndown forecast chart - MultiVerticalUploadModal: multi-file drag-drop, batch preview, commit - Nav entry: CCP Metrics (Building2 icon) Docs: - Design brief for stakeholder meeting (docs/vcl-multi-vertical-design-brief.md)
66 lines
3.2 KiB
JavaScript
66 lines
3.2 KiB
JavaScript
// Migration: Add multi-vertical support for VCL compliance reporting
|
|
// Adds vertical column to compliance_items and compliance_uploads,
|
|
// creates vcl_multi_vertical_summary table for per-vertical metric data.
|
|
const pool = require('../db');
|
|
|
|
async function run() {
|
|
console.log('Starting VCL multi-vertical migration...');
|
|
try {
|
|
// Add vertical column to compliance_items
|
|
await pool.query(`ALTER TABLE compliance_items ADD COLUMN IF NOT EXISTS vertical TEXT DEFAULT NULL`);
|
|
console.log('✓ vertical column added to compliance_items');
|
|
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_compliance_items_vertical ON compliance_items(vertical)`);
|
|
console.log('✓ idx_compliance_items_vertical index created');
|
|
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_compliance_items_vertical_status ON compliance_items(vertical, status)`);
|
|
console.log('✓ idx_compliance_items_vertical_status index created');
|
|
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_compliance_items_vertical_metric ON compliance_items(vertical, metric_id, status)`);
|
|
console.log('✓ idx_compliance_items_vertical_metric index created');
|
|
|
|
// Add vertical column to compliance_uploads
|
|
await pool.query(`ALTER TABLE compliance_uploads ADD COLUMN IF NOT EXISTS vertical TEXT DEFAULT NULL`);
|
|
console.log('✓ vertical column added to compliance_uploads');
|
|
|
|
// Create summary table for per-vertical metric data from Summary sheets
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS vcl_multi_vertical_summary (
|
|
id SERIAL PRIMARY KEY,
|
|
upload_id INTEGER NOT NULL REFERENCES compliance_uploads(id) ON DELETE CASCADE,
|
|
vertical TEXT NOT NULL,
|
|
metric_id TEXT NOT NULL,
|
|
metric_desc TEXT DEFAULT '',
|
|
category TEXT DEFAULT 'Other',
|
|
team TEXT DEFAULT '',
|
|
priority TEXT DEFAULT '',
|
|
non_compliant INTEGER DEFAULT 0,
|
|
compliant INTEGER DEFAULT 0,
|
|
total INTEGER DEFAULT 0,
|
|
compliance_pct NUMERIC(5,2) DEFAULT 0,
|
|
target NUMERIC(5,2) DEFAULT 0,
|
|
status TEXT DEFAULT '',
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
)
|
|
`);
|
|
console.log('✓ vcl_multi_vertical_summary table created');
|
|
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_vcl_multi_summary_vertical ON vcl_multi_vertical_summary(vertical)`);
|
|
console.log('✓ idx_vcl_multi_summary_vertical index created');
|
|
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_vcl_multi_summary_upload ON vcl_multi_vertical_summary(upload_id)`);
|
|
console.log('✓ idx_vcl_multi_summary_upload index created');
|
|
|
|
await pool.query(`CREATE INDEX IF NOT EXISTS idx_vcl_multi_summary_vertical_metric ON vcl_multi_vertical_summary(vertical, metric_id)`);
|
|
console.log('✓ idx_vcl_multi_summary_vertical_metric index created');
|
|
|
|
} catch (err) {
|
|
console.error('Migration error:', err.message);
|
|
process.exit(1);
|
|
}
|
|
console.log('Migration complete.');
|
|
process.exit(0);
|
|
}
|
|
|
|
run();
|