// static/admin/js/vendor-product-create.js /** * Admin vendor product create page logic * Create new vendor product entries with translations */ const adminVendorProductCreateLog = window.LogConfig.loggers.adminVendorProductCreate || window.LogConfig.createLogger('adminVendorProductCreate', false); adminVendorProductCreateLog.info('Loading...'); function adminVendorProductCreate() { adminVendorProductCreateLog.info('adminVendorProductCreate() called'); // 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', // Loading states loading: false, saving: false, // Tom Select instance vendorSelectInstance: null, // Active language tab activeLanguage: 'en', // Form data form: { vendor_id: null, // Translations by language translations: defaultTranslations(), // Product identifiers vendor_sku: '', brand: '', gtin: '', gtin_type: '', // Pricing price: null, sale_price: null, currency: 'EUR', tax_rate_percent: 17, availability: '', // Image primary_image_url: '', // Status is_active: true, is_featured: false, is_digital: false }, async init() { adminVendorProductCreateLog.info('Vendor Product Create init() called'); // Guard against multiple initialization if (window._adminVendorProductCreateInitialized) { adminVendorProductCreateLog.warn('Already initialized, skipping'); return; } window._adminVendorProductCreateInitialized = true; // Initialize Tom Select this.initVendorSelect(); adminVendorProductCreateLog.info('Vendor Product Create initialization complete'); }, /** * Initialize Tom Select for vendor autocomplete */ initVendorSelect() { const selectEl = this.$refs.vendorSelect; if (!selectEl) { adminVendorProductCreateLog.warn('Vendor select element not found'); return; } // Wait for Tom Select to be available if (typeof TomSelect === 'undefined') { adminVendorProductCreateLog.warn('TomSelect not loaded, retrying in 100ms'); setTimeout(() => this.initVendorSelect(), 100); return; } this.vendorSelectInstance = new TomSelect(selectEl, { valueField: 'id', labelField: 'name', searchField: ['name', 'vendor_code'], placeholder: 'Search vendor...', load: async (query, callback) => { try { const response = await apiClient.get('/admin/vendors', { search: query, limit: 50 }); callback(response.vendors || []); } catch (error) { adminVendorProductCreateLog.error('Failed to search vendors:', error); callback([]); } }, render: { option: (data, escape) => { return `
${escape(data.name)} ${escape(data.vendor_code || '')}
`; }, item: (data, escape) => { return `
${escape(data.name)}
`; } }, onChange: (value) => { this.form.vendor_id = value ? parseInt(value) : null; } }); adminVendorProductCreateLog.info('Vendor select initialized'); }, /** * Generate a unique vendor SKU * Format: XXXX_XXXX_XXXX (includes vendor_id for uniqueness) */ generateSku() { const vendorId = this.form.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; adminVendorProductCreateLog.info('Generated SKU:', sku); }, /** * Create the product */ async createProduct() { if (!this.form.vendor_id) { Utils.showToast('Please select a vendor', 'error'); return; } if (!this.form.translations.en.title?.trim()) { Utils.showToast('Please enter a product title (English)', 'error'); return; } this.saving = true; try { // Build translations object for API (only include non-empty) const translations = {}; for (const lang of ['en', 'fr', 'de', 'lu']) { const t = this.form.translations[lang]; if (t.title?.trim() || t.description?.trim()) { translations[lang] = { title: t.title?.trim() || null, description: t.description?.trim() || null }; } } // Build create payload const payload = { vendor_id: this.form.vendor_id, translations: Object.keys(translations).length > 0 ? translations : null, // Product identifiers brand: this.form.brand?.trim() || null, vendor_sku: this.form.vendor_sku?.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 || 'EUR', tax_rate_percent: this.form.tax_rate_percent !== null ? parseInt(this.form.tax_rate_percent) : 17, availability: this.form.availability || null, // Image primary_image_url: this.form.primary_image_url?.trim() || null, // Status is_active: this.form.is_active, is_featured: this.form.is_featured, is_digital: this.form.is_digital }; adminVendorProductCreateLog.info('Creating product with payload:', payload); const response = await apiClient.post('/admin/vendor-products', payload); adminVendorProductCreateLog.info('Product created:', response.id); Utils.showToast('Product created successfully', 'success'); // Redirect to the new product's detail page setTimeout(() => { window.location.href = `/admin/vendor-products/${response.id}`; }, 1000); } catch (error) { adminVendorProductCreateLog.error('Failed to create product:', error); Utils.showToast(error.message || 'Failed to create product', 'error'); } finally { this.saving = false; } } }; }