Files
orion/app/modules/dev_tools/static/admin/js/code-quality-dashboard.js
Samir Boulahtit 0b4291d893 refactor(js): migrate JavaScript files to module directories
Move 47 JS files from static/{admin,vendor,shared}/js/ to their
respective module directories app/modules/*/static/*/js/:

- Orders: orders.js, order-detail.js
- Catalog: products.js (renamed from vendor-products.js), product-*.js
- Inventory: inventory.js (admin & vendor)
- Customers: customers.js, users.js, user-*.js
- Billing: billing-history.js, subscriptions.js, subscription-tiers.js,
  billing.js, invoices.js, feature-store.js, upgrade-prompts.js
- Messaging: messages.js, notifications.js, email-templates.js
- Marketplace: marketplace*.js, letzshop*.js, onboarding.js
- Monitoring: monitoring.js, background-tasks.js, imports.js, logs.js
- Dev Tools: testing-*.js, code-quality-*.js

Update 39 templates to reference new module static paths using
url_for('{module}_static', path='...') pattern.

Files staying in static/ (platform core):
- admin: dashboard, login, platforms, vendors, companies, admin-users,
  settings, components, init-alpine, module-config
- vendor: dashboard, login, profile, settings, team, media, init-alpine
- shared: api-client, utils, money, icons, log-config, vendor-selector,
  media-picker

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:08:20 +01:00

293 lines
10 KiB
JavaScript

