feat: add import error tracking and translation tabs
Import Error Tracking:
- Add MarketplaceImportError model to store detailed error information
- Store row number, identifier, error type, message, and row data for each error
- Add API endpoint GET /admin/marketplace-import-jobs/{job_id}/errors
- Add UI to view and browse import errors in job details modal
- Support pagination and error type filtering
Translation Tabs:
- Replace flat translation list with tabbed interface on product detail page
- Add language tabs with full language names
- Add copy-to-clipboard functionality for translation content
- Improved UX with better visual separation of translations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,12 @@ function adminImports() {
|
||||
showJobModal: false,
|
||||
selectedJob: null,
|
||||
|
||||
// Job errors state
|
||||
jobErrors: [],
|
||||
jobErrorsTotal: 0,
|
||||
jobErrorsPage: 1,
|
||||
loadingErrors: false,
|
||||
|
||||
// Auto-refresh for active jobs
|
||||
autoRefreshInterval: null,
|
||||
|
||||
@@ -275,6 +281,58 @@ function adminImports() {
|
||||
closeJobModal() {
|
||||
this.showJobModal = false;
|
||||
this.selectedJob = null;
|
||||
// Clear errors state
|
||||
this.jobErrors = [];
|
||||
this.jobErrorsTotal = 0;
|
||||
this.jobErrorsPage = 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load errors for a specific job
|
||||
*/
|
||||
async loadJobErrors(jobId) {
|
||||
if (!jobId) return;
|
||||
|
||||
this.loadingErrors = true;
|
||||
this.jobErrorsPage = 1;
|
||||
|
||||
try {
|
||||
const response = await apiClient.get(
|
||||
`/admin/marketplace-import-jobs/${jobId}/errors?page=1&limit=20`
|
||||
);
|
||||
this.jobErrors = response.errors || [];
|
||||
this.jobErrorsTotal = response.total || 0;
|
||||
adminImportsLog.debug('Loaded job errors:', this.jobErrors.length);
|
||||
} catch (error) {
|
||||
adminImportsLog.error('Failed to load job errors:', error);
|
||||
this.error = error.message || 'Failed to load import errors';
|
||||
} finally {
|
||||
this.loadingErrors = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Load more errors (pagination)
|
||||
*/
|
||||
async loadMoreJobErrors(jobId) {
|
||||
if (!jobId || this.loadingErrors) return;
|
||||
|
||||
this.loadingErrors = true;
|
||||
this.jobErrorsPage++;
|
||||
|
||||
try {
|
||||
const response = await apiClient.get(
|
||||
`/admin/marketplace-import-jobs/${jobId}/errors?page=${this.jobErrorsPage}&limit=20`
|
||||
);
|
||||
const newErrors = response.errors || [];
|
||||
this.jobErrors = [...this.jobErrors, ...newErrors];
|
||||
adminImportsLog.debug('Loaded more job errors:', newErrors.length);
|
||||
} catch (error) {
|
||||
adminImportsLog.error('Failed to load more job errors:', error);
|
||||
this.jobErrorsPage--; // Revert page on failure
|
||||
} finally {
|
||||
this.loadingErrors = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -176,6 +176,53 @@ function adminMarketplaceProductDetail() {
|
||||
} catch (e) {
|
||||
return dateString;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get full language name from ISO code
|
||||
*/
|
||||
getLanguageName(code) {
|
||||
const languages = {
|
||||
'en': 'English',
|
||||
'de': 'German',
|
||||
'fr': 'French',
|
||||
'lb': 'Luxembourgish',
|
||||
'es': 'Spanish',
|
||||
'it': 'Italian',
|
||||
'nl': 'Dutch',
|
||||
'pt': 'Portuguese',
|
||||
'pl': 'Polish',
|
||||
'cs': 'Czech',
|
||||
'da': 'Danish',
|
||||
'sv': 'Swedish',
|
||||
'fi': 'Finnish',
|
||||
'no': 'Norwegian',
|
||||
'hu': 'Hungarian',
|
||||
'ro': 'Romanian',
|
||||
'bg': 'Bulgarian',
|
||||
'el': 'Greek',
|
||||
'sk': 'Slovak',
|
||||
'sl': 'Slovenian',
|
||||
'hr': 'Croatian',
|
||||
'lt': 'Lithuanian',
|
||||
'lv': 'Latvian',
|
||||
'et': 'Estonian'
|
||||
};
|
||||
return languages[code?.toLowerCase()] || '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy text to clipboard
|
||||
*/
|
||||
async copyToClipboard(text) {
|
||||
if (!text) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
Utils.showToast('Copied to clipboard', 'success');
|
||||
} catch (err) {
|
||||
adminMarketplaceProductDetailLog.error('Failed to copy to clipboard:', err);
|
||||
Utils.showToast('Failed to copy to clipboard', 'error');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user