loreal_ecf/ATF_premium.html

597 lines
No EOL
57 KiB
HTML
Executable file

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Amazon PDP Premium Mockup Generator</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f8f9fa; color: #333; line-height: 1.6; }
.container { max-width: 1600px; margin: 0 auto; padding: 20px; }
.header { text-align: center; margin-bottom: 30px; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.header h1 { color: #232f3e; margin-bottom: 10px; }
.header p { color: #666; font-size: 16px; }
.editor-section { display: grid; grid-template-columns: 1fr 2fr; gap: 20px; margin-bottom: 30px; }
.editor-panel { background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.editor-panel h3 { margin-bottom: 15px; color: #232f3e; }
.upload-section { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 30px; }
.upload-box { background: white; border: 2px dashed #ddd; border-radius: 8px; padding: 30px; text-align: center; transition: all 0.3s ease; cursor: pointer; }
.upload-box:hover { border-color: #ff9900; background: #fff8f0; }
.upload-box.dragover { border-color: #ff9900; background: #fff8f0; transform: scale(1.02); }
.upload-icon { font-size: 48px; margin-bottom: 15px; color: #666; }
.upload-text { font-size: 18px; font-weight: 600; margin-bottom: 10px; color: #333; }
.upload-subtext { color: #666; font-size: 14px; }
.file-input { display: none; }
.preview-section { display: grid; grid-template-columns: 1fr 2fr; gap: 20px; margin-bottom: 30px; }
.image-preview, .csv-preview { background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.image-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 10px; }
.image-thumb { width: 80px; height: 80px; object-fit: cover; border-radius: 4px; border: 2px solid #ddd; cursor: pointer; transition: all 0.3s ease; }
.image-thumb:hover { border-color: #ff9900; transform: scale(1.05); }
.image-thumb.selected { border-color: #ff9900; box-shadow: 0 0 0 2px rgba(255, 153, 0, 0.3); }
.csv-data { background: #f8f9fa; border-radius: 4px; padding: 15px; font-family: monospace; font-size: 14px; max-height: 200px; overflow-y: auto; }
.mockup-container { background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 20px; }
.mockup-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; gap: 10px; flex-wrap: wrap; }
.export-btn { background: #ff9900; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-weight: 600; transition: background 0.3s ease; }
.export-btn:disabled { background: #ccc; cursor: not-allowed; }
.amazon-mockup { background: white; border: 1px solid #ddd; border-radius: 4px; padding: 20px; font-family: Arial, sans-serif; max-width: 100%; }
.amazon-header { background: #232f3e; color: white; padding: 10px 20px; margin: -20px -20px 20px -20px; border-radius: 4px 4px 0 0; }
.product-section { display: grid; grid-template-columns: 420px 1fr; gap: 30px; margin-bottom: 30px; }
.hero-image { width: 100%; max-width: 400px; height: 400px; object-fit: cover; border: 1px solid #ddd; border-radius: 4px; background: #f8f9fa; display: flex; align-items: center; justify-content: center; color: #666; font-size: 14px; cursor: pointer; }
.thumbnail-images { display: flex; gap: 5px; flex-wrap: wrap; }
.thumbnail { width: 60px; height: 60px; object-fit: cover; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; }
.brand-name { color: #0066c0; font-size: 14px; margin-bottom: 5px; }
.product-title { font-size: 20px; font-weight: 400; line-height: 1.3; margin-bottom: 8px; color: #0f1111; }
.subtitle { font-size: 14px; color: #565959; margin-bottom: 12px; }
.bullets { margin-bottom: 20px; }
.bullet { margin-bottom: 8px; font-size: 14px; line-height: 1.4; }
.bullet::before { content: "• "; color: #ff9900; font-weight: bold; }
.description { font-size: 14px; line-height: 1.5; }
.instructions { margin-top: 10px; color: #565959; font-size: 13px; line-height: 1.45; }
.aplus-content { margin-top: 30px; border-top: 1px solid #ddd; padding-top: 20px; }
.aplus-images { display: grid; gap: 15px; }
.aplus-images.standard { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.aplus-images.large { grid-template-columns: repeat(3, 1fr); }
.aplus-image { width: 100%; aspect-ratio: 1/1; height: auto; border: 1px solid #ddd; border-radius: 4px; background: #ffffff; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; }
.aplus-image img { width: 100%; height: 100%; object-fit: contain; background: #ffffff; }
.order-badge { position: absolute; top: 6px; left: 6px; background: #232f3e; color: #fff; font-size: 12px; padding: 2px 6px; border-radius: 12px; font-weight: 600; }
.premium-section { margin-top: 30px; padding-top: 30px; border-top: 2px solid #ddd; }
.brand-story { margin-bottom: 30px; }
.brand-story-hero { width: 100%; height: 250px; background: #f0f0f0; border-radius: 8px; margin-bottom: 15px; display: flex; align-items: center; justify-content: center; color: #666; font-size: 16px; }
.brand-story-text { font-size: 15px; line-height: 1.6; color: #333; }
.comparison-table { width: 100%; border-collapse: collapse; margin-bottom: 30px; }
.comparison-table th, .comparison-table td { border: 1px solid #ddd; padding: 10px; text-align: left; font-size: 14px; }
.comparison-table th { background: #f8f9fa; font-weight: 600; }
.faq-section { margin-bottom: 30px; }
.faq-item { margin-bottom: 15px; padding: 15px; background: #f8f9fa; border-radius: 8px; }
.faq-question { font-weight: 600; font-size: 15px; margin-bottom: 8px; color: #232f3e; }
.faq-answer { font-size: 14px; line-height: 1.5; color: #333; }
.video-section { margin-bottom: 30px; }
.video-placeholder { width: 100%; height: 300px; background: #f0f0f0; border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #666; font-size: 16px; }
.features-section { margin-bottom: 30px; }
.feature-item { margin-bottom: 15px; padding: 15px; background: #f8f9fa; border-radius: 8px; display: flex; gap: 12px; align-items: flex-start; }
.feature-icon { width: 50px; height: 50px; background: #e0e0e0; border-radius: 8px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: #666; font-size: 20px; }
.feature-content { flex: 1; }
.feature-headline { font-weight: 600; font-size: 15px; margin-bottom: 6px; color: #232f3e; }
.feature-text { font-size: 14px; line-height: 1.4; color: #333; }
.ingredients-section { margin-bottom: 30px; }
.ingredients-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; }
.ingredient-item { padding: 15px; background: #f8f9fa; border-radius: 8px; text-align: center; }
.ingredient-name { font-weight: 600; font-size: 15px; margin-bottom: 6px; color: #232f3e; }
.ingredient-desc { font-size: 14px; line-height: 1.4; color: #333; }
.module-toggle { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.module-toggle input[type="checkbox"] { width: 18px; height: 18px; }
.module-toggle label { font-weight: 600; color: #232f3e; cursor: pointer; }
.module-content { display: none; }
.module-content.visible { display: block; }
.module-input { margin-bottom: 10px; }
.module-input textarea { width: 100%; min-height: 80px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-family: inherit; font-size: 14px; resize: vertical; }
.module-input input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-family: inherit; font-size: 14px; }
.module-input select { width: 100%; max-width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; }
.toolbar select { max-width: 230px; }
.comparison-editor { max-height: 300px; overflow-y: auto; }
.comparison-row { display: grid; grid-template-columns: 200px repeat(3, 1fr); gap: 8px; margin-bottom: 8px; align-items: center; }
.comparison-row input { padding: 6px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; }
.comparison-row:first-child input { font-weight: 600; background: #f8f9fa; }
.faq-editor { max-height: 400px; overflow-y: auto; }
.faq-pair { margin-bottom: 15px; padding: 12px; background: #f8f9fa; border-radius: 6px; }
.faq-pair input { width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 6px; font-size: 13px; }
.faq-pair textarea { width: 100%; min-height: 60px; padding: 6px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; resize: vertical; }
.features-editor { max-height: 400px; overflow-y: auto; }
.feature-edit-item { margin-bottom: 15px; padding: 12px; background: #f8f9fa; border-radius: 6px; display: flex; gap: 8px; align-items: flex-start; }
.feature-edit-item input[type="text"] { padding: 6px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; }
.feature-edit-item textarea { padding: 6px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; resize: vertical; min-height: 60px; }
.ingredients-editor { max-height: 400px; overflow-y: auto; }
.ingredient-edit-item { margin-bottom: 15px; padding: 12px; background: #f8f9fa; border-radius: 6px; display: flex; gap: 8px; align-items: flex-start; }
.ingredient-edit-item input[type="text"] { padding: 6px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; flex: 1; }
.ingredient-edit-item textarea { padding: 6px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; resize: vertical; min-height: 60px; flex: 2; }
.add-btn { background: #ff9900; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 13px; margin-top: 10px; }
.toolbar { display:flex; gap:10px; align-items:center; flex-wrap: wrap; }
@media (max-width: 768px) { .upload-section, .preview-section, .product-section, .editor-section { grid-template-columns: 1fr; } .container { padding: 10px; } }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Amazon PDP Premium Mockup Generator</h1>
<p>Upload assets, edit content, and generate professional mockups</p>
</div>
<!-- Uploaders moved to top -->
<div class="upload-section">
<div class="upload-box" id="imageUpload">
<div class="upload-icon">📸</div>
<div class="upload-text">Drop Product Images Here</div>
<div class="upload-subtext">or click to browse files</div>
<input type="file" class="file-input" id="imageInput" multiple accept="image/jpeg,image/jpg,image/png">
</div>
<div class="upload-box" id="csvUpload">
<div class="upload-icon">📊</div>
<div class="upload-text">Upload CSV Data</div>
<div class="upload-subtext">or click to browse file</div>
<input type="file" class="file-input" id="csvInput" accept=".csv">
</div>
</div>
<!-- Preview section moved here -->
<div class="preview-section">
<div class="image-preview">
<h3>Uploaded Images</h3>
<div class="image-grid" id="imageGrid"><div class="empty-state">No images uploaded yet</div></div>
</div>
<div class="csv-preview">
<h3>CSV Data Preview</h3>
<div class="csv-data" id="csvData"><div class="empty-state">No CSV data loaded yet</div></div>
</div>
</div>
<div class="editor-section">
<div class="editor-panel">
<h3>Premium Modules</h3>
<div class="module-toggle">
<input type="checkbox" id="brandStoryToggle">
<label for="brandStoryToggle">Brand Story</label>
<button id="applyBrandStoryBtn" class="add-btn" style="margin-left:auto;">Add to page</button>
</div>
<div class="module-content" id="brandStoryContent">
<div class="module-input">
<label>Brand Story Text</label>
<textarea id="brandStoryText" placeholder="Enter brand story narrative..."></textarea>
</div>
<div class="module-input">
<label>Hero Image (select from uploaded assets)</label>
<select id="brandStoryHeroSelect">
<option value="">None</option>
</select>
</div>
</div>
<div class="module-toggle">
<input type="checkbox" id="comparisonToggle">
<label for="comparisonToggle">Comparison Table</label>
<button id="applyComparisonBtn" class="add-btn" style="margin-left:auto;">Add to page</button>
</div>
<div class="module-content" id="comparisonContent">
<div class="comparison-editor" id="comparisonEditor">
<div class="comparison-row">
<input type="text" placeholder="Attribute">
<input type="text" placeholder="Product A">
<input type="text" placeholder="Product B">
<input type="text" placeholder="Product C">
</div>
<button id="addComparisonRow" class="add-btn">Add Row</button>
</div>
</div>
<div class="module-toggle">
<input type="checkbox" id="faqToggle">
<label for="faqToggle">FAQ</label>
<button id="applyFaqBtn" class="add-btn" style="margin-left:auto;">Add to page</button>
</div>
<div class="module-content" id="faqContent">
<div class="faq-editor" id="faqEditor">
<div class="faq-pair">
<input type="text" placeholder="Question">
<textarea placeholder="Answer"></textarea>
</div>
<button id="addFaqPair" class="add-btn">Add Q&A</button>
</div>
</div>
<div class="module-toggle">
<input type="checkbox" id="videoToggle">
<label for="videoToggle">Video</label>
<button id="applyVideoBtn" class="add-btn" style="margin-left:auto;">Add to page</button>
</div>
<div class="module-content" id="videoContent">
<div class="module-input">
<label>Video URL</label>
<input type="url" id="videoUrl" placeholder="https://example.com/video.mp4">
</div>
<div class="module-input">
<label>Video Thumbnail (select from uploaded assets)</label>
<select id="videoThumbnailSelect">
<option value="">None</option>
</select>
</div>
</div>
<div class="module-toggle">
<input type="checkbox" id="featuresToggle">
<label for="featuresToggle">Feature Callouts</label>
<button id="applyFeaturesBtn" class="add-btn" style="margin-left:auto;">Add to page</button>
</div>
<div class="module-content" id="featuresContent">
<div class="features-editor" id="featuresEditor">
<div class="feature-edit-item">
<input type="text" placeholder="Icon" style="width:60px;">
<input type="text" placeholder="Headline" style="flex:1;">
<textarea placeholder="Description" style="flex:2;"></textarea>
</div>
<button id="addFeatureItem" class="add-btn">Add Feature</button>
</div>
</div>
<div class="module-toggle">
<input type="checkbox" id="ingredientsToggle">
<label for="ingredientsToggle">Ingredients</label>
<button id="applyIngredientsBtn" class="add-btn" style="margin-left:auto;">Add to page</button>
</div>
<div class="module-content" id="ingredientsContent">
<div class="ingredients-editor" id="ingredientsEditor">
<div class="ingredient-edit-item">
<input type="text" placeholder="Ingredient Name" style="flex:1;">
<textarea placeholder="Description" style="flex:2;"></textarea>
</div>
<button id="addIngredientItem" class="add-btn">Add Ingredient</button>
</div>
</div>
</div>
<div class="mockup-container">
<div class="mockup-header">
<h3>Amazon Product Page Mockup</h3>
<div class="toolbar">
<label style="display:flex; align-items:center; gap:6px; font-size:14px; color:#232f3e;"><span>EAN</span><input id="eanInput" type="text" inputmode="numeric" pattern="\d{13}" maxlength="13" placeholder="13-digit EAN" style="height:30px; padding:4px 8px; border:1px solid #ddd; border-radius:4px; min-width:180px;" /></label>
<label style="display:flex; align-items:center; gap:6px; font-size:14px; color:#232f3e;"><input type="checkbox" id="pinHeroToggle"> Pin hero</label>
<label style="display:flex; align-items:center; gap:6px; font-size:14px; color:#232f3e;"><span>A+ layout</span><select id="aplusLayoutSelect" style="height:30px; padding:4px 8px; border:1px solid #ddd; border-radius:4px;"><option value="standard" selected>Standard (auto-fit)</option><option value="large">Large 3-up</option></select></label>
<button class="export-btn" id="downloadCsvBtn">Download CSV</button>
<button class="export-btn" id="downloadAssetsBtn" disabled>Download assets (ZIP)</button>
<button class="export-btn" id="exportBtn" disabled>Export as JPG</button>
</div>
</div>
<div class="amazon-mockup" id="amazonMockup">
<div class="amazon-header"><h2>Amazon.com</h2></div>
<div class="product-section">
<div class="product-images">
<div class="hero-image" id="heroImage">Drop your main product image here</div>
<div class="thumbnail-images" id="thumbnailImages"></div>
</div>
<div class="product-info">
<div class="brand-name" id="brandName">Brand Name</div>
<div class="product-title" id="productTitle">Product Title</div>
<div class="subtitle" id="subtitle" style="display:none;"></div>
<div class="bullets" id="bullets"></div>
<div class="description" id="description">Product description will appear here...</div>
<div class="instructions" id="instructions" style="display:none;"></div>
</div>
</div>
<div class="aplus-content">
<div class="aplus-images standard" id="aplusImages"></div>
</div>
<div class="premium-section" id="premiumSection">
<div class="brand-story" id="brandStory" style="display:none;">
<h4 style="margin-bottom:15px; color:#232f3e;">Brand Story</h4>
<button id="removeBrandStoryBtn" class="add-btn" style="position:absolute; top:10px; right:10px;">Remove</button>
<div class="brand-story-hero" id="brandStoryHero">Brand story hero image</div>
<div class="brand-story-text" id="brandStoryTextEl"></div>
</div>
<div id="comparisonTableContainer" style="display:none;">
<h4 style="margin-bottom:15px; color:#232f3e;">Compare with similar items</h4>
<button id="removeComparisonBtn" class="add-btn" style="position:absolute; top:10px; right:10px;">Remove</button>
<table class="comparison-table" id="comparisonTable"></table>
</div>
<div id="faqContainer" style="display:none;">
<h4 style="margin-bottom:15px; color:#232f3e;">Frequently Asked Questions</h4>
<button id="removeFaqBtn" class="add-btn" style="position:absolute; top:10px; right:10px;">Remove</button>
<div class="faq-section" id="faqSection"></div>
</div>
<div id="videoContainer" style="display:none;">
<h4 style="margin-bottom:15px; color:#232f3e;">Product Video</h4>
<button id="removeVideoBtn" class="add-btn" style="position:absolute; top:10px; right:10px;">Remove</button>
<div class="video-section" id="videoSection">
<div class="video-placeholder" id="videoPlaceholder">Video content will appear here</div>
</div>
</div>
<div id="featuresContainer" style="display:none;">
<h4 style="margin-bottom:15px; color:#232f3e;">Key Features</h4>
<button id="removeFeaturesBtn" class="add-btn" style="position:absolute; top:10px; right:10px;">Remove</button>
<div class="features-section" id="featuresSection"></div>
</div>
<div id="ingredientsContainer" style="display:none;">
<h4 style="margin-bottom:15px; color:#232f3e;">Key Ingredients & Technology</h4>
<button id="removeIngredientsBtn" class="add-btn" style="position:absolute; top:10px; right:10px;">Remove</button>
<div class="ingredients-section" id="ingredientsSection">
<div class="ingredients-grid" id="ingredientsGrid"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="statusMessage"></div>
</div>
<script>
class AmazonMockupGenerator {
constructor() {
this.uploadedImages = []; this.csvData = {}; this.selectedHeroImage = null; this.pinHero = false; this.detectedEAN = '';
this.brandStoryEnabled = false; this.comparisonEnabled = false; this.faqEnabled = false;
this.videoEnabled = false; this.featuresEnabled = false; this.ingredientsEnabled = false;
this.initializeEventListeners();
}
initializeEventListeners() {
const imageUpload = document.getElementById('imageUpload'), imageInput = document.getElementById('imageInput');
imageUpload.addEventListener('click', () => imageInput.click());
imageUpload.addEventListener('dragover', this.handleDragOver.bind(this));
imageUpload.addEventListener('dragleave', this.handleDragLeave.bind(this));
imageUpload.addEventListener('drop', this.handleImageDrop.bind(this));
imageInput.addEventListener('change', this.handleImageInput.bind(this));
const csvUpload = document.getElementById('csvUpload'), csvInput = document.getElementById('csvInput');
csvUpload.addEventListener('click', () => csvInput.click());
csvUpload.addEventListener('dragover', this.handleDragOver.bind(this));
csvUpload.addEventListener('dragleave', this.handleDragLeave.bind(this));
csvUpload.addEventListener('drop', this.handleCsvDrop.bind(this));
csvInput.addEventListener('change', this.handleCsvInput.bind(this));
document.getElementById('exportBtn').addEventListener('click', this.exportMockup.bind(this));
document.getElementById('downloadAssetsBtn').addEventListener('click', this.downloadAssetsZip.bind(this));
document.getElementById('pinHeroToggle').addEventListener('change', (e) => { this.pinHero = e.target.checked; });
document.getElementById('eanInput').addEventListener('input', (e) => { let d = (e.target.value || '').replace(/\D/g, ''); if (d.length > 13) d = d.slice(0, 13); const p = d.padStart(13, '0'); this.detectedEAN = p; e.target.value = p; });
document.getElementById('aplusLayoutSelect').addEventListener('change', (e) => { this.aplusLayout = e.target.value; this.updateMockup(); });
// default to standard layout explicitly
this.aplusLayout = 'standard';
document.getElementById('aplusLayoutSelect').value = 'standard';
// Download CSV button
document.getElementById('downloadCsvBtn').addEventListener('click', () => this.downloadCsv());
document.getElementById('heroImage').onclick = () => document.getElementById('imageInput').click();
document.getElementById('brandStoryToggle').addEventListener('change', (e) => { this.brandStoryEnabled = e.target.checked; this.updateTrayVisibility(); this.updatePremiumModules(); });
document.getElementById('comparisonToggle').addEventListener('change', (e) => { this.comparisonEnabled = e.target.checked; this.updateTrayVisibility(); this.updatePremiumModules(); });
document.getElementById('faqToggle').addEventListener('change', (e) => { this.faqEnabled = e.target.checked; this.updateTrayVisibility(); this.updatePremiumModules(); });
document.getElementById('videoToggle').addEventListener('change', (e) => { this.videoEnabled = e.target.checked; this.updateTrayVisibility(); this.updatePremiumModules(); });
document.getElementById('featuresToggle').addEventListener('change', (e) => { this.featuresEnabled = e.target.checked; this.updateTrayVisibility(); this.updatePremiumModules(); });
document.getElementById('ingredientsToggle').addEventListener('change', (e) => { this.ingredientsEnabled = e.target.checked; this.updateTrayVisibility(); this.updatePremiumModules(); });
document.getElementById('brandStoryText').addEventListener('input', () => this.updatePremiumModules());
document.getElementById('applyBrandStoryBtn').addEventListener('click', () => { this.brandStoryEnabled = true; this.updateTrayVisibility(); this.updatePremiumModules(); this.showStatus('Brand Story added to page','success'); });
document.getElementById('applyComparisonBtn').addEventListener('click', () => { this.comparisonEnabled = true; this.updateTrayVisibility(); this.updatePremiumModules(); this.showStatus('Comparison Table added to page','success'); });
document.getElementById('applyFaqBtn').addEventListener('click', () => { this.faqEnabled = true; this.updateTrayVisibility(); this.updatePremiumModules(); this.showStatus('FAQ added to page','success'); });
document.getElementById('applyVideoBtn').addEventListener('click', () => { this.videoEnabled = true; this.updateTrayVisibility(); this.updatePremiumModules(); this.showStatus('Video added to page','success'); });
document.getElementById('applyFeaturesBtn').addEventListener('click', () => { this.featuresEnabled = true; this.updateTrayVisibility(); this.updatePremiumModules(); this.showStatus('Feature Callouts added to page','success'); });
document.getElementById('applyIngredientsBtn').addEventListener('click', () => { this.ingredientsEnabled = true; this.updateTrayVisibility(); this.updatePremiumModules(); this.showStatus('Ingredients added to page','success'); });
document.getElementById('brandStoryHeroSelect').addEventListener('change', (e) => { this.updatePremiumModules(); });
document.getElementById('videoUrl').addEventListener('input', () => this.updatePremiumModules());
document.getElementById('videoThumbnailSelect').addEventListener('change', (e) => { this.updatePremiumModules(); });
document.getElementById('addComparisonRow').addEventListener('click', () => this.addComparisonRow());
document.getElementById('addFaqPair').addEventListener('click', () => this.addFaqPair());
document.getElementById('addFeatureItem').addEventListener('click', () => this.addFeatureItem());
document.getElementById('addIngredientItem').addEventListener('click', () => this.addIngredientItem());
document.getElementById('comparisonEditor').addEventListener('input', () => this.updatePremiumModules());
document.getElementById('faqEditor').addEventListener('input', () => this.updatePremiumModules());
document.getElementById('featuresEditor').addEventListener('input', () => this.updatePremiumModules());
document.getElementById('ingredientsEditor').addEventListener('input', () => this.updatePremiumModules());
// Remove buttons
document.getElementById('removeBrandStoryBtn').addEventListener('click', () => { this.brandStoryEnabled = false; this.updateTrayVisibility(); this.updatePremiumModules(); });
document.getElementById('removeComparisonBtn').addEventListener('click', () => { this.comparisonEnabled = false; this.updateTrayVisibility(); this.updatePremiumModules(); });
document.getElementById('removeFaqBtn').addEventListener('click', () => { this.faqEnabled = false; this.updateTrayVisibility(); this.updatePremiumModules(); });
document.getElementById('removeVideoBtn').addEventListener('click', () => { this.videoEnabled = false; this.updateTrayVisibility(); this.updatePremiumModules(); });
document.getElementById('removeFeaturesBtn').addEventListener('click', () => { this.featuresEnabled = false; this.updateTrayVisibility(); this.updatePremiumModules(); });
document.getElementById('removeIngredientsBtn').addEventListener('click', () => { this.ingredientsEnabled = false; this.updateTrayVisibility(); this.updatePremiumModules(); });
}
handleDragOver(e){ e.preventDefault(); e.currentTarget.classList.add('dragover'); }
handleDragLeave(e){ e.preventDefault(); e.currentTarget.classList.remove('dragover'); }
handleImageDrop(e){ e.preventDefault(); e.currentTarget.classList.remove('dragover'); const files = Array.from(e.dataTransfer.files).filter(f => ['image/jpeg','image/jpg','image/png'].includes(f.type)); this.processImages(files); }
handleImageInput(e){ const files = Array.from(e.target.files); this.processImages(files); }
handleCsvDrop(e){ e.preventDefault(); e.currentTarget.classList.remove('dragover'); const files = Array.from(e.dataTransfer.files).filter(f => f.name.toLowerCase().endsWith('.csv')); if (files.length) this.processCsv(files[0]); }
handleCsvInput(e){ if (e.target.files.length) this.processCsv(e.target.files[0]); }
processImages(files){ files.forEach(file => { const ok = ['image/jpeg','image/jpg','image/png'].includes(file.type); if (!ok) return; const reader = new FileReader(); reader.onload = (ev) => { const imageData = { name: file.name, data: ev.target.result, file }; this.uploadedImages.push(imageData); this.tryDetectEanFromName(file.name); if (this.selectedHeroImage === null && this.uploadedImages.length > 0) this.selectedHeroImage = 0; this.updateImagePreview(); this.updateMockup(); }; reader.readAsDataURL(file); }); this.showStatus(`Added ${files.length} image(s)`, 'success'); }
tryDetectEanFromName(filename){ const n = filename.split('/').pop().split('\\').pop(); const m = n.match(/(\d{13}|\d{8})/); if (m && !this.detectedEAN){ this.detectedEAN = m[1]; const inp = document.getElementById('eanInput'); if (inp && !inp.value) inp.value = this.detectedEAN; } }
processCsv(file){ const reader = new FileReader(); reader.onload = (e)=>{ try { const txt = e.target.result; this.csvData = this.parseCsv(txt); this.updateCsvPreview(); this.updateMockup(); this.showStatus('CSV data loaded successfully','success'); } catch(err){ this.showStatus('Error parsing CSV: '+err.message,'error'); } }; reader.readAsText(file); }
parseCsv(csvText){ const lines = csvText.split(/\r?\n/); const data = {}; for (const raw of lines){ const line = raw.trim(); if (!line) continue; const first = line.indexOf(','); if (first === -1) continue; const second = line.indexOf(',', first+1); if (second === -1) continue; const field = line.slice(0, first).trim(); let pos = second+1; let value=''; if (line[pos] === '"'){ pos++; let i=pos, end=-1; while(i<line.length){ if(line[i]==='"'){ if(line[i+1]==='"'){ i+=2; continue;} else { end=i; break; } } i++; } value = end!==-1? line.slice(pos,end): line.slice(pos); value = value.replace(/""/g,'"'); } else { const third = line.indexOf(',', pos); value = third===-1? line.slice(pos): line.slice(pos, third); } value = value.trim(); if (field) data[field]=value; } return data; }
updateImagePreview(){ const grid = document.getElementById('imageGrid'); if (!this.uploadedImages.length){ grid.innerHTML = '<div class="empty-state">No images uploaded yet</div>'; return; } grid.innerHTML=''; this.uploadedImages.forEach((img, idx)=>{ const el=document.createElement('img'); el.src=img.data; el.className='image-thumb'; el.title=img.name; el.addEventListener('click', ()=> this.promoteImageToHero(idx)); el.draggable=true; el.addEventListener('dragstart',(ev)=>this.handleThumbDragStart(ev,idx)); el.addEventListener('dragover',(ev)=>this.handleThumbDragOver(ev)); el.addEventListener('drop',(ev)=>this.handleThumbDrop(ev,idx)); if (this.selectedHeroImage===idx) el.classList.add('selected'); grid.appendChild(el); }); }
promoteImageToHero(index){ if (index>0 && index<this.uploadedImages.length){ if (!this.pinHero){ const [moved]=this.uploadedImages.splice(index,1); this.uploadedImages.unshift(moved); } } this.selectedHeroImage=0; this.updateImagePreview(); this.updateMockup(); }
handleThumbDragStart(ev,idx){ ev.dataTransfer.effectAllowed='move'; ev.dataTransfer.setData('text/plain', String(idx)); }
handleThumbDragOver(ev){ ev.preventDefault(); ev.dataTransfer.dropEffect='move'; }
handleThumbDrop(ev,targetIdx){ ev.preventDefault(); const s=ev.dataTransfer.getData('text/plain'); const src=parseInt(s,10); if (Number.isNaN(src)||src===targetIdx) return; const [moved]=this.uploadedImages.splice(src,1); this.uploadedImages.splice(targetIdx,0,moved); this.selectedHeroImage=0; this.updateImagePreview(); this.updateMockup(); }
updateCsvPreview(){ const box=document.getElementById('csvData'); if (!Object.keys(this.csvData).length){ box.innerHTML='<div class="empty-state">No CSV data loaded yet</div>'; return;} let html=''; Object.entries(this.csvData).forEach(([k,v])=>{ html += `<div><strong>${k}:</strong> ${v}</div>`; }); box.innerHTML=html; }
updateMockup(){
const brand = this.csvData['DMI_Product_type'] || this.csvData.brand_name; if (brand) document.getElementById('brandName').textContent = brand;
const title = this.csvData['Amazon - Product Name (Long)'] || this.csvData.product_title; if (title) document.getElementById('productTitle').textContent = title;
const bulletsBox = document.getElementById('bullets'); bulletsBox.innerHTML=''; const bulletFields = ['Amazon - Bullet Features 1','Amazon - Bullet Features 2','Amazon - Bullet Features 3','Amazon - Bullet Features 4','Amazon - Bullet Features 5']; let hasBullets=false; for (const key of bulletFields){ const val=this.csvData[key]; if (val){ const b=document.createElement('div'); b.className='bullet'; b.textContent=val; bulletsBox.appendChild(b); hasBullets=true; } } if (!hasBullets){ for (let i=1;i<=5;i++){ const k=`bullet_${i}`; if (this.csvData[k]){ const b=document.createElement('div'); b.className='bullet'; b.textContent=this.csvData[k]; bulletsBox.appendChild(b);} } }
const subtitleEl=document.getElementById('subtitle'); const benefits=this.csvData['Amazon Benefits']; if (benefits){ subtitleEl.style.display=''; subtitleEl.textContent=benefits; } else { subtitleEl.style.display='none'; subtitleEl.textContent=''; }
const desc=this.csvData['amazon_product_description_short'] || this.csvData.description; document.getElementById('description').textContent = desc || 'Product description will appear here...';
const instructionsEl=document.getElementById('instructions'); const iou=this.csvData['amazon_instruction_of_use']; if (iou){ instructionsEl.style.display=''; instructionsEl.textContent=iou; } else { instructionsEl.style.display='none'; instructionsEl.textContent=''; }
const heroBox=document.getElementById('heroImage'); if (this.selectedHeroImage!==null && this.uploadedImages[this.selectedHeroImage]){ heroBox.innerHTML=''; const img=document.createElement('img'); img.src=this.uploadedImages[this.selectedHeroImage].data; img.style.width='100%'; img.style.height='100%'; img.style.objectFit='cover'; heroBox.appendChild(img);} else { heroBox.innerHTML='Drop your main product image here'; }
const thumbs=document.getElementById('thumbnailImages'); thumbs.innerHTML=''; this.uploadedImages.forEach((image, index)=>{ if (index===0) return; const t=document.createElement('img'); t.src=image.data; t.className='thumbnail'; t.addEventListener('click',()=>this.promoteImageToHero(index)); thumbs.appendChild(t); });
const aplus=document.getElementById('aplusImages'); aplus.className = `aplus-images ${this.aplusLayout}`; aplus.innerHTML=''; this.uploadedImages.forEach((image,index)=>{ const tile=document.createElement('div'); tile.className='aplus-image'; const img=document.createElement('img'); img.src=image.data; tile.appendChild(img); const pos = index+1; const badge=document.createElement('span'); badge.className='order-badge'; badge.textContent = `p${pos}`; tile.appendChild(badge); aplus.appendChild(tile); });
this.updatePremiumModules();
document.getElementById('exportBtn').disabled = this.uploadedImages.length===0 && Object.keys(this.csvData).length===0; document.getElementById('downloadAssetsBtn').disabled = this.uploadedImages.length===0;
}
updateTrayVisibility(){
document.getElementById('brandStoryContent').classList.toggle('visible', this.brandStoryEnabled);
document.getElementById('comparisonContent').classList.toggle('visible', this.comparisonEnabled);
document.getElementById('faqContent').classList.toggle('visible', this.faqEnabled);
document.getElementById('videoContent').classList.toggle('visible', this.videoEnabled);
document.getElementById('featuresContent').classList.toggle('visible', this.featuresEnabled);
document.getElementById('ingredientsContent').classList.toggle('visible', this.ingredientsEnabled);
const heroSelect = document.getElementById('brandStoryHeroSelect');
const videoThumbSelect = document.getElementById('videoThumbnailSelect');
const prevHero = heroSelect.value;
const prevThumb = videoThumbSelect.value;
heroSelect.innerHTML = '<option value="">None</option>';
videoThumbSelect.innerHTML = '<option value="">None</option>';
this.uploadedImages.forEach((img, idx) => {
const opt1 = document.createElement('option'); opt1.value = String(idx); opt1.textContent = img.name; heroSelect.appendChild(opt1);
const opt2 = document.createElement('option'); opt2.value = String(idx); opt2.textContent = img.name; videoThumbSelect.appendChild(opt2);
});
// restore previous selections if still valid
if (prevHero !== '' && Number(prevHero) < this.uploadedImages.length) heroSelect.value = prevHero; else heroSelect.value = '';
if (prevThumb !== '' && Number(prevThumb) < this.uploadedImages.length) videoThumbSelect.value = prevThumb; else videoThumbSelect.value = '';
}
updatePremiumModules(){
// Update tray visibility first
this.updateTrayVisibility();
// Brand Story
const brandStoryEl = document.getElementById('brandStory'); const brandStoryTextEl = document.getElementById('brandStoryText');
if (this.brandStoryEnabled){
brandStoryEl.style.display='block';
document.getElementById('brandStoryTextEl').textContent = brandStoryTextEl.value.trim() || 'Brand story text will appear here...';
const sel = heroSelect.value;
const heroIdx = sel === '' ? -1 : parseInt(sel, 10);
if (!Number.isNaN(heroIdx) && heroIdx >= 0 && this.uploadedImages[heroIdx]){
document.getElementById('brandStoryHero').innerHTML = '<img src="' + this.uploadedImages[heroIdx].data + '" style="width:100%; height:250px; object-fit:cover;">';
} else {
document.getElementById('brandStoryHero').innerHTML = 'Brand story hero image';
}
} else {
brandStoryEl.style.display='none';
}
// Comparison
const comparisonEl = document.getElementById('comparisonTableContainer'); const comparisonTable = document.getElementById('comparisonTable');
if (this.comparisonEnabled){ comparisonEl.style.display='block'; comparisonTable.innerHTML=''; const rows = document.querySelectorAll('.comparison-row'); if (rows.length > 0){ const tr = document.createElement('tr'); const firstRow = rows[0]; const inputs = firstRow.querySelectorAll('input'); inputs.forEach(inp => { const th = document.createElement('th'); th.textContent = inp.value || inp.placeholder; tr.appendChild(th); }); comparisonTable.appendChild(tr); for (let i = 1; i < rows.length; i++){ const r = document.createElement('tr'); const ins = rows[i].querySelectorAll('input'); ins.forEach(inp => { const td = document.createElement('td'); td.textContent = inp.value || inp.placeholder; r.appendChild(td); }); comparisonTable.appendChild(r); } } } else { comparisonEl.style.display='none'; }
// FAQ
const faqEl = document.getElementById('faqContainer'); const faqSec = document.getElementById('faqSection'); faqSec.innerHTML=''; let hasFaq=false; const pairs = document.querySelectorAll('.faq-pair'); pairs.forEach(pair => { const qInp = pair.querySelector('input'); const aTxt = pair.querySelector('textarea'); if (qInp.value.trim() && aTxt.value.trim()){ const item = document.createElement('div'); item.className='faq-item'; item.innerHTML = '<div class="faq-question">' + qInp.value.trim() + '</div><div class="faq-answer">' + aTxt.value.trim() + '</div>'; faqSec.appendChild(item); hasFaq=true; } }); faqEl.style.display = hasFaq ? 'block' : 'none';
// Video
const videoEl = document.getElementById('videoContainer'); const videoUrl = document.getElementById('videoUrl').value.trim(); const videoThumbIdx = parseInt(document.getElementById('videoThumbnailSelect').value) || -1;
if (this.videoEnabled){ videoEl.style.display='block'; let content = videoUrl ? `Video: ${videoUrl}` : 'Video content will appear here...'; if (videoThumbIdx >= 0 && this.uploadedImages[videoThumbIdx]){ content = '<img src="' + this.uploadedImages[videoThumbIdx].data + '" style="width:100%; height:300px; object-fit:cover;">'; } document.getElementById('videoPlaceholder').innerHTML = content; } else { videoEl.style.display='none'; }
// Features
const featuresEl = document.getElementById('featuresContainer'); const featuresSec = document.getElementById('featuresSection'); featuresSec.innerHTML=''; let hasFeatures=false; const featureItems = document.querySelectorAll('.feature-edit-item'); featureItems.forEach(item => { const iconInp = item.querySelector('input[type="text"]'); const headlineInp = item.querySelectorAll('input[type="text"]')[1]; const textArea = item.querySelector('textarea'); if (headlineInp.value.trim() && textArea.value.trim()){ const feature = document.createElement('div'); feature.className='feature-item'; feature.innerHTML = '<div class="feature-icon">' + (iconInp.value.trim() || '⭐') + '</div><div class="feature-content"><div class="feature-headline">' + headlineInp.value.trim() + '</div><div class="feature-text">' + textArea.value.trim() + '</div></div>'; featuresSec.appendChild(feature); hasFeatures=true; } else if (headlineInp.value.trim() || textArea.value.trim() || iconInp.value.trim()){ const feature = document.createElement('div'); feature.className='feature-item'; feature.innerHTML = '<div class="feature-icon">' + (iconInp.value.trim() || '⭐') + '</div><div class="feature-content"><div class="feature-headline">' + (headlineInp.value.trim() || 'Feature headline...') + '</div><div class="feature-text">' + (textArea.value.trim() || 'Feature description will appear here...') + '</div></div>'; featuresSec.appendChild(feature); hasFeatures=true; } }); featuresEl.style.display = this.featuresEnabled ? 'block' : 'none';
// Ingredients
const ingredientsEl = document.getElementById('ingredientsContainer'); const ingredientsGrid = document.getElementById('ingredientsGrid'); ingredientsGrid.innerHTML=''; let hasIngredients=false; const ingredientItems = document.querySelectorAll('.ingredient-edit-item'); ingredientItems.forEach(item => { const nameInp = item.querySelector('input[type="text"]'); const descArea = item.querySelector('textarea'); if (nameInp.value.trim() && descArea.value.trim()){ const ingredient = document.createElement('div'); ingredient.className='ingredient-item'; ingredient.innerHTML = '<div class="ingredient-name">' + nameInp.value.trim() + '</div><div class="ingredient-desc">' + descArea.value.trim() + '</div>'; ingredientsGrid.appendChild(ingredient); hasIngredients=true; } else if (nameInp.value.trim() || descArea.value.trim()){ const ingredient = document.createElement('div'); ingredient.className='ingredient-item'; ingredient.innerHTML = '<div class="ingredient-name">' + (nameInp.value.trim() || 'Ingredient name...') + '</div><div class="ingredient-desc">' + (descArea.value.trim() || 'Ingredient description will appear here...') + '</div>'; ingredientsGrid.appendChild(ingredient); hasIngredients=true; } }); ingredientsEl.style.display = this.ingredientsEnabled ? 'block' : 'none';
}
addComparisonRow(){ const editor = document.getElementById('comparisonEditor'); const newRow = document.createElement('div'); newRow.className = 'comparison-row'; newRow.innerHTML = '<input type="text" placeholder="Attribute"><input type="text" placeholder="Value A"><input type="text" placeholder="Value B"><input type="text" placeholder="Value C">'; editor.appendChild(newRow); this.updatePremiumModules(); }
addFaqPair(){ const editor = document.getElementById('faqEditor'); const newPair = document.createElement('div'); newPair.className = 'faq-pair'; newPair.innerHTML = '<input type="text" placeholder="Question"><textarea placeholder="Answer"></textarea>'; editor.appendChild(newPair); this.updatePremiumModules(); }
addFeatureItem(){ const editor = document.getElementById('featuresEditor'); const newItem = document.createElement('div'); newItem.className = 'feature-edit-item'; newItem.innerHTML = '<input type="text" placeholder="Icon" value="⭐" style="width:60px;"><input type="text" placeholder="Headline" style="flex:1;"><textarea placeholder="Description" style="flex:2;"></textarea>'; editor.appendChild(newItem); this.updatePremiumModules(); }
addIngredientItem(){ const editor = document.getElementById('ingredientsEditor'); const newItem = document.createElement('div'); newItem.className = 'ingredient-edit-item'; newItem.innerHTML = '<input type="text" placeholder="Ingredient Name" style="flex:1;"><textarea placeholder="Description" style="flex:2;"></textarea>'; editor.appendChild(newItem); this.updatePremiumModules(); }
showStatus(msg,type){ const div=document.getElementById('statusMessage'); div.innerHTML=`<div style="padding:10px; border-radius:4px; margin-bottom:15px; font-size:14px; background:${type==='success'?'#d4edda':'#f8d7da'}; color:${type==='success'?'#155724':'#721c24'}; border:1px solid ${type==='success'?'#c3e6cb':'#f5c6cb'};">${msg}</div>`; setTimeout(()=>{ div.innerHTML=''; },3000); }
downloadCsv(){
// Build rows as [field, , value]
const rows = [];
const push = (k, v) => { if (v && String(v).trim()) rows.push([k, '', String(v).trim()]); };
// Base fields
push('DMI_Product_type', document.getElementById('brandName').textContent);
push('Amazon - Product Name (Long)', document.getElementById('productTitle').textContent);
push('Amazon Benefits', document.getElementById('subtitle').textContent);
// Bullets from DOM
Array.from(document.getElementById('bullets').children).forEach((el, i) => push(`Amazon - Bullet Features ${i+1}`, el.textContent));
push('amazon_product_description_short', document.getElementById('description').textContent);
push('amazon_instruction_of_use', document.getElementById('instructions').style.display !== 'none' ? document.getElementById('instructions').textContent : '');
// Brand Story
if (this.brandStoryEnabled){
const txt = document.getElementById('brandStoryText').value;
push('premium_brand_story_text', txt);
}
// Comparison: header row then attribute rows
if (this.comparisonEnabled){
const rowsEls = document.querySelectorAll('.comparison-row');
if (rowsEls.length){
const hdr = rowsEls[0].querySelectorAll('input');
push('premium_comparison_sku_1_name', hdr[1]?.value || '');
push('premium_comparison_sku_2_name', hdr[2]?.value || '');
push('premium_comparison_sku_3_name', hdr[3]?.value || '');
for (let i=1;i<rowsEls.length;i++){
const ins = rowsEls[i].querySelectorAll('input');
const idx = i; // 1-based attribute index
push(`premium_comparison_attr_${idx}`, ins[0]?.value || '');
push(`premium_comparison_sku_1_attr_${idx}`, ins[1]?.value || '');
push(`premium_comparison_sku_2_attr_${idx}`, ins[2]?.value || '');
push(`premium_comparison_sku_3_attr_${idx}`, ins[3]?.value || '');
}
}
}
// FAQ
if (this.faqEnabled){
const pairs = document.querySelectorAll('.faq-pair');
let n=1; pairs.forEach(p=>{ const q=p.querySelector('input')?.value||''; const a=p.querySelector('textarea')?.value||''; push(`premium_faq_question_${n}`, q); push(`premium_faq_answer_${n}`, a); n++; });
}
// Video
if (this.videoEnabled){
push('premium_video_url', document.getElementById('videoUrl').value || '');
push('premium_video_thumbnail_index', document.getElementById('videoThumbnailSelect').value || '');
}
// Features
if (this.featuresEnabled){
const items = document.querySelectorAll('.feature-edit-item');
let n=1; items.forEach(it=>{ const icon=it.querySelector('input[type="text"]')?.value||''; const head=it.querySelectorAll('input[type="text"]')[1]?.value||''; const txt=it.querySelector('textarea')?.value||''; push(`premium_feature_${n}_icon`, icon); push(`premium_feature_${n}_headline`, head); push(`premium_feature_${n}_text`, txt); n++; });
}
// Ingredients
if (this.ingredientsEnabled){
const items = document.querySelectorAll('.ingredient-edit-item');
let n=1; items.forEach(it=>{ const name=it.querySelector('input[type="text"]')?.value||''; const desc=it.querySelector('textarea')?.value||''; push(`premium_ingredient_${n}_name`, name); push(`premium_ingredient_${n}_desc`, desc); n++; });
}
// Build CSV text
let csv = '';
rows.forEach(([k, , v])=>{
const safe = '"' + String(v).replace(/"/g,'""') + '"';
csv += `${k},,${safe}\n`;
});
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'premium_structure.csv';
link.click();
URL.revokeObjectURL(link.href);
}
async exportMockup(){ const mockup=document.getElementById('amazonMockup'); const btn=document.getElementById('exportBtn'); btn.disabled=true; btn.textContent='Exporting...'; try{ if (typeof html2canvas !== 'undefined'){ const canvas = await html2canvas(mockup, { backgroundColor:'#ffffff', scale:2 }); const ean = (this.detectedEAN || document.getElementById('eanInput').value || '').trim(); const fileName = `${ean ? ean + '_' : ''}ATF_AMZ.jpg`; const dataUrl = canvas.toDataURL('image/jpeg', 0.92); const link=document.createElement('a'); link.download=fileName; link.href=dataUrl; link.click(); } else { this.showStatus('Export requires html2canvas','error'); } } catch(err){ this.showStatus('Export failed: '+err.message,'error'); } finally { btn.disabled=false; btn.textContent='Export as JPG'; } }
async downloadAssetsZip(){ try{ if (!this.uploadedImages.length) return; if (typeof JSZip==='undefined'){ this.showStatus('ZIP download requires JSZip','error'); return; } const zip = new JSZip(); const folder = zip.folder('assets'); for (let i=0;i<this.uploadedImages.length;i++){ const img=this.uploadedImages[i]; const pos=i+1; const ext=(img.file && img.file.name.split('.').pop())||'jpg'; const base=(img.file && img.file.name.replace(/\.[^.]+$/, ''))||`image_${pos}`; const newName=`XXX_${base}_AMZ_p${pos}.${ext}`; let blob; if (img.file instanceof File || img.file instanceof Blob){ blob=img.file; } else { blob = await (await fetch(img.data)).blob(); } folder.file(newName, blob); } const content = await zip.generateAsync({ type:'blob' }); const link=document.createElement('a'); link.href=URL.createObjectURL(content); const eanVal=(this.detectedEAN || document.getElementById('eanInput').value || '').trim(); link.download = `${eanVal ? eanVal + '_' : ''}AMZ.zip`; link.click(); URL.revokeObjectURL(link.href); } catch(err){ this.showStatus('Failed to build ZIP: '+err.message,'error'); } }
}
document.addEventListener('DOMContentLoaded', ()=>{ new AmazonMockupGenerator(); });
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
</body>
</html>