fix: Letzshop page improvements - products, jobs, and tabs

1. Products tab now shows Letzshop marketplace products instead of vendor products
   - Uses /admin/products endpoint with marketplace=Letzshop filter
   - Fixed field names (image_link, price_numeric, sku vs vendor_sku)
   - Search now works with title, GTIN, SKU, brand

2. Jobs section moved to a separate tab
   - New "Jobs" tab between Exceptions and Settings
   - Tab watcher reloads data when switching tabs
   - Updated filter dropdown (removed export, added historical_import)

3. Product stats endpoint now accepts marketplace and vendor_name filters

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-20 22:34:55 +01:00
parent 2bdf0a977a
commit ee690e95c9
6 changed files with 102 additions and 55 deletions

View File

@@ -153,6 +153,20 @@ function adminMarketplaceLetzshop() {
this.initTomSelect();
});
// Watch for tab changes to reload relevant data
this.$watch('activeTab', async (newTab) => {
marketplaceLetzshopLog.info('Tab changed to:', newTab);
if (newTab === 'jobs' && this.selectedVendor) {
await this.loadJobs();
} else if (newTab === 'products' && this.selectedVendor) {
await this.loadProducts();
} else if (newTab === 'orders') {
await this.loadOrders();
} else if (newTab === 'exceptions') {
await Promise.all([this.loadExceptions(), this.loadExceptionStats()]);
}
});
// Check localStorage for last selected vendor
const savedVendorId = localStorage.getItem('letzshop_selected_vendor_id');
if (savedVendorId) {
@@ -408,11 +422,12 @@ function adminMarketplaceLetzshop() {
},
// ═══════════════════════════════════════════════════════════════
// PRODUCTS TAB - LISTING
// PRODUCTS TAB - LISTING (Letzshop marketplace products)
// ═══════════════════════════════════════════════════════════════
/**
* Load products for selected vendor
* Load Letzshop products for selected vendor
* Uses /admin/products endpoint with marketplace=Letzshop filter
*/
async loadProducts() {
if (!this.selectedVendor) {
@@ -426,7 +441,8 @@ function adminMarketplaceLetzshop() {
try {
const params = new URLSearchParams({
vendor_id: this.selectedVendor.id.toString(),
marketplace: 'Letzshop',
vendor_name: this.selectedVendor.name,
skip: ((this.productsPage - 1) * this.productsLimit).toString(),
limit: this.productsLimit.toString()
});
@@ -439,17 +455,12 @@ function adminMarketplaceLetzshop() {
params.append('is_active', this.productFilters.is_active);
}
const response = await apiClient.get(`/admin/vendor-products?${params}`);
const response = await apiClient.get(`/admin/products?${params}`);
this.products = response.products || [];
this.totalProducts = response.total || 0;
// Update stats
if (response.stats) {
this.productStats = response.stats;
} else {
// Calculate from response if not provided
await this.loadProductStats();
}
// Load stats separately
await this.loadProductStats();
} catch (error) {
marketplaceLetzshopLog.error('Failed to load products:', error);
this.products = [];
@@ -460,18 +471,22 @@ function adminMarketplaceLetzshop() {
},
/**
* Load product statistics
* Load product statistics for Letzshop products
*/
async loadProductStats() {
if (!this.selectedVendor) return;
try {
const response = await apiClient.get(`/admin/vendor-products/stats?vendor_id=${this.selectedVendor.id}`);
const params = new URLSearchParams({
marketplace: 'Letzshop',
vendor_name: this.selectedVendor.name
});
const response = await apiClient.get(`/admin/products/stats?${params}`);
this.productStats = {
total: response.total || 0,
active: response.active || 0,
inactive: response.inactive || 0,
last_sync: response.last_sync || null
last_sync: null // TODO: Get from last import job
};
} catch (error) {
marketplaceLetzshopLog.error('Failed to load product stats:', error);