Implement comprehensive code quality dashboard (Phase 2-4) to track and manage
architecture violations found by the validation script.
Backend Implementation:
- Add JSON output support to validate_architecture.py script
- Create CodeQualityService with scan management, violation tracking, and statistics
- Implement REST API endpoints for code quality management:
* POST /admin/code-quality/scan - trigger new architecture scan
* GET /admin/code-quality/scans - list scan history
* GET /admin/code-quality/violations - list violations with filtering/pagination
* GET /admin/code-quality/violations/{id} - get violation details
* POST /admin/code-quality/violations/{id}/assign - assign to developer
* POST /admin/code-quality/violations/{id}/resolve - mark as resolved
* POST /admin/code-quality/violations/{id}/ignore - mark as ignored
* POST /admin/code-quality/violations/{id}/comments - add comments
* GET /admin/code-quality/stats - dashboard statistics
- Fix architecture_scan model imports to use app.core.database
Frontend Implementation:
- Create code quality dashboard page (code-quality-dashboard.html)
* Summary cards for total violations, errors, warnings, health score
* Status breakdown (open, assigned, resolved, ignored)
* Trend visualization for last 7 scans
* Top violating files list
* Violations by rule and module
* Quick action links
- Create violations list page (code-quality-violations.html)
* Filterable table by severity, status, rule ID, file path
* Pagination support
* Violation detail view links
- Add Alpine.js components (code-quality-dashboard.js, code-quality-violations.js)
* Dashboard state management and scan triggering
* Violations list with filtering and pagination
* API integration with authentication
- Add "Code Quality" navigation link in admin sidebar (Developer Tools section)
Routes:
- GET /admin/code-quality - dashboard page
- GET /admin/code-quality/violations - violations list
- GET /admin/code-quality/violations/{id} - violation details
Features:
- Real-time scan execution from UI
- Technical debt score calculation (0-100 scale)
- Violation workflow: open → assigned → resolved/ignored
- Trend tracking across multiple scans
- File and module-level insights
- Assignment system with priorities and due dates
- Collaborative comments on violations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
334 lines
18 KiB
HTML
334 lines
18 KiB
HTML
{# app/templates/admin/partials/sidebar.html #}
|
|
<!-- Desktop sidebar -->
|
|
<aside class="z-20 hidden w-64 overflow-y-auto bg-white dark:bg-gray-800 md:block flex-shrink-0">
|
|
<div class="py-4 text-gray-500 dark:text-gray-400">
|
|
<a class="ml-6 text-lg font-bold text-gray-800 dark:text-gray-200" href="/admin/dashboard">
|
|
Admin Portal
|
|
</a>
|
|
<ul class="mt-6">
|
|
<!-- Dashboard -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'dashboard'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'dashboard' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/dashboard">
|
|
<span x-html="$icon('home')"></span>
|
|
<span class="ml-4">Dashboard</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Main Navigation -->
|
|
<ul>
|
|
<!-- Vendors -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'vendors'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'vendors' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/vendors">
|
|
<span x-html="$icon('shopping-bag')"></span>
|
|
<span class="ml-4">Vendors</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Users -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'users'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'users' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/users">
|
|
<span x-html="$icon('users')"></span>
|
|
<span class="ml-4">Users</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Import Jobs -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'imports'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'imports' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/imports">
|
|
<span x-html="$icon('cube')"></span>
|
|
<span class="ml-4">Import Jobs</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Content Management Section -->
|
|
<div class="px-6 my-6">
|
|
<hr class="border-gray-200 dark:border-gray-700" />
|
|
</div>
|
|
<p class="px-6 text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider">
|
|
Content Management
|
|
</p>
|
|
<ul class="mt-3">
|
|
<!-- Platform Homepage -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'platform-homepage'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'platform-homepage' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/platform-homepage">
|
|
<span x-html="$icon('home')"></span>
|
|
<span class="ml-4">Platform Homepage</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Content Pages -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'content-pages'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'content-pages' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/content-pages">
|
|
<span x-html="$icon('document-text')"></span>
|
|
<span class="ml-4">Content Pages</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Developer Tools Section -->
|
|
<div class="px-6 my-6">
|
|
<hr class="border-gray-200 dark:border-gray-700" />
|
|
</div>
|
|
<p class="px-6 text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider">
|
|
Developer Tools
|
|
</p>
|
|
<ul class="mt-3">
|
|
<!-- Components -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'components'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'components' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/components">
|
|
<span x-html="$icon('view-grid')"></span>
|
|
<span class="ml-4">Components</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Icons -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'icons'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'icons' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/icons">
|
|
<span x-html="$icon('photograph')"></span>
|
|
<span class="ml-4">Icons</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Testing Hub -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'testing'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'testing' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/testing">
|
|
<span x-html="$icon('beaker')"></span>
|
|
<span class="ml-4">Testing Hub</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Code Quality -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'code-quality'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'code-quality' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/code-quality">
|
|
<span x-html="$icon('shield-check')"></span>
|
|
<span class="ml-4">Code Quality</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Settings Section -->
|
|
<div class="px-6 my-6">
|
|
<hr class="border-gray-200 dark:border-gray-700" />
|
|
</div>
|
|
<ul>
|
|
<!-- Settings -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'settings'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'settings' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/settings">
|
|
<span x-html="$icon('cog')"></span>
|
|
<span class="ml-4">Settings</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- Mobile sidebar -->
|
|
<div x-show="isSideMenuOpen"
|
|
x-transition:enter="transition ease-in-out duration-150"
|
|
x-transition:enter-start="opacity-0"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in-out duration-150"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
class="fixed inset-0 z-10 flex items-end bg-black bg-opacity-50 sm:items-center sm:justify-center"></div>
|
|
|
|
<aside class="fixed inset-y-0 z-20 flex-shrink-0 w-64 mt-16 overflow-y-auto bg-white dark:bg-gray-800 md:hidden"
|
|
x-show="isSideMenuOpen"
|
|
x-transition:enter="transition ease-in-out duration-150"
|
|
x-transition:enter-start="opacity-0 transform -translate-x-20"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in-out duration-150"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0 transform -translate-x-20"
|
|
@click.away="closeSideMenu"
|
|
@keydown.escape="closeSideMenu">
|
|
<div class="py-4 text-gray-500 dark:text-gray-400">
|
|
<a class="ml-6 text-lg font-bold text-gray-800 dark:text-gray-200" href="/admin/dashboard">
|
|
Admin Portal
|
|
</a>
|
|
<ul class="mt-6">
|
|
<!-- Dashboard -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'dashboard'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'dashboard' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/dashboard">
|
|
<span x-html="$icon('home')"></span>
|
|
<span class="ml-4">Dashboard</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Main Navigation -->
|
|
<ul>
|
|
<!-- Vendors -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'vendors'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'vendors' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/vendors">
|
|
<span x-html="$icon('shopping-bag')"></span>
|
|
<span class="ml-4">Vendors</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Users -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'users'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'users' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/users">
|
|
<span x-html="$icon('users')"></span>
|
|
<span class="ml-4">Users</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Import Jobs -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'imports'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'imports' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/imports">
|
|
<span x-html="$icon('cube')"></span>
|
|
<span class="ml-4">Import Jobs</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Content Management Section -->
|
|
<div class="px-6 my-6">
|
|
<hr class="border-gray-200 dark:border-gray-700" />
|
|
</div>
|
|
<p class="px-6 text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider">
|
|
Content Management
|
|
</p>
|
|
<ul class="mt-3">
|
|
<!-- Platform Homepage -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'platform-homepage'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'platform-homepage' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/platform-homepage">
|
|
<span x-html="$icon('home')"></span>
|
|
<span class="ml-4">Platform Homepage</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Content Pages -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'content-pages'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'content-pages' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/content-pages">
|
|
<span x-html="$icon('document-text')"></span>
|
|
<span class="ml-4">Content Pages</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Developer Tools Section -->
|
|
<div class="px-6 my-6">
|
|
<hr class="border-gray-200 dark:border-gray-700" />
|
|
</div>
|
|
<p class="px-6 text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider">
|
|
Developer Tools
|
|
</p>
|
|
<ul class="mt-3">
|
|
<!-- Components -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'components'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'components' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/components">
|
|
<span x-html="$icon('view-grid')"></span>
|
|
<span class="ml-4">Components</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Icons -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'icons'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'icons' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/icons">
|
|
<span x-html="$icon('photograph')"></span>
|
|
<span class="ml-4">Icons</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Testing Hub -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'testing'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'testing' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/testing">
|
|
<span x-html="$icon('beaker')"></span>
|
|
<span class="ml-4">Testing Hub</span>
|
|
</a>
|
|
</li>
|
|
|
|
<!-- Code Quality -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'code-quality'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'code-quality' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/code-quality">
|
|
<span x-html="$icon('shield-check')"></span>
|
|
<span class="ml-4">Code Quality</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Settings Section -->
|
|
<div class="px-6 my-6">
|
|
<hr class="border-gray-200 dark:border-gray-700" />
|
|
</div>
|
|
<ul>
|
|
<!-- Settings -->
|
|
<li class="relative px-6 py-3">
|
|
<span x-show="currentPage === 'settings'" class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
|
|
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
|
|
:class="currentPage === 'settings' ? 'text-gray-800 dark:text-gray-100' : ''"
|
|
href="/admin/settings">
|
|
<span x-html="$icon('cog')"></span>
|
|
<span class="ml-4">Settings</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</aside> |