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>
This commit is contained in:
2025-12-21 20:57:47 +01:00
parent 6a903e16c6
commit 26b3dc9e3b
27 changed files with 5270 additions and 119 deletions

View File

@@ -25,6 +25,7 @@ function codeQualityViolations() {
total_pages: 0
},
filters: {
validator_type: '',
severity: '',
status: '',
rule_id: '',
@@ -34,6 +35,7 @@ function codeQualityViolations() {
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') || '';
@@ -53,6 +55,7 @@ function codeQualityViolations() {
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;
@@ -168,6 +171,7 @@ function codeQualityViolations() {
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);

View File

@@ -167,7 +167,11 @@ const Icons = {
// Trust & Payment Icons
'cash': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z"/></svg>`,
'support': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192l-3.536 3.536M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-5 0a4 4 0 11-8 0 4 4 0 018 0z"/></svg>`,
'emoji-happy': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>`
'emoji-happy': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>`,
// Messaging
'chat-bubble-left-right': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12.5c0 2.5-2.015 4.5-4.5 4.5H11l-4 4v-4H4.5C2.015 17 0 15 0 12.5v-5C0 5.015 2.015 3 4.5 3h11C18.015 3 20 5.015 20 7.5v5z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9.5c0-2.485 2.015-4.5 4.5-4.5h3c2.485 0 4.5 2.015 4.5 4.5v5c0 2.485-2.015 4.5-4.5 4.5H17l-3 3v-3h-.5c-2.485 0-4.5-2.015-4.5-4.5v-5z"/></svg>`,
'chat-bubble-left': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/></svg>`
};
/**