Files
orion/static/admin/js/code-quality-violations.js
Samir Boulahtit 26b3dc9e3b feat: add unified code quality dashboard with multiple validators
- Add validator_type field to scans and violations (architecture,
  security, performance)
- Create security validator with SEC-xxx rules
- Create performance validator with PERF-xxx rules
- Add base validator class for shared functionality
- Add validate_all.py script to run all validators
- Update code quality service with validator type filtering
- Add validator type tabs to dashboard UI
- Add validator type filter to violations list
- Update stats response with per-validator breakdown
- Add security and performance rules documentation
- Add chat-bubble icons to icon library

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 20:57:47 +01:00

188 lines
6.1 KiB
JavaScript

/**
* Code Quality Violations List Component
* Manages the violations list page with filtering and pagination
*/
// ✅ Use centralized logger
const codeQualityViolationsLog = window.LogConfig.createLogger('CODE-QUALITY');
function codeQualityViolations() {
return {
// Extend base data
...data(),
// Set current page for navigation
currentPage: 'code-quality',
// Violations-specific data
loading: false,
error: null,
violations: [],
pagination: {
page: 1,
page_size: 50,
total: 0,
total_pages: 0
},
filters: {
validator_type: '',
severity: '',
status: '',
rule_id: '',
file_path: ''
},
async init() {
// Load filters from URL params
const params = new URLSearchParams(window.location.search);
this.filters.validator_type = params.get('validator_type') || '';
this.filters.severity = params.get('severity') || '';
this.filters.status = params.get('status') || '';
this.filters.rule_id = params.get('rule_id') || '';
this.filters.file_path = params.get('file_path') || '';
await this.loadViolations();
},
async loadViolations() {
this.loading = true;
this.error = null;
try {
// Build query params
const params = {
page: this.pagination.page.toString(),
page_size: this.pagination.page_size.toString()
};
if (this.filters.validator_type) params.validator_type = this.filters.validator_type;
if (this.filters.severity) params.severity = this.filters.severity;
if (this.filters.status) params.status = this.filters.status;
if (this.filters.rule_id) params.rule_id = this.filters.rule_id;
if (this.filters.file_path) params.file_path = this.filters.file_path;
const data = await apiClient.get('/admin/code-quality/violations', params);
this.violations = data.violations;
this.pagination = {
page: data.page,
page_size: data.page_size,
total: data.total,
total_pages: data.total_pages
};
// Update URL with current filters (without reloading)
this.updateURL();
} catch (err) {
codeQualityViolationsLog.error('Failed to load violations:', err);
this.error = err.message;
// Redirect to login if unauthorized
if (err.message.includes('Unauthorized')) {
window.location.href = '/admin/login';
}
} finally {
this.loading = false;
}
},
applyFilters() {
// Reset to page 1 when filters change
this.pagination.page = 1;
this.loadViolations();
},
async nextPage() {
if (this.pagination.page < this.pagination.total_pages) {
this.pagination.page++;
await this.loadViolations();
}
},
// Computed: Total number of pages
get totalPages() {
return this.pagination.total_pages;
},
// Computed: Start index for pagination display
get startIndex() {
if (this.pagination.total === 0) return 0;
return (this.pagination.page - 1) * this.pagination.page_size + 1;
},
// Computed: End index for pagination display
get endIndex() {
const end = this.pagination.page * this.pagination.page_size;
return end > this.pagination.total ? this.pagination.total : end;
},
// Computed: Generate page numbers array with ellipsis
get pageNumbers() {
const pages = [];
const totalPages = this.totalPages;
const current = this.pagination.page;
if (totalPages <= 7) {
// Show all pages if 7 or fewer
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
} else {
// Always show first page
pages.push(1);
if (current > 3) {
pages.push('...');
}
// Show pages around current page
const start = Math.max(2, current - 1);
const end = Math.min(totalPages - 1, current + 1);
for (let i = start; i <= end; i++) {
pages.push(i);
}
if (current < totalPages - 2) {
pages.push('...');
}
// Always show last page
pages.push(totalPages);
}
return pages;
},
async previousPage() {
if (this.pagination.page > 1) {
this.pagination.page--;
await this.loadViolations();
}
},
goToPage(pageNum) {
if (pageNum !== '...' && pageNum >= 1 && pageNum <= this.totalPages) {
this.pagination.page = pageNum;
this.loadViolations();
}
},
updateURL() {
const params = new URLSearchParams();
if (this.filters.validator_type) params.set('validator_type', this.filters.validator_type);
if (this.filters.severity) params.set('severity', this.filters.severity);
if (this.filters.status) params.set('status', this.filters.status);
if (this.filters.rule_id) params.set('rule_id', this.filters.rule_id);
if (this.filters.file_path) params.set('file_path', this.filters.file_path);
const newURL = params.toString()
? `${window.location.pathname}?${params.toString()}`
: window.location.pathname;
window.history.replaceState({}, '', newURL);
}
};
}