// static/admin/js/vendor-product-edit.js /** * Admin vendor product edit page logic * Edit vendor product information with translations */ const adminVendorProductEditLog = window.LogConfig.loggers.adminVendorProductEdit || window.LogConfig.createLogger('adminVendorProductEdit', false); adminVendorProductEditLog.info('Loading...'); function adminVendorProductEdit() { adminVendorProductEditLog.info('adminVendorProductEdit() called'); // Extract product ID from URL const pathParts = window.location.pathname.split('/'); const productId = parseInt(pathParts[pathParts.length - 2]); // /vendor-products/{id}/edit // Default translations structure const defaultTranslations = () => ({ en: { title: '', description: '' }, fr: { title: '', description: '' }, de: { title: '', description: '' }, lu: { title: '', description: '' } }); return { // Inherit base layout state ...data(), // Set page identifier currentPage: 'vendor-products', // Product ID from URL productId: productId, // Loading states loading: true, saving: false, error: '', // Product data from API product: null, // Active language tab activeLanguage: 'en', // Form data form: { // Translations by language translations: defaultTranslations(), // Product identifiers vendor_sku: '', brand: '', gtin: '', gtin_type: 'ean13', // Pricing price: null, sale_price: null, currency: 'EUR', tax_rate_percent: 17, availability: '', // Image primary_image_url: '', // Product type & status is_digital: false, is_active: true, is_featured: false, // Optional supplier info supplier: '', cost: null }, async init() { adminVendorProductEditLog.info('Vendor Product Edit init() called, ID:', this.productId); // Guard against multiple initialization if (window._adminVendorProductEditInitialized) { adminVendorProductEditLog.warn('Already initialized, skipping'); return; } window._adminVendorProductEditInitialized = true; // Load product data await this.loadProduct(); adminVendorProductEditLog.info('Vendor Product Edit initialization complete'); }, /** * Load product details and populate form */ async loadProduct() { this.loading = true; this.error = ''; try { const response = await apiClient.get(`/admin/vendor-products/${this.productId}`); this.product = response; adminVendorProductEditLog.info('Loaded product:', response); // Populate translations from vendor_translations const translations = defaultTranslations(); if (response.vendor_translations) { for (const lang of ['en', 'fr', 'de', 'lu']) { if (response.vendor_translations[lang]) { translations[lang] = { title: response.vendor_translations[lang].title || '', description: response.vendor_translations[lang].description || '' }; } } } // Populate form with current values this.form = { translations: translations, // Product identifiers vendor_sku: response.vendor_sku || '', brand: response.brand || '', gtin: response.gtin || '', gtin_type: response.gtin_type || 'ean13', // Pricing (convert cents to euros if stored as cents) price: response.price || null, sale_price: response.sale_price || null, currency: response.currency || 'EUR', tax_rate_percent: response.tax_rate_percent ?? 17, availability: response.availability || '', // Image primary_image_url: response.primary_image_url || '', // Product type & status is_digital: response.is_digital ?? false, is_active: response.is_active ?? true, is_featured: response.is_featured ?? false, // Optional supplier info supplier: response.supplier || '', cost: response.cost || null }; adminVendorProductEditLog.info('Form initialized:', this.form); } catch (error) { adminVendorProductEditLog.error('Failed to load product:', error); this.error = error.message || 'Failed to load product details'; } finally { this.loading = false; } }, /** * Check if form is valid (all mandatory fields filled) */ isFormValid() { // English title and description are required if (!this.form.translations.en.title?.trim()) return false; if (!this.form.translations.en.description?.trim()) return false; // Product identifiers if (!this.form.vendor_sku?.trim()) return false; if (!this.form.brand?.trim()) return false; if (!this.form.gtin?.trim()) return false; if (!this.form.gtin_type) return false; // Pricing if (this.form.price === null || this.form.price === '' || this.form.price < 0) return false; if (!this.form.currency) return false; if (this.form.tax_rate_percent === null || this.form.tax_rate_percent === '') return false; // Image if (!this.form.primary_image_url?.trim()) return false; return true; }, /** * Generate a unique vendor SKU * Format: XXXX_XXXX_XXXX (includes vendor_id for uniqueness) */ generateSku() { const vendorId = this.product?.vendor_id || 0; // Generate random alphanumeric segments const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const generateSegment = (length) => { let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; }; // First segment includes vendor ID (padded) const vendorSegment = vendorId.toString().padStart(4, '0').slice(-4); // Generate SKU: VID + random + random const sku = `${vendorSegment}_${generateSegment(4)}_${generateSegment(4)}`; this.form.vendor_sku = sku; adminVendorProductEditLog.info('Generated SKU:', sku); }, /** * Save product changes */ async saveProduct() { if (!this.isFormValid()) { Utils.showToast('Please fill in all required fields', 'error'); return; } this.saving = true; try { // Build translations object for API const translations = {}; for (const lang of ['en', 'fr', 'de', 'lu']) { const t = this.form.translations[lang]; // Only include if there's actual content if (t.title?.trim() || t.description?.trim()) { translations[lang] = { title: t.title?.trim() || null, description: t.description?.trim() || null }; } } // Build update payload const payload = { translations: Object.keys(translations).length > 0 ? translations : null, // Product identifiers vendor_sku: this.form.vendor_sku?.trim() || null, brand: this.form.brand?.trim() || null, gtin: this.form.gtin?.trim() || null, gtin_type: this.form.gtin_type || null, // Pricing price: this.form.price !== null && this.form.price !== '' ? parseFloat(this.form.price) : null, sale_price: this.form.sale_price !== null && this.form.sale_price !== '' ? parseFloat(this.form.sale_price) : null, currency: this.form.currency || null, tax_rate_percent: this.form.tax_rate_percent !== null && this.form.tax_rate_percent !== '' ? parseInt(this.form.tax_rate_percent) : null, availability: this.form.availability || null, // Image primary_image_url: this.form.primary_image_url?.trim() || null, // Status is_digital: this.form.is_digital, is_active: this.form.is_active, is_featured: this.form.is_featured, // Optional supplier info supplier: this.form.supplier?.trim() || null, cost: this.form.cost !== null && this.form.cost !== '' ? parseFloat(this.form.cost) : null }; adminVendorProductEditLog.info('Saving payload:', payload); await apiClient.patch(`/admin/vendor-products/${this.productId}`, payload); adminVendorProductEditLog.info('Product saved:', this.productId); Utils.showToast('Product updated successfully', 'success'); // Redirect to detail page setTimeout(() => { window.location.href = `/admin/vendor-products/${this.productId}`; }, 1000); } catch (error) { adminVendorProductEditLog.error('Failed to save product:', error); Utils.showToast(error.message || 'Failed to save product', 'error'); } finally { this.saving = false; } } }; }