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>
197 lines
6.5 KiB
JavaScript
197 lines
6.5 KiB
JavaScript
// noqa: js-006 - async init pattern is safe, loadData has try/catch
|
|
/**
|
|
* 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,
|
|
per_page: 20,
|
|
total: 0,
|
|
pages: 0
|
|
},
|
|
filters: {
|
|
validator_type: '',
|
|
severity: '',
|
|
status: '',
|
|
rule_id: '',
|
|
file_path: ''
|
|
},
|
|
|
|
async init() {
|
|
// Guard against multiple initialization
|
|
if (window._codeQualityViolationsInitialized) {
|
|
codeQualityViolationsLog.warn('Already initialized, skipping');
|
|
return;
|
|
}
|
|
window._codeQualityViolationsInitialized = true;
|
|
|
|
// Load platform settings for rows per page
|
|
if (window.PlatformSettings) {
|
|
this.pagination.per_page = await window.PlatformSettings.getRowsPerPage();
|
|
}
|
|
|
|
// 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.per_page.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.total = data.total;
|
|
this.pagination.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.pages) {
|
|
this.pagination.page++;
|
|
await this.loadViolations();
|
|
}
|
|
},
|
|
|
|
// Computed: Total number of pages
|
|
get totalPages() {
|
|
return this.pagination.pages;
|
|
},
|
|
|
|
// Computed: Start index for pagination display
|
|
get startIndex() {
|
|
if (this.pagination.total === 0) return 0;
|
|
return (this.pagination.page - 1) * this.pagination.per_page + 1;
|
|
},
|
|
|
|
// Computed: End index for pagination display
|
|
get endIndex() {
|
|
const end = this.pagination.page * this.pagination.per_page;
|
|
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);
|
|
}
|
|
};
|
|
}
|