fix: resolve architecture validation errors in media and customers APIs

- Add proper media exceptions (MediaNotFoundException, MediaUploadException, etc.)
- Update media service to use exceptions from app/exceptions/media
- Remove direct HTTPException raises from vendor/media.py and vendor/customers.py
- Move db.query from customers API to service layer (get_customer_orders)
- Fix pagination macro call in vendor/media.html template
- Update media.js: add parent init call, PlatformSettings, apiClient.postFormData
- Add try/catch error handling to media.js init method

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-06 22:40:10 +01:00
parent 8ee3f91467
commit 7b81f59eba
7 changed files with 397 additions and 248 deletions

View File

@@ -48,6 +48,60 @@ function vendorMedia() {
pages: 0
},
// Computed pagination properties required by pagination macro
get startIndex() {
if (this.pagination.total === 0) return 0;
return (this.pagination.page - 1) * this.pagination.per_page + 1;
},
get endIndex() {
return Math.min(this.pagination.page * this.pagination.per_page, this.pagination.total);
},
get totalPages() {
return this.pagination.pages;
},
get pageNumbers() {
const pages = [];
const total = this.pagination.pages;
const current = this.pagination.page;
if (total <= 7) {
for (let i = 1; i <= total; i++) pages.push(i);
} else {
pages.push(1);
if (current > 3) pages.push('...');
for (let i = Math.max(2, current - 1); i <= Math.min(total - 1, current + 1); i++) {
pages.push(i);
}
if (current < total - 2) pages.push('...');
pages.push(total);
}
return pages;
},
previousPage() {
if (this.pagination.page > 1) {
this.pagination.page--;
this.loadMedia();
}
},
nextPage() {
if (this.pagination.page < this.pagination.pages) {
this.pagination.page++;
this.loadMedia();
}
},
goToPage(pageNum) {
if (pageNum !== '...' && pageNum >= 1 && pageNum <= this.pagination.pages) {
this.pagination.page = pageNum;
this.loadMedia();
}
},
// Modal states
showUploadModal: false,
showDetailModal: false,
@@ -65,8 +119,30 @@ function vendorMedia() {
uploadingFiles: [],
async init() {
// Guard against duplicate initialization
if (window._vendorMediaInitialized) return;
window._vendorMediaInitialized = true;
vendorMediaLog.info('Initializing media library...');
await this.loadMedia();
try {
// IMPORTANT: Call parent init first to set vendorCode from URL
const parentInit = data().init;
if (parentInit) {
await parentInit.call(this);
}
// Initialize pagination per_page from PlatformSettings
if (window.PlatformSettings) {
this.pagination.per_page = await window.PlatformSettings.getRowsPerPage();
}
await this.loadMedia();
} catch (err) {
vendorMediaLog.error('Failed to initialize media library:', err);
this.error = err.message || 'Failed to initialize media library';
this.loading = false;
}
},
async loadMedia() {
@@ -231,22 +307,19 @@ function vendorMedia() {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`/api/v1/vendor/media/upload?folder=${this.uploadFolder}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${localStorage.getItem('vendor_token')}`
},
body: formData
});
// Use apiClient.postFormData for automatic auth handling
const response = await apiClient.postFormData(
`/vendor/media/upload?folder=${this.uploadFolder}`,
formData
);
if (response.ok) {
uploadItem.status = 'success';
vendorMediaLog.info(`Uploaded: ${file.name}`);
} else {
const errorData = await response.json();
uploadItem.status = 'error';
uploadItem.error = errorData.detail || 'Upload failed';
vendorMediaLog.error(`Upload failed for ${file.name}:`, errorData);
uploadItem.error = response.message || 'Upload failed';
vendorMediaLog.error(`Upload failed for ${file.name}:`, response);
}
} catch (err) {
uploadItem.status = 'error';