Files
orion/app/templates/admin/code-quality-violation-detail.html
Samir Boulahtit cc74970223 feat: add logging, marketplace, and admin enhancements
Database & Migrations:
- Add application_logs table migration for hybrid cloud logging
- Add companies table migration and restructure vendor relationships

Logging System:
- Implement hybrid logging system (database + file)
- Add log_service for centralized log management
- Create admin logs page with filtering and viewing capabilities
- Add init_log_settings.py script for log configuration
- Enhance core logging with database integration

Marketplace Integration:
- Add marketplace admin page with product management
- Create marketplace vendor page with product listings
- Implement marketplace.js for both admin and vendor interfaces
- Add marketplace integration documentation

Admin Enhancements:
- Add imports management page and functionality
- Create settings page for admin configuration
- Add vendor themes management page
- Enhance vendor detail and edit pages
- Improve code quality dashboard and violation details
- Add logs viewing and management
- Update icons guide and shared icon system

Architecture & Documentation:
- Document frontend structure and component architecture
- Document models structure and relationships
- Add vendor-in-token architecture documentation
- Add vendor RBAC (role-based access control) documentation
- Document marketplace integration patterns
- Update architecture patterns documentation

Infrastructure:
- Add platform static files structure (css, img, js)
- Move architecture_scan.py to proper models location
- Update model imports and registrations
- Enhance exception handling
- Update dependency injection patterns

UI/UX:
- Improve vendor edit interface
- Update admin user interface
- Enhance page templates documentation
- Add vendor marketplace interface
2025-12-01 21:51:07 +01:00

366 lines
17 KiB
HTML

