// static/vendor/js/marketplace.js /** * Vendor marketplace import page logic */ // ✅ Use centralized logger const vendorMarketplaceLog = window.LogConfig.loggers.marketplace; console.log('[VENDOR MARKETPLACE] Loading...'); function vendorMarketplace() { console.log('[VENDOR MARKETPLACE] vendorMarketplace() called'); return { // ✅ Inherit base layout state ...data(), // ✅ Set page identifier currentPage: 'marketplace', // Loading states loading: false, importing: false, error: '', successMessage: '', // Import form importForm: { csv_url: '', marketplace: 'Letzshop', language: 'fr', batch_size: 1000 }, // Vendor settings (for quick fill) vendorSettings: { letzshop_csv_url_fr: '', letzshop_csv_url_en: '', letzshop_csv_url_de: '' }, // Import jobs jobs: [], totalJobs: 0, page: 1, limit: 10, // Modal state showJobModal: false, selectedJob: null, // Auto-refresh for active jobs autoRefreshInterval: null, async init() { // Guard against multiple initialization if (window._vendorMarketplaceInitialized) { return; } window._vendorMarketplaceInitialized = true; // IMPORTANT: Call parent init first to set vendorCode from URL const parentInit = data().init; if (parentInit) { await parentInit.call(this); } await this.loadVendorSettings(); await this.loadJobs(); // Auto-refresh active jobs every 10 seconds this.startAutoRefresh(); }, /** * Load vendor settings (for quick fill) */ async loadVendorSettings() { try { const response = await apiClient.get('/vendor/settings'); this.vendorSettings = { letzshop_csv_url_fr: response.letzshop_csv_url_fr || '', letzshop_csv_url_en: response.letzshop_csv_url_en || '', letzshop_csv_url_de: response.letzshop_csv_url_de || '' }; } catch (error) { console.error('[VENDOR MARKETPLACE] Failed to load vendor settings:', error); // Non-critical, don't show error to user } }, /** * Load import jobs */ async loadJobs() { this.loading = true; this.error = ''; try { const response = await apiClient.get( `/vendor/marketplace/imports?page=${this.page}&limit=${this.limit}` ); this.jobs = response.items || []; this.totalJobs = response.total || 0; console.log('[VENDOR MARKETPLACE] Loaded jobs:', this.jobs.length); } catch (error) { console.error('[VENDOR MARKETPLACE] Failed to load jobs:', error); this.error = error.message || 'Failed to load import jobs'; } finally { this.loading = false; } }, /** * Start new import */ async startImport() { if (!this.importForm.csv_url) { this.error = 'Please enter a CSV URL'; return; } this.importing = true; this.error = ''; this.successMessage = ''; try { const payload = { source_url: this.importForm.csv_url, marketplace: this.importForm.marketplace, batch_size: this.importForm.batch_size }; console.log('[VENDOR MARKETPLACE] Starting import:', payload); const response = await apiClient.post('/vendor/marketplace/import', payload); console.log('[VENDOR MARKETPLACE] Import started:', response); this.successMessage = `Import job #${response.job_id} started successfully!`; // Clear form this.importForm.csv_url = ''; this.importForm.language = 'fr'; this.importForm.batch_size = 1000; // Reload jobs to show the new import await this.loadJobs(); // Clear success message after 5 seconds setTimeout(() => { this.successMessage = ''; }, 5000); } catch (error) { console.error('[VENDOR MARKETPLACE] Failed to start import:', error); this.error = error.message || 'Failed to start import'; } finally { this.importing = false; } }, /** * Quick fill form with saved CSV URL */ quickFill(language) { const urlMap = { 'fr': this.vendorSettings.letzshop_csv_url_fr, 'en': this.vendorSettings.letzshop_csv_url_en, 'de': this.vendorSettings.letzshop_csv_url_de }; const url = urlMap[language]; if (url) { this.importForm.csv_url = url; this.importForm.language = language; console.log('[VENDOR MARKETPLACE] Quick filled:', language, url); } }, /** * Refresh jobs list */ async refreshJobs() { await this.loadJobs(); }, /** * Refresh single job status */ async refreshJobStatus(jobId) { try { const response = await apiClient.get(`/vendor/marketplace/imports/${jobId}`); // Update job in list const index = this.jobs.findIndex(j => j.id === jobId); if (index !== -1) { this.jobs[index] = response; } // Update selected job if modal is open if (this.selectedJob && this.selectedJob.id === jobId) { this.selectedJob = response; } console.log('[VENDOR MARKETPLACE] Refreshed job:', jobId); } catch (error) { console.error('[VENDOR MARKETPLACE] Failed to refresh job:', error); } }, /** * View job details in modal */ async viewJobDetails(jobId) { try { const response = await apiClient.get(`/vendor/marketplace/imports/${jobId}`); this.selectedJob = response; this.showJobModal = true; console.log('[VENDOR MARKETPLACE] Viewing job details:', jobId); } catch (error) { console.error('[VENDOR MARKETPLACE] Failed to load job details:', error); this.error = error.message || 'Failed to load job details'; } }, /** * Close job details modal */ closeJobModal() { this.showJobModal = false; this.selectedJob = null; }, /** * Pagination: Previous page */ async previousPage() { if (this.page > 1) { this.page--; await this.loadJobs(); } }, /** * Pagination: Next page */ async nextPage() { if (this.page * this.limit < this.totalJobs) { this.page++; await this.loadJobs(); } }, /** * Format date for display */ formatDate(dateString) { if (!dateString) return 'N/A'; try { const date = new Date(dateString); return date.toLocaleString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch (error) { return dateString; } }, /** * Calculate duration between start and end */ calculateDuration(job) { if (!job.started_at) { return 'Not started'; } const start = new Date(job.started_at); const end = job.completed_at ? new Date(job.completed_at) : new Date(); const durationMs = end - start; // Convert to human-readable format const seconds = Math.floor(durationMs / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); if (hours > 0) { return `${hours}h ${minutes % 60}m`; } else if (minutes > 0) { return `${minutes}m ${seconds % 60}s`; } else { return `${seconds}s`; } }, /** * Start auto-refresh for active jobs */ startAutoRefresh() { // Clear any existing interval if (this.autoRefreshInterval) { clearInterval(this.autoRefreshInterval); } // Refresh every 10 seconds if there are active jobs this.autoRefreshInterval = setInterval(async () => { const hasActiveJobs = this.jobs.some(job => job.status === 'pending' || job.status === 'processing' ); if (hasActiveJobs) { console.log('[VENDOR MARKETPLACE] Auto-refreshing active jobs...'); await this.loadJobs(); } }, 10000); // 10 seconds }, /** * Stop auto-refresh (cleanup) */ stopAutoRefresh() { if (this.autoRefreshInterval) { clearInterval(this.autoRefreshInterval); this.autoRefreshInterval = null; } } }; } // Cleanup on page unload window.addEventListener('beforeunload', () => { if (window._vendorMarketplaceInstance && window._vendorMarketplaceInstance.stopAutoRefresh) { window._vendorMarketplaceInstance.stopAutoRefresh(); } });