// noqa: js-006 - async init pattern is safe, loadData has try/catch
/**
* Code Quality Dashboard Component
* Manages the unified code quality dashboard page
* Supports multiple validator types: architecture, security, performance
*/
// Use centralized logger
const codeQualityLog = window.LogConfig.createLogger('CODE-QUALITY');
function codeQualityDashboard() {
return {
// Extend base data
...data(),
// Set current page for navigation
currentPage: 'code-quality',
// Validator type selection
selectedValidator: 'all', // 'all', 'architecture', 'security', 'performance'
validatorTypes: ['architecture', 'security', 'performance'],
// Dashboard-specific data
loading: false,
scanning: false,
scanDropdownOpen: false,
error: null,
successMessage: null,
scanProgress: null, // Progress message during scan
runningScans: [], // Track running scan IDs
pollInterval: null, // Polling interval ID
stats: {
total_violations: 0,
errors: 0,
warnings: 0,
info: 0,
open: 0,
assigned: 0,
resolved: 0,
ignored: 0,
technical_debt_score: 100,
trend: [],
by_severity: {},
by_rule: {},
by_module: {},
top_files: [],
last_scan: null,
validator_type: null,
by_validator: {}
},
async init() {
// Guard against multiple initialization
if (window._adminCodeQualityDashboardInitialized) return;
window._adminCodeQualityDashboardInitialized = true;
try {
// Check URL for validator_type parameter
const urlParams = new URLSearchParams(window.location.search);
const urlValidator = urlParams.get('validator_type');
if (urlValidator && this.validatorTypes.includes(urlValidator)) {
this.selectedValidator = urlValidator;
} else {
// Ensure 'all' is explicitly set as default
this.selectedValidator = 'all';
}
await this.loadStats();
// Check for any running scans on page load
await this.checkRunningScans();
} catch (error) {
codeQualityLog.error('Failed to initialize code quality dashboard:', error);
}
},
async checkRunningScans() {
try {
const runningScans = await apiClient.get('/admin/code-quality/scans/running');
if (runningScans && runningScans.length > 0) {
this.scanning = true;
this.runningScans = runningScans.map(s => s.id);
this.updateProgressMessage(runningScans);
this.startPolling();
}
} catch (err) {
codeQualityLog.error('Failed to check running scans:', err);
}
},
updateProgressMessage(scans) {
const runningScans = scans.filter(s => s.status === 'running' || s.status === 'pending');
if (runningScans.length === 0) {
this.scanProgress = null;
return;
}
// Show progress from the first running scan
const firstRunning = runningScans.find(s => s.status === 'running');
if (firstRunning && firstRunning.progress_message) {
this.scanProgress = firstRunning.progress_message;
} else {
const validatorNames = runningScans.map(s => this.capitalizeFirst(s.validator_type));
this.scanProgress = `Running ${validatorNames.join(', ')} scan${runningScans.length > 1 ? 's' : ''}...`;
}
},
async loadStats() {
this.loading = true;
this.error = null;
try {
// Build URL with validator_type filter if not 'all'
let url = '/admin/code-quality/stats';
if (this.selectedValidator !== 'all') {
url += `?validator_type=${this.selectedValidator}`;
}
const stats = await apiClient.get(url);
this.stats = stats;
} catch (err) {
codeQualityLog.error('Failed to load stats:', err);
this.error = err.message;
// Redirect to login if unauthorized
if (err.message.includes('Unauthorized')) {
window.location.href = '/admin/login';
}
} finally {
this.loading = false;
}
},
async selectValidator(validatorType) {
if (this.selectedValidator !== validatorType) {
this.selectedValidator = validatorType;
await this.loadStats();
// Update URL without reload
const url = new URL(window.location);
if (validatorType === 'all') {
url.searchParams.delete('validator_type');
} else {
url.searchParams.set('validator_type', validatorType);
}
window.history.pushState({}, '', url);
}
},
async runScan(validatorType = 'all') {
this.scanning = true;
this.error = null;
this.successMessage = null;
this.scanProgress = 'Queuing scan...';
try {
// Determine which validators to run
const validatorTypesToRun = validatorType === 'all'
? this.validatorTypes
: [validatorType];
const result = await apiClient.post('/admin/code-quality/scan', {
validator_types: validatorTypesToRun
});
// Store running scan IDs for polling
this.runningScans = result.scans.map(s => s.id);
// Show initial status message
const validatorNames = validatorTypesToRun.map(v => this.capitalizeFirst(v));
this.scanProgress = `Running ${validatorNames.join(', ')} scan${validatorNames.length > 1 ? 's' : ''}...`;
// Start polling for completion
this.startPolling();
} catch (err) {
codeQualityLog.error('Failed to run scan:', err);
this.error = err.message;
this.scanning = false;
this.scanProgress = null;
// Redirect to login if unauthorized
if (err.message.includes('Unauthorized')) {
window.location.href = '/admin/login';
}
}
},
startPolling() {
// Clear any existing polling
if (this.pollInterval) {
clearInterval(this.pollInterval);
}
// Poll every 3 seconds
this.pollInterval = setInterval(async () => {
await this.pollScanStatus();
}, 3000);
},
stopPolling() {
if (this.pollInterval) {
clearInterval(this.pollInterval);
this.pollInterval = null;
}
},
async pollScanStatus() {
if (this.runningScans.length === 0) {
this.stopPolling();
this.scanning = false;
this.scanProgress = null;
return;
}
try {
const runningScans = await apiClient.get('/admin/code-quality/scans/running');
// Update progress message from running scans
this.updateProgressMessage(runningScans);
// Check if our scans have completed
const stillRunning = this.runningScans.filter(id =>
runningScans.some(s => s.id === id)
);
if (stillRunning.length === 0) {
// All scans completed - get final results
await this.handleScanCompletion();
} else {
// Update running scans list
this.runningScans = stillRunning;
}
} catch (err) {
codeQualityLog.error('Failed to poll scan status:', err);
}
},
async handleScanCompletion() {
this.stopPolling();
// Get results for all completed scans
let totalViolations = 0;
let totalErrors = 0;
let totalWarnings = 0;
const completedScans = [];
for (const scanId of this.runningScans) {
try {
const scan = await apiClient.get(`/admin/code-quality/scans/${scanId}/status`);
completedScans.push(scan);
totalViolations += scan.total_violations || 0;
totalErrors += scan.errors || 0;
totalWarnings += scan.warnings || 0;
} catch (err) {
codeQualityLog.error(`Failed to get scan ${scanId} results:`, err);
}
}
// Format success message based on number of validators run
if (completedScans.length > 1) {
this.successMessage = `Scan completed: ${totalViolations} total violations found (${totalErrors} errors, ${totalWarnings} warnings) across ${completedScans.length} validators`;
} else if (completedScans.length === 1) {
const scan = completedScans[0];
this.successMessage = `${this.capitalizeFirst(scan.validator_type)} scan completed: ${scan.total_violations} violations found (${scan.errors} errors, ${scan.warnings} warnings)`;
} else {
this.successMessage = 'Scan completed';
}
// Reload stats after scan
await this.loadStats();
// Reset scanning state
this.scanning = false;
this.scanProgress = null;
this.runningScans = [];
// Clear success message after 5 seconds
setTimeout(() => {
this.successMessage = null;
}, 5000);
},
async refresh() {
await this.loadStats();
},
capitalizeFirst(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
};
}