Spaces:
Running
Running
| // Simulated vendor configuration data | |
| const vendorConfig = [ | |
| {"Vendor_Carrier": "Lenovo", "Method": "Pallet", "CUSTPONBR": "Customer PO", "SKU": "Product ID", "SHIPQTY": "Product Quantity", "PACKAGEID": "Pallet ID"}, | |
| {"Vendor_Carrier": "HP", "Method": "Pallet", "CUSTPONBR": "PO", "SKU": "VPN", "SHIPQTY": "UNITS", "PACKAGEID": "PACKAHE"}, | |
| {"Vendor_Carrier": "Microsoft", "Method": "VPN", "CUSTPONBR": "PONum", "SKU": "PARTID", "SHIPQTY": "SHPQTY", "PACKAGEID": "PARTID"} | |
| ]; | |
| // DOM Elements | |
| const vendorSelect = document.getElementById('vendor-select'); | |
| const fileInput = document.getElementById('file-input'); | |
| const dropZone = document.getElementById('drop-zone'); | |
| const fileInfo = document.getElementById('file-info'); | |
| const fileName = document.getElementById('file-name'); | |
| const processBtn = document.getElementById('process-btn'); | |
| const resultsContainer = document.getElementById('results-container'); | |
| const errorContainer = document.getElementById('error-container'); | |
| const errorMessage = document.getElementById('error-message'); | |
| const jobInfo = document.getElementById('job-info'); | |
| const timestampEl = document.getElementById('timestamp'); | |
| const headerRowEl = document.getElementById('header-row'); | |
| const columnIndicesEl = document.getElementById('column-indices'); | |
| const resultsTableBody = document.querySelector('#results-table tbody'); | |
| const downloadBtn = document.getElementById('download-btn'); | |
| let currentFile = null; | |
| let transformedData = []; | |
| // Initialize vendor dropdown | |
| function initVendorDropdown() { | |
| vendorConfig.forEach(vendor => { | |
| const option = document.createElement('option'); | |
| option.value = vendor.Vendor_Carrier; | |
| option.textContent = vendor.Vendor_Carrier; | |
| vendorSelect.appendChild(option); | |
| }); | |
| } | |
| // Handle file selection | |
| function handleFileSelection(file) { | |
| if (!file) return; | |
| const validTypes = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', | |
| 'application/vnd.ms-excel', | |
| 'text/csv', | |
| 'text/plain']; | |
| if (!validTypes.includes(file.type)) { | |
| showError('Invalid file type. Please upload an Excel or CSV file.'); | |
| return; | |
| } | |
| currentFile = file; | |
| fileName.textContent = file.name; | |
| fileInfo.classList.remove('hidden'); | |
| processBtn.disabled = false; | |
| } | |
| // Drag and drop handlers | |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, preventDefaults, false); | |
| }); | |
| function preventDefaults(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| } | |
| ['dragenter', 'dragover'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, highlight, false); | |
| }); | |
| ['dragleave', 'drop'].forEach(eventName => { | |
| dropZone.addEventListener(eventName, unhighlight, false); | |
| }); | |
| function highlight() { | |
| dropZone.classList.add('dragover'); | |
| } | |
| function unhighlight() { | |
| dropZone.classList.remove('dragover'); | |
| } | |
| dropZone.addEventListener('drop', handleDrop, false); | |
| function handleDrop(e) { | |
| const dt = e.dataTransfer; | |
| const file = dt.files[0]; | |
| handleFileSelection(file); | |
| } | |
| // Click on drop zone triggers file input | |
| dropZone.addEventListener('click', () => { | |
| fileInput.click(); | |
| }); | |
| fileInput.addEventListener('change', (e) => { | |
| handleFileSelection(e.target.files[0]); | |
| }); | |
| // Process file button handler | |
| processBtn.addEventListener('click', processFile); | |
| // Download CSV handler | |
| downloadBtn.addEventListener('click', downloadCSV); | |
| // Initialize the app | |
| initVendorDropdown(); | |
| // Main processing function | |
| async function processFile() { | |
| if (!currentFile || !vendorSelect.value) { | |
| showError('Please select a vendor and upload a file.'); | |
| return; | |
| } | |
| // Show loading state | |
| processBtn.disabled = true; | |
| processBtn.innerHTML = '<i data-feather="loader" class="mr-2 animate-spin"></i>Processing...'; | |
| feather.replace(); | |
| try { | |
| // In a real app, this would be an API call to the backend | |
| // For this demo, we'll simulate the processing | |
| await new Promise(resolve => setTimeout(resolve, 1500)); | |
| // Get selected vendor config | |
| const vendor = vendorConfig.find(v => v.Vendor_Carrier === vendorSelect.value); | |
| if (!vendor) throw new Error('Vendor configuration not found'); | |
| // Simulate data transformation | |
| const result = simulateDataTransformation(vendor); | |
| // Display results | |
| displayResults(result.data, result.jobInfo); | |
| } catch (error) { | |
| showError(error.message); | |
| } finally { | |
| processBtn.disabled = false; | |
| processBtn.innerHTML = '<i data-feather="play" class="mr-2"></i>Process File'; | |
| feather.replace(); | |
| } | |
| } | |
| // Simulate data transformation (in a real app, this would be server-side) | |
| function simulateDataTransformation(vendorConfig) { | |
| // Build mapping dictionary | |
| const mapping = {}; | |
| const requiredHeaders = []; | |
| ['CUSTPONBR', 'SKU', 'SHIPQTY', 'PACKAGEID'].forEach(internal => { | |
| const external = vendorConfig[internal]; | |
| if (external && external.trim() !== '') { | |
| mapping[external] = internal; | |
| requiredHeaders.push(external); | |
| } | |
| }); | |
| // Simulate finding header row (in a real app, this would parse the file) | |
| const headerRowIndex = Math.floor(Math.random() * 3); // Random row 0-2 | |
| // Validate headers (simulated) | |
| const missingHeaders = []; | |
| const foundHeaders = Object.keys(mapping); | |
| requiredHeaders.forEach(header => { | |
| if (!foundHeaders.includes(header)) { | |
| missingHeaders.push(header); | |
| } | |
| }); | |
| if (missingHeaders.length > 0) { | |
| throw new Error(`Missing required headers: ${missingHeaders.join(', ')}`); | |
| } | |
| // Simulate transformed data | |
| const data = []; | |
| const rowCount = 5 + Math.floor(Math.random() * 10); | |
| for (let i = 0; i < rowCount; i++) { | |
| data.push({ | |
| CUSTPONBR: `PO-${Math.floor(Math.random() * 10000)}`, | |
| SKU: `SKU-${Math.floor(Math.random() * 1000)}`, | |
| SHIPQTY: Math.floor(Math.random() * 100), | |
| PACKAGEID: `PKG-${Math.floor(Math.random() * 100)}` | |
| }); | |
| } | |
| // Generate job info | |
| const now = new Date(); | |
| const jobInfo = { | |
| timestamp: now.toISOString().replace('T', ' ').substring(0, 19), | |
| headerRow: headerRowIndex, | |
| columnIndices: generateColumnIndices(Object.keys(mapping).length) | |
| }; | |
| return { data, jobInfo }; | |
| } | |
| // Generate fake column indices (A, B, C, etc.) | |
| function generateColumnIndices(count) { | |
| const indices = []; | |
| for (let i = 0; i < count; i++) { | |
| indices.push(String.fromCharCode(65 + i)); // ASCII 'A' is 65 | |
| } | |
| return indices.join(', '); | |
| } | |
| // Display results in the table | |
| function displayResults(data, jobInfoData) { | |
| // Clear previous results | |
| resultsTableBody.innerHTML = ''; | |
| // Populate table with data | |
| transformedData = data; | |
| data.forEach(row => { | |
| const tr = document.createElement('tr'); | |
| tr.innerHTML = ` | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">${row.CUSTPONBR}</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">${row.SKU}</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">${row.SHIPQTY}</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">${row.PACKAGEID}</td> | |
| `; | |
| resultsTableBody.appendChild(tr); | |
| }); | |
| // Update job info | |
| timestampEl.textContent = jobInfoData.timestamp; | |
| headerRowEl.textContent = jobInfoData.headerRow; | |
| columnIndicesEl.textContent = jobInfoData.columnIndices; | |
| // Show results and job info | |
| resultsContainer.classList.remove('hidden'); | |
| jobInfo.classList.remove('hidden'); | |
| // Hide error if visible | |
| errorContainer.classList.add('hidden'); | |
| // Scroll to results | |
| resultsContainer.scrollIntoView({ behavior: 'smooth' }); | |
| } | |
| // Show error message | |
| function showError(message) { | |
| errorMessage.innerHTML = `<p>${message}</p>`; | |
| errorContainer.classList.remove('hidden'); | |
| resultsContainer.classList.add('hidden'); | |
| jobInfo.classList.add('hidden'); | |
| // Scroll to error | |
| errorContainer.scrollIntoView({ behavior: 'smooth' }); | |
| } | |
| // Download CSV | |
| function downloadCSV() { | |
| if (transformedData.length === 0) return; | |
| // Create CSV content | |
| let csvContent = "CUSTPONBR,SKU,SHIPQTY,PACKAGEID\n"; | |
| transformedData.forEach(row => { | |
| csvContent += `${row.CUSTPONBR},${row.SKU},${row.SHIPQTY},${row.PACKAGEID}\n`; | |
| }); | |
| // Create download link | |
| const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); | |
| const url = URL.createObjectURL(blob); | |
| const link = document.createElement('a'); | |
| link.setAttribute('href', url); | |
| link.setAttribute('download', `transformed_data_${new Date().toISOString().slice(0, 10)}.csv`); | |
| link.style.visibility = 'hidden'; | |
| document.body.appendChild(link); | |
| link.click(); | |
| document.body.removeChild(link); | |
| } |