{# app/templates/admin/code-quality-violation-detail.html #}
{% extends "admin/base.html" %}
{% block title %}Violation Detail{% endblock %}
{% block alpine_data %}codeQualityViolationDetail({{ violation_id }}){% endblock %}
{% block extra_scripts %}
<script>
function codeQualityViolationDetail(violationId) {
return {
...data(),
currentPage: 'code-quality',
violationId: violationId,
violation: null,
loading: true,
error: null,
updating: false,
commenting: false,
newComment: '',
assignUserId: '',
resolutionNote: '',
ignoreReason: '',
async init() {
await this.loadViolation();
},
async loadViolation() {
this.loading = true;
this.error = null;
try {
const response = await apiClient.get(`/admin/code-quality/violations/${this.violationId}`);
this.violation = response;
} catch (error) {
window.LogConfig.logError(error, 'Load Violation');
this.error = error.response?.data?.message || 'Failed to load violation details';
} finally {
this.loading = false;
}
},
async assignViolation() {
const userId = parseInt(this.assignUserId);
if (!userId || isNaN(userId)) {
Utils.showToast('Please enter a valid user ID', 'error');
return;
}
this.updating = true;
try {
await apiClient.post(`/admin/code-quality/violations/${this.violationId}/assign`, {
user_id: userId,
priority: 'medium'
});
this.assignUserId = '';
Utils.showToast('Violation assigned successfully', 'success');
await this.loadViolation();
} catch (error) {
window.LogConfig.logError(error, 'Assign Violation');
Utils.showToast(error.message || 'Failed to assign violation', 'error');
} finally {
this.updating = false;
}
},
async resolveViolation() {
if (!this.resolutionNote.trim()) {
Utils.showToast('Please enter a resolution note', 'error');
return;
}
this.updating = true;
try {
await apiClient.post(`/admin/code-quality/violations/${this.violationId}/resolve`, {
resolution_note: this.resolutionNote
});
this.resolutionNote = '';
Utils.showToast('Violation resolved successfully', 'success');
await this.loadViolation();
} catch (error) {
window.LogConfig.logError(error, 'Resolve Violation');
Utils.showToast(error.message || 'Failed to resolve violation', 'error');
} finally {
this.updating = false;
}
},
async ignoreViolation() {
if (!this.ignoreReason.trim()) {
Utils.showToast('Please enter a reason for ignoring', 'error');
return;
}
this.updating = true;
try {
await apiClient.post(`/admin/code-quality/violations/${this.violationId}/ignore`, {
reason: this.ignoreReason
});
this.ignoreReason = '';
Utils.showToast('Violation ignored successfully', 'success');
await this.loadViolation();
} catch (error) {
window.LogConfig.logError(error, 'Ignore Violation');
Utils.showToast(error.message || 'Failed to ignore violation', 'error');
} finally {
this.updating = false;
}
},
async addComment() {
if (!this.newComment.trim()) return;
this.commenting = true;
try {
await apiClient.post(`/admin/code-quality/violations/${this.violationId}/comments`, {
comment: this.newComment
});
this.newComment = '';
Utils.showToast('Comment added successfully', 'success');
await this.loadViolation();
} catch (error) {
window.LogConfig.logError(error, 'Add Comment');
Utils.showToast(error.message || 'Failed to add comment', 'error');
} finally {
this.commenting = false;
}
},
getSeverityColor(severity) {
return severity === 'error' ? 'red' : 'yellow';
},
getStatusColor(status) {
const colors = {
'open': 'blue',
'assigned': 'purple',
'resolved': 'green',
'ignored': 'gray'
};
return colors[status] || 'gray';
},
formatDate(dateString) {
return Utils.formatDate(dateString);
}
};
}
</script>
{% endblock %}
{% block content %}
<!-- Page Header -->
<div class="flex items-center justify-between my-6">
<div>
<h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200">
Violation Details
</h2>
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
Review and manage architecture violation
</p>
</div>
<a href="/admin/code-quality/violations"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-gray-700 dark:text-gray-300 transition-colors duration-150 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:shadow-outline-gray">
<span x-html="$icon('arrow-left', 'w-4 h-4 mr-2')"></span>
Back to Violations
</a>
</div>
<!-- Loading State -->
<div x-show="loading" class="text-center py-12">
<span x-html="$icon('spinner', 'inline w-8 h-8 text-purple-600')"></span>
<p class="mt-2 text-gray-600 dark:text-gray-400">Loading violation details...</p>
</div>
<!-- Error State -->
<div x-show="error && !loading" class="mb-6 p-4 bg-red-100 border border-red-400 text-red-700 rounded-lg flex items-start">
<span x-html="$icon('exclamation', 'w-5 h-5 mr-3 mt-0.5 flex-shrink-0')"></span>
<div>
<p class="font-semibold">Error loading violation</p>
<p class="text-sm" x-text="error"></p>
</div>
</div>
<!-- Content -->
<div x-show="!loading && violation" class="grid gap-6 mb-8">
<!-- Violation Info Card -->
<div class="p-6 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div class="flex items-start justify-between mb-4">
<div>
<div class="flex items-center gap-2 mb-2">
<span class="px-2 py-1 text-xs font-semibold rounded-full"
:class="`bg-${getSeverityColor(violation.severity)}-100 text-${getSeverityColor(violation.severity)}-800 dark:bg-${getSeverityColor(violation.severity)}-800 dark:text-${getSeverityColor(violation.severity)}-100`"
x-text="violation.severity.toUpperCase()"></span>
<span class="px-2 py-1 text-xs font-semibold rounded-full"
:class="`bg-${getStatusColor(violation.status)}-100 text-${getStatusColor(violation.status)}-800 dark:bg-${getStatusColor(violation.status)}-800 dark:text-${getStatusColor(violation.status)}-100`"
x-text="violation.status.toUpperCase()"></span>
</div>
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-200" x-text="violation.rule_id"></h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1" x-text="violation.rule_name"></p>
</div>
<div class="text-right text-sm text-gray-500 dark:text-gray-400">
<p>ID: <span x-text="violation.id"></span></p>
<p>Scan: <span x-text="violation.scan_id"></span></p>
</div>
</div>
<!-- Violation Details -->
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">File Path</label>
<code class="block px-3 py-2 bg-gray-100 dark:bg-gray-900 text-sm rounded" x-text="violation.file_path"></code>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">Line Number</label>
<p class="text-gray-900 dark:text-white" x-text="violation.line_number"></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">First Detected</label>
<p class="text-gray-900 dark:text-white" x-text="formatDate(violation.first_detected)"></p>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">Message</label>
<p class="text-gray-900 dark:text-white" x-text="violation.message"></p>
</div>
<div x-show="violation.context">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">Code Context</label>
<pre class="p-3 bg-gray-100 dark:bg-gray-900 text-sm rounded overflow-x-auto"><code x-text="violation.context"></code></pre>
</div>
<div x-show="violation.suggestion">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-1">Suggestion</label>
<div class="p-3 bg-blue-50 dark:bg-blue-900 text-sm rounded">
<p class="text-blue-900 dark:text-blue-100" x-text="violation.suggestion"></p>
</div>
</div>
</div>
</div>
<!-- Management Card -->
<div class="p-6 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<h4 class="text-lg font-semibold text-gray-700 dark:text-gray-200 mb-4">Manage Violation</h4>
<!-- Assign Section -->
<div class="mb-6" x-show="violation.status === 'open' || violation.status === 'assigned'">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-2">Assign to User</label>
<div class="flex gap-2">
<input x-model="assignUserId"
type="number"
placeholder="User ID"
class="flex-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input rounded-md">
<button @click="assignViolation()"
:disabled="updating || !assignUserId"
class="px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple disabled:opacity-50 disabled:cursor-not-allowed">
<span x-show="!updating">Assign</span>
<span x-show="updating">Assigning...</span>
</button>
</div>
<p x-show="violation.assigned_to" class="mt-1 text-xs text-gray-500">
Currently assigned to user ID: <span x-text="violation.assigned_to"></span>
</p>
</div>
<!-- Action Buttons -->
<div class="grid gap-4 md:grid-cols-2" x-show="violation.status === 'open' || violation.status === 'assigned'">
<!-- Resolve -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-2">Resolve Violation</label>
<textarea x-model="resolutionNote"
rows="2"
placeholder="Resolution note..."
class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-textarea rounded-md"></textarea>
<button @click="resolveViolation()"
:disabled="updating || !resolutionNote.trim()"
class="mt-2 w-full px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-green-600 border border-transparent rounded-lg active:bg-green-600 hover:bg-green-700 focus:outline-none focus:shadow-outline-green disabled:opacity-50 disabled:cursor-not-allowed">
<span x-show="!updating">Mark as Resolved</span>
<span x-show="updating">Resolving...</span>
</button>
</div>
<!-- Ignore -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-400 mb-2">Ignore Violation</label>
<textarea x-model="ignoreReason"
rows="2"
placeholder="Reason for ignoring..."
class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-textarea rounded-md"></textarea>
<button @click="ignoreViolation()"
:disabled="updating || !ignoreReason.trim()"
class="mt-2 w-full px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-gray-600 border border-transparent rounded-lg active:bg-gray-600 hover:bg-gray-700 focus:outline-none focus:shadow-outline-gray disabled:opacity-50 disabled:cursor-not-allowed">
<span x-show="!updating">Ignore Violation</span>
<span x-show="updating">Ignoring...</span>
</button>
</div>
</div>
<!-- Resolution Info (for resolved/ignored) -->
<div x-show="violation.status === 'resolved' || violation.status === 'ignored'" class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
<p class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
This violation has been <span x-text="violation.status"></span>
</p>
<p class="text-sm text-gray-600 dark:text-gray-400" x-show="violation.resolution_note">
Note: <span x-text="violation.resolution_note"></span>
</p>
<p class="text-sm text-gray-500 mt-1" x-show="violation.resolved_at">
<span x-text="formatDate(violation.resolved_at)"></span> by user ID <span x-text="violation.resolved_by"></span>
</p>
</div>
<!-- Comments Section -->
<div>
<h5 class="text-md font-semibold text-gray-700 dark:text-gray-200 mb-3">Comments</h5>
<!-- Add Comment -->
<div class="mb-4">
<textarea x-model="newComment"
rows="3"
placeholder="Add a comment..."
class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-textarea rounded-md"></textarea>
<div class="mt-2 flex justify-end">
<button @click="addComment()"
:disabled="commenting || !newComment.trim()"
class="px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple disabled:opacity-50 disabled:cursor-not-allowed">
<span x-show="!commenting">Add Comment</span>
<span x-show="commenting">Adding...</span>
</button>
</div>
</div>
<!-- Comment List -->
<div class="space-y-3">
<template x-if="!violation.comments || violation.comments.length === 0">
<p class="text-sm text-gray-500 dark:text-gray-400 italic">No comments yet</p>
</template>
<template x-for="comment in (violation.comments || [])" :key="comment.id">
<div class="p-3 bg-gray-50 dark:bg-gray-900 rounded-lg">
<div class="flex items-start justify-between mb-2">
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">
User ID: <span x-text="comment.user_id"></span>
</p>
<p class="text-xs text-gray-500" x-text="formatDate(comment.created_at)"></p>
</div>
<p class="text-sm text-gray-900 dark:text-white" x-text="comment.comment"></p>
</div>
</template>
</div>
</div>
</div>
</div>
{% endblock %}