Design a fully working, production-grade inline Context Selection feature inside the existing Superfuel Workflow page (dark theme). The page must match the screenshot exactly: left sidebar, a header with the workflow title “Add Competitor Keywords to the Bullet Points,” and a numbered vertical list of workflow steps. Add a fully functional Select Context button on Step 1 (“Get ASIN from Context”) that expands an inline Context Selection Panel beneath Step 1 (not a separate page or full-screen drawer). The panel must support three tabs: Manual Input, Search & Filters, Saved Contexts. Use the Superfuel x shadcn style: Inter font, 8px grid, dark palette (background #0B0C0F, panel #131417, borders #2B2C2F, text #E6E6E6, secondary text #A0A3A8, accent #2563EB), 8px radii, subtle shadows, and 150ms ease motion. All copy must match the tone: concise, factual, and technical.
9839720
verified
| document.addEventListener('DOMContentLoaded', function() { | |
| // Mock product data | |
| const mockProducts = [ | |
| {"asin":"B08PP5MSVB","title":"Runner Pro Shoe - Blue","image":"https://via.placeholder.com/64","metrics":{"sales_30":1423,"cvr_30":0.18,"clicks_30":921}}, | |
| {"asin":"B09XYZ1234","title":"Trail Grip Shoe - Black","image":"https://via.placeholder.com/64","metrics":{"sales_30":543,"cvr_30":0.12,"clicks_30":320}}, | |
| {"asin":"B07ABC9876","title":"Daily Comfort Sneaker","image":"https://via.placeholder.com/64","metrics":{"sales_30":12,"cvr_30":0.02,"clicks_30":4}}, | |
| {"asin":"B0A1B2C3D4","title":"Performance Running Shorts","image":"https://via.placeholder.com/64","metrics":{"sales_30":876,"cvr_30":0.15,"clicks_30":421}}, | |
| {"asin":"B0D5E6F7G8","title":"Athletic Compression Socks","image":"https://via.placeholder.com/64","metrics":{"sales_30":321,"cvr_30":0.08,"clicks_30":154}}, | |
| {"asin":"B0H9I8J7K6","title":"Yoga Mat - Extra Thick","image":"https://via.placeholder.com/64","metrics":{"sales_30":654,"cvr_30":0.22,"clicks_30":298}}, | |
| {"asin":"B0L1M2N3O4","title":"Adjustable Dumbbell Set","image":"https://via.placeholder.com/64","metrics":{"sales_30":432,"cvr_30":0.14,"clicks_30":187}}, | |
| {"asin":"B0P5Q6R7S8","title":"Foam Roller - High Density","image":"https://via.placeholder.com/64","metrics":{"sales_30":189,"cvr_30":0.07,"clicks_30":92}}, | |
| {"asin":"B0T9U8V7W6","title":"Resistance Bands Set","image":"https://via.placeholder.com/64","metrics":{"sales_30":567,"cvr_30":0.19,"clicks_30":301}}, | |
| {"asin":"B0X1Y2Z3A4","title":"Jump Rope - Weighted","image":"https://via.placeholder.com/64","metrics":{"sales_30":234,"cvr_30":0.11,"clicks_30":123}} | |
| ]; | |
| // DOM Elements | |
| const selectContextBtn = document.getElementById('selectContextBtn'); | |
| const contextPanel = document.getElementById('contextPanel'); | |
| const parseBtn = document.getElementById('parseBtn'); | |
| const asinInput = document.getElementById('asinInput'); | |
| const previewSection = document.getElementById('previewSection'); | |
| const previewGrid = document.getElementById('previewGrid'); | |
| const totalValidCount = document.getElementById('totalValidCount'); | |
| const selectAllBtn = document.getElementById('selectAllBtn'); | |
| const deselectAllBtn = document.getElementById('deselectAllBtn'); | |
| const selectedCountText = document.getElementById('selectedCountText'); | |
| const applySelectionBtn = document.getElementById('applySelectionBtn'); | |
| const saveContextCheckbox = document.getElementById('saveContextCheckbox'); | |
| const contextNameInput = document.getElementById('contextNameInput'); | |
| const tabButtons = document.querySelectorAll('.tab-btn'); | |
| const tabContents = document.querySelectorAll('.tab-content'); | |
| const toast = document.getElementById('toast'); | |
| const confirmModal = document.getElementById('confirmModal'); | |
| const modalCount = document.getElementById('modalCount'); | |
| const confirmInput = document.getElementById('confirmInput'); | |
| const cancelConfirmBtn = document.getElementById('cancelConfirmBtn'); | |
| const proceedConfirmBtn = document.getElementById('proceedConfirmBtn'); | |
| // State | |
| let selectedProducts = []; | |
| let currentTab = 'manual'; | |
| // Event Listeners | |
| selectContextBtn.addEventListener('click', toggleContextPanel); | |
| parseBtn.addEventListener('click', parseAsins); | |
| selectAllBtn.addEventListener('click', selectAllProducts); | |
| deselectAllBtn.addEventListener('click', deselectAllProducts); | |
| applySelectionBtn.addEventListener('click', applySelection); | |
| saveContextCheckbox.addEventListener('change', toggleSaveContextInput); | |
| tabButtons.forEach(button => { | |
| button.addEventListener('click', () => switchTab(button.dataset.tab)); | |
| }); | |
| confirmInput.addEventListener('input', checkConfirmation); | |
| cancelConfirmBtn.addEventListener('click', closeConfirmModal); | |
| proceedConfirmBtn.addEventListener('click', confirmAndApply); | |
| // Functions | |
| function toggleContextPanel() { | |
| contextPanel.classList.toggle('hidden'); | |
| if (!contextPanel.classList.contains('hidden')) { | |
| // Focus first input when panel opens | |
| if (currentTab === 'manual') { | |
| asinInput.focus(); | |
| } | |
| } | |
| } | |
| function switchTab(tabName) { | |
| currentTab = tabName; | |
| // Update tab button states | |
| tabButtons.forEach(button => { | |
| if (button.dataset.tab === tabName) { | |
| button.classList.add('active-tab'); | |
| } else { | |
| button.classList.remove('active-tab'); | |
| } | |
| }); | |
| // Show/hide tab contents | |
| tabContents.forEach(content => { | |
| if (content.id === `${tabName}Tab`) { | |
| content.classList.remove('hidden'); | |
| } else { | |
| content.classList.add('hidden'); | |
| } | |
| }); | |
| } | |
| function parseAsins() { | |
| const asinsText = asinInput.value.trim(); | |
| if (!asinsText) { | |
| return; | |
| } | |
| // Simulate API call | |
| setTimeout(() => { | |
| // Validate ASINs (mock) | |
| const validProducts = mockProducts.slice(0, 10); // Just show first 10 for demo | |
| const totalCount = 142; // Mock total count | |
| // Update UI | |
| renderProductPreview(validProducts); | |
| totalValidCount.textContent = totalCount; | |
| previewSection.classList.remove('hidden'); | |
| // Update selected count | |
| updateSelectedCount(); | |
| }, 300); | |
| } | |
| function renderProductPreview(products) { | |
| previewGrid.innerHTML = ''; | |
| products.forEach(product => { | |
| const productCard = document.createElement('div'); | |
| productCard.className = 'product-card bg-[#1E1F23] rounded-md p-3 cursor-pointer'; | |
| productCard.dataset.asin = product.asin; | |
| productCard.innerHTML = ` | |
| <div class="flex"> | |
| <div class="flex-shrink-0 mr-3"> | |
| <img src="${product.image}" alt="${product.title}" class="w-16 h-16 object-cover rounded"> | |
| </div> | |
| <div class="flex-1 min-w-0"> | |
| <h4 class="text-sm font-medium truncate-2-lines" title="${product.title}">${product.title}</h4> | |
| <div class="flex items-center mt-1"> | |
| <span class="bg-[#131417] text-xs px-1.5 py-0.5 rounded font-mono">${product.asin}</span> | |
| </div> | |
| <div class="flex flex-wrap gap-1.5 mt-2"> | |
| <span class="bg-[#1A1C21] text-xs px-1.5 py-0.5 rounded">Sales: ${product.metrics.sales_30}</span> | |
| <span class="bg-[#1A1C21] text-xs px-1.5 py-0.5 rounded">CVR: ${(product.metrics.cvr_30 * 100).toFixed(1)}%</span> | |
| <span class="bg-[#1A1C21] text-xs px-1.5 py-0.5 rounded">Clicks: ${product.metrics.clicks_30}</span> | |
| </div> | |
| </div> | |
| <div class="ml-2 flex-shrink-0"> | |
| <input type="checkbox" class="product-checkbox" data-asin="${product.asin}"> | |
| </div> | |
| </div> | |
| `; | |
| // Add event listeners | |
| const checkbox = productCard.querySelector('.product-checkbox'); | |
| checkbox.addEventListener('change', () => toggleProductSelection(product.asin, checkbox.checked)); | |
| productCard.addEventListener('click', (e) => { | |
| if (e.target.tagName !== 'INPUT') { | |
| checkbox.checked = !checkbox.checked; | |
| toggleProductSelection(product.asin, checkbox.checked); | |
| } | |
| }); | |
| previewGrid.appendChild(productCard); | |
| }); | |
| } | |
| function toggleProductSelection(asin, isSelected) { | |
| if (isSelected && !selectedProducts.includes(asin)) { | |
| selectedProducts.push(asin); | |
| } else if (!isSelected) { | |
| selectedProducts = selectedProducts.filter(a => a !== asin); | |
| } | |
| // Update UI | |
| updateSelectedCount(); | |
| highlightSelectedCards(); | |
| } | |
| function selectAllProducts() { | |
| const checkboxes = document.querySelectorAll('.product-checkbox'); | |
| checkboxes.forEach(checkbox => { | |
| checkbox.checked = true; | |
| if (!selectedProducts.includes(checkbox.dataset.asin)) { | |
| selectedProducts.push(checkbox.dataset.asin); | |
| } | |
| }); | |
| updateSelectedCount(); | |
| highlightSelectedCards(); | |
| } | |
| function deselectAllProducts() { | |
| const checkboxes = document.querySelectorAll('.product-checkbox'); | |
| checkboxes.forEach(checkbox => { | |
| checkbox.checked = false; | |
| }); | |
| selectedProducts = []; | |
| updateSelectedCount(); | |
| highlightSelectedCards(); | |
| } | |
| function highlightSelectedCards() { | |
| const productCards = document.querySelectorAll('.product-card'); | |
| productCards.forEach(card => { | |
| if (selectedProducts.includes(card.dataset.asin)) { | |
| card.classList.add('selected'); | |
| } else { | |
| card.classList.remove('selected'); | |
| } | |
| }); | |
| } | |
| function updateSelectedCount() { | |
| const count = selectedProducts.length; | |
| selectedCountText.textContent = `Selected: ${count} product${count !== 1 ? 's' : ''}`; | |
| // Show warning if count is large | |
| if (count > 500) { | |
| selectedCountText.innerHTML += ` <span class="text-yellow-400">(Large selection)</span>`; | |
| } | |
| } | |
| function toggleSaveContextInput() { | |
| if (saveContextCheckbox.checked) { | |
| contextNameInput.classList.remove('hidden'); | |
| contextNameInput.focus(); | |
| } else { | |
| contextNameInput.classList.add('hidden'); | |
| } | |
| } | |
| function applySelection() { | |
| const count = selectedProducts.length; | |
| if (count === 0) { | |
| showToast('Please select at least one product'); | |
| return; | |
| } | |
| if (count > 5000) { | |
| showConfirmModal(count); | |
| return; | |
| } | |
| if (count > 500) { | |
| // Soft warning - just proceed | |
| proceedWithApply(); | |
| } else { | |
| proceedWithApply(); | |
| } | |
| } | |
| function showConfirmModal(count) { | |
| modalCount.textContent = count.toLocaleString(); | |
| confirmModal.classList.remove('hidden'); | |
| confirmInput.value = ''; | |
| proceedConfirmBtn.disabled = true; | |
| } | |
| function closeConfirmModal() { | |
| confirmModal.classList.add('hidden'); | |
| } | |
| function checkConfirmation() { | |
| proceedConfirmBtn.disabled = confirmInput.value.trim().toUpperCase() !== 'CONFIRM'; | |
| } | |
| function confirmAndApply() { | |
| closeConfirmModal(); | |
| proceedWithApply(); | |
| } | |
| function proceedWithApply() { | |
| const contextName = saveContextCheckbox.checked ? contextNameInput.value.trim() : null; | |
| // Simulate API call | |
| setTimeout(() => { | |
| // Update step 1 subtitle | |
| const subtitle = document.querySelector('#contextPanel + div p'); | |
| subtitle.textContent = `Context: ${selectedProducts.length} product${selectedProducts.length !== 1 ? 's' : ''} selected`; | |
| if (contextName) { | |
| subtitle.textContent += ` (Saved as "${contextName}")`; | |
| } | |
| // Close panel | |
| contextPanel.classList.add('hidden'); | |
| // Show success toast | |
| showToast('Context applied successfully'); | |
| // Reset selection for next time | |
| selectedProducts = []; | |
| asinInput.value = ''; | |
| previewSection.classList.add('hidden'); | |
| saveContextCheckbox.checked = false; | |
| contextNameInput.classList.add('hidden'); | |
| contextNameInput.value = ''; | |
| }, 500); | |
| } | |
| function showToast(message) { | |
| const toastMessage = document.getElementById('toastMessage'); | |
| toastMessage.textContent = message; | |
| toast.classList.remove('hidden'); | |
| setTimeout(() => { | |
| toast.classList.add('hidden'); | |
| }, 3000); | |
| } | |
| // Initialize | |
| switchTab('manual'); | |
| }); |