UNPKG

ondc-campaign-sdk

Version:

[![npm version](https://img.shields.io/npm/v/ondc-campaign-sdk.svg)](https://www.npmjs.com/package/ondc-campaign-sdk) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Made with ❤️](https://img.shields.io/badge/Made%20with-%

956 lines (863 loc) 29.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.renderModernPremiumTemplate = renderModernPremiumTemplate; // Static data that will be replaced by API calls later async function getBrandsData() { return new Promise(resolve => { setTimeout(() => { resolve([ { id: 1, name: "Nike", logo: "https://logos-world.net/wp-content/uploads/2020/04/Nike-Logo.png", discountText: "Up to 40% OFF", backgroundColor: "#FF6B6B", redirectUrl: "/brands/nike" }, { id: 2, name: "Adidas", logo: "https://logos-world.net/wp-content/uploads/2020/04/Adidas-Logo.png", discountText: "Up to 35% OFF", backgroundColor: "#4ECDC4", redirectUrl: "/brands/adidas" }, { id: 3, name: "Apple", logo: "https://logos-world.net/wp-content/uploads/2020/04/Apple-Logo.png", discountText: "Special Offers", backgroundColor: "#45B7D1", redirectUrl: "/brands/apple" }, { id: 4, name: "Samsung", logo: "https://logos-world.net/wp-content/uploads/2020/04/Samsung-Logo.png", discountText: "Up to 30% OFF", backgroundColor: "#F39C12", redirectUrl: "/brands/samsung" }, { id: 5, name: "Sony", logo: "https://logos-world.net/wp-content/uploads/2020/04/Sony-Logo.png", discountText: "Best Deals", backgroundColor: "#9B59B6", redirectUrl: "/brands/sony" } ]); }, 100); }); } async function getCategoriesData() { return new Promise(resolve => { setTimeout(() => { resolve([ { id: 1, name: "Electronics", icon: "📱", gradient: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)", redirectUrl: "/categories/electronics" }, { id: 2, name: "Fashion", icon: "👗", gradient: "linear-gradient(135deg, #f093fb 0%, #f5576c 100%)", redirectUrl: "/categories/fashion" }, { id: 3, name: "Home & Garden", icon: "🏠", gradient: "linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)", redirectUrl: "/categories/home-garden" }, { id: 4, name: "Sports", icon: "⚽", gradient: "linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)", redirectUrl: "/categories/sports" }, { id: 5, name: "Books", icon: "📚", gradient: "linear-gradient(135deg, #fa709a 0%, #fee140 100%)", redirectUrl: "/categories/books" }, { id: 6, name: "Health & Beauty", icon: "💄", gradient: "linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)", redirectUrl: "/categories/health-beauty" } ]); }, 100); }); } async function renderModernPremiumTemplate(campaignData, styleConfig = {}, templateConfig = {}) { if (!campaignData || !campaignData.products || campaignData.products.length === 0) { return renderNoContentState(); } const defaultStyle = { primaryGradient: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)", secondaryGradient: "linear-gradient(135deg, #f093fb 0%, #f5576c 100%)", accentColor: "#ff6b6b", textPrimary: "#2d3748", textSecondary: "#718096", backgroundColor: "#ffffff", borderRadius: "16px" }; const defaultTemplateConfig = { enableAnimations: true, autoScrollSpeed: 30, showDiscountBadges: true, cardStyle: 'elevated' }; const mergedStyle = { ...defaultStyle, ...styleConfig }; const mergedConfig = { ...defaultTemplateConfig, ...templateConfig }; // Get static data (will be API calls later) const brandsData = await getBrandsData(); const categoriesData = await getCategoriesData(); const featuredProducts = getFeaturedProducts(campaignData.products, 8); return ` ${generateModernPremiumCSS(mergedStyle, mergedConfig)} <div class="modern-premium-template" id="modern-campaign"> ${generateHeroBanner(campaignData)} ${generateBrandsSection(brandsData, mergedConfig)} ${generateCategoriesSection(categoriesData, mergedConfig)} ${generateFeaturedProductsSection(featuredProducts, campaignData, mergedConfig)} </div> ${generateModernPremiumJavaScript(mergedConfig)} `; } function renderNoContentState() { return ` <div class="no-content-state"> <div class="loading-animation"> <div class="spinner"></div> <h2>Loading Campaign...</h2> <p>Preparing your exclusive deals</p> </div> <style> .no-content-state { min-height: 400px; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 16px; color: white; text-align: center; margin: 2rem 0; } .loading-animation { display: flex; flex-direction: column; align-items: center; gap: 1rem; } .spinner { width: 40px; height: 40px; border: 3px solid rgba(255,255,255,0.3); border-top: 3px solid white; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style> </div> `; } function getFeaturedProducts(products, count) { return [...products] .sort((a, b) => (b.discountPercentage || 0) - (a.discountPercentage || 0)) .slice(0, count); } function generateHeroBanner(campaignData) { return ` <section class="hero-banner"> <div class="hero-content"> <div class="hero-text"> <h1 class="hero-title">${campaignData.campaignName || 'Special Campaign'}</h1> <p class="hero-description">${campaignData.description || 'Discover amazing products with unbeatable prices'}</p> </div> <div class="hero-image"> <img src="${campaignData.banner || 'https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?w=800&h=300&fit=crop'}" alt="Campaign Banner" /> </div> </div> </section> `; } function generateBrandsSection(brandsData, config) { const brandCards = brandsData.map((brand, index) => ` <div class="brand-card" data-brand-id="${brand.id}" onclick="redirectToBrand('${brand.redirectUrl}')"> <div class="brand-content"> <div class="brand-logo"> <img src="${brand.logo}" alt="${brand.name}" /> </div> <h3 class="brand-name">${brand.name}</h3> <p class="brand-offer">${brand.discountText}</p> </div> </div> `).join(''); return ` <section class="brands-section"> <div class="section-header"> <h2 class="section-title">Top Deals By Brands</h2> <div class="nav-buttons"> <button class="nav-btn prev-btn" onclick="scrollContainer('brandsContainer', -300)"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M15 18l-6-6 6-6"/> </svg> </button> <button class="nav-btn next-btn" onclick="scrollContainer('brandsContainer', 300)"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M9 18l6-6-6-6"/> </svg> </button> </div> </div> <div class="scroll-container" id="brandsContainer"> <div class="brands-scroll"> ${brandCards} </div> </div> </section> `; } function generateCategoriesSection(categoriesData, config) { const categoryCards = categoriesData.map((category, index) => ` <div class="category-card" data-category-id="${category.id}" onclick="redirectToCategory('${category.redirectUrl}')"> <div class="category-content"> <div class="category-icon">${category.icon}</div> <h3 class="category-name">${category.name}</h3> </div> </div> `).join(''); return ` <section class="categories-section"> <div class="section-header"> <h2 class="section-title">Shop By Categories</h2> <div class="nav-buttons"> <button class="nav-btn prev-btn" onclick="scrollContainer('categoriesContainer', -300)"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M15 18l-6-6 6-6"/> </svg> </button> <button class="nav-btn next-btn" onclick="scrollContainer('categoriesContainer', 300)"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M9 18l6-6-6-6"/> </svg> </button> </div> </div> <div class="scroll-container" id="categoriesContainer"> <div class="categories-scroll"> ${categoryCards} </div> </div> </section> `; } function generateFeaturedProductsSection(products, campaignData, config) { const productCards = products.map((product, index) => generateModernProductCard(product, index, config, campaignData)).join(''); return ` <section class="featured-products-section"> <div class="section-header"> <h2 class="section-title">Featured Products</h2> </div> <div class="products-grid"> ${productCards} </div> </section> `; } function generateModernProductCard(product, index, config, campaignData) { const productName = product.productName || ''; const productId = product.productId || ''; const imageUrl = product.imgUrl || (product.galleryImages && product.galleryImages.length > 0 ? product.galleryImages[0].url : ''); const discountPercent = product.discountPercentage || 0; const regularPrice = product.regularPrice || 0; const discountedPrice = product.discountedPrice || regularPrice; const brandName = product.brandName || ''; const rating = product.productRatings || 0; const formattedRegularPrice = `₹${regularPrice.toLocaleString('en-IN')}`; const formattedDiscountedPrice = `₹${discountedPrice.toLocaleString('en-IN')}`; const animationDelay = config.enableAnimations ? `style="animation-delay: ${index * 0.15}s;"` : ''; const productNameSlug = productName .toLowerCase() .replace(/[^a-z0-9\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-'); const productUrl = `https://shop.samhita.org/product/${productNameSlug}/${productId}`; return ` <div class="product-card ${config.cardStyle}-style" ${animationDelay} data-product-id="${productId}"> <div class="product-image-container"> <img src="${imageUrl}" alt="${productName}" loading="lazy" onerror="this.onerror=null;this.src='https://via.placeholder.com/300x300?text=Product';" /> ${config.showDiscountBadges && discountPercent > 0 ? `<div class="discount-badge">-${discountPercent}%</div>` : ''} <div class="product-overlay"> <button class="quick-view-btn" onclick="openQuickView('${productId}')"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/> <circle cx="12" cy="12" r="3"/> </svg> Quick View </button> </div> </div> <div class="product-details"> ${brandName ? `<div class="product-brand">${brandName}</div>` : ''} <h3 class="product-name">${productName}</h3> ${rating > 0 ? ` <div class="product-rating"> <div class="stars"> ${Array(5).fill('').map((_, i) => `<span class="star ${i < Math.floor(rating) ? 'filled' : ''}"></span>`).join('')} </div> <span class="rating-value">${rating.toFixed(1)}</span> </div> ` : ''} <div class="product-price"> ${discountedPrice < regularPrice ? `<span class="current-price">${formattedDiscountedPrice}</span> <span class="original-price">${formattedRegularPrice}</span>` : `<span class="current-price">${formattedRegularPrice}</span>`} </div> <button class="add-to-cart-btn" onclick="handleProductClick('${productId}', '${productUrl}', '${campaignData._id || ''}')"> <span>Add to Cart</span> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="9" cy="21" r="1"/> <circle cx="20" cy="21" r="1"/> <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/> </svg> </button> </div> </div> `; } function generateModernPremiumCSS(style, config) { return ` <style> :root { --primary-gradient: ${style.primaryGradient}; --secondary-gradient: ${style.secondaryGradient}; --accent: ${style.accentColor}; --text-primary: ${style.textPrimary}; --text-secondary: ${style.textSecondary}; --bg-color: ${style.backgroundColor}; --border-radius: ${style.borderRadius}; } .modern-premium-template { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.5; color: var(--text-primary); background: var(--bg-color); padding: 1.5rem; max-width: 1200px; margin: 0 auto; } * { margin: 0; padding: 0; box-sizing: border-box; } /* Hero Banner */ .hero-banner { margin-bottom: 2.5rem; border-radius: 12px; overflow: hidden; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border: 1px solid #dee2e6; } .hero-content { display: grid; grid-template-columns: 1fr 1fr; align-items: center; gap: 2rem; padding: 2rem; } .hero-title { font-size: 2.2rem; font-weight: 600; margin-bottom: 1rem; line-height: 1.3; color: var(--text-primary); } .hero-description { font-size: 1rem; color: var(--text-secondary); line-height: 1.5; } .hero-image img { width: 100%; height: 200px; object-fit: cover; border-radius: 8px; } /* Section Headers */ .section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; } .section-title { font-size: 1.5rem; font-weight: 600; color: var(--text-primary); } /* Navigation Buttons */ .nav-buttons { display: flex; gap: 0.5rem; } .nav-btn { width: 40px; height: 40px; border: 1px solid #dee2e6; background: white; border-radius: 8px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s ease; color: var(--text-secondary); } .nav-btn:hover { background: #f8f9fa; border-color: var(--accent); color: var(--accent); } .nav-btn:disabled { opacity: 0.5; cursor: not-allowed; } /* Scroll Containers */ .scroll-container { overflow: hidden; position: relative; } .brands-scroll, .categories-scroll { display: flex; gap: 1rem; overflow-x: auto; scroll-behavior: smooth; padding-bottom: 0.5rem; } .brands-scroll::-webkit-scrollbar, .categories-scroll::-webkit-scrollbar { display: none; } /* Brand Cards */ .brand-card { min-width: 280px; height: 140px; border: 1px solid #dee2e6; border-radius: 8px; cursor: pointer; transition: all 0.2s ease; background: white; flex-shrink: 0; } .brand-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); border-color: var(--accent); } .brand-content { padding: 1.5rem; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; } .brand-logo img { width: 60px; height: 40px; object-fit: contain; margin-bottom: 0.75rem; } .brand-name { font-size: 1.1rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary); } .brand-offer { font-size: 0.9rem; color: var(--accent); font-weight: 500; } /* Category Cards */ .category-card { min-width: 180px; height: 140px; border: 1px solid #dee2e6; border-radius: 8px; cursor: pointer; transition: all 0.2s ease; background: white; flex-shrink: 0; } .category-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); border-color: var(--accent); } .category-content { padding: 1.5rem; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; } .category-icon { font-size: 2.5rem; margin-bottom: 0.75rem; } .category-name { font-size: 1rem; font-weight: 600; color: var(--text-primary); } /* Products Grid */ .featured-products-section { margin-bottom: 2rem; } .products-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem; align-items: start; } /* Product Cards */ .product-card { background: white; border: 1px solid #dee2e6; border-radius: 12px; overflow: hidden; transition: all 0.2s ease; height: fit-content; min-height: 480px; display: flex; flex-direction: column; } .product-card:hover { transform: translateY(-4px); box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1); border-color: var(--accent); } .product-image-container { position: relative; height: 200px; overflow: hidden; flex-shrink: 0; } .product-image-container img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s ease; } .product-card:hover .product-image-container img { transform: scale(1.02); } .discount-badge { position: absolute; top: 0.75rem; right: 0.75rem; background: var(--accent); color: white; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; z-index: 3; } .product-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.3s ease; } .product-card:hover .product-overlay { opacity: 1; } .quick-view-btn { background: white; color: var(--text-primary); border: none; padding: 0.5rem 1rem; border-radius: 6px; font-weight: 500; display: flex; align-items: center; gap: 0.5rem; cursor: pointer; transition: all 0.2s ease; font-size: 0.85rem; } .quick-view-btn:hover { background: var(--accent); color: white; } .product-details { padding: 1.25rem; flex-grow: 1; display: flex; flex-direction: column; min-height: 0; } .product-brand { font-size: 0.75rem; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.5rem; } .product-name { font-size: 1rem; font-weight: 500; margin-bottom: 0.75rem; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 2.8rem; } .product-rating { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; min-height: 1.2rem; } .stars { display: flex; gap: 1px; } .star { color: #ddd; font-size: 0.85rem; } .star.filled { color: #ffc107; } .rating-value { font-size: 0.8rem; color: var(--text-secondary); font-weight: 500; } .product-price { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1rem; min-height: 1.5rem; } .current-price { font-size: 1.2rem; font-weight: 600; color: var(--accent); } .original-price { font-size: 0.9rem; color: var(--text-secondary); text-decoration: line-through; } .add-to-cart-btn { width: 100%; background: var(--text-primary); color: white; border: none; padding: 0.75rem; border-radius: 8px; font-weight: 500; display: flex; align-items: center; justify-content: center; gap: 0.5rem; cursor: pointer; transition: all 0.2s ease; margin-top: auto; min-height: 44px; } .add-to-cart-btn:hover { background: var(--accent); } /* Responsive Design */ @media (max-width: 768px) { .modern-premium-template { padding: 1rem; } .hero-content { grid-template-columns: 1fr; gap: 1.5rem; padding: 1.5rem; } .hero-title { font-size: 1.8rem; } .section-header { flex-direction: column; gap: 1rem; align-items: flex-start; } .section-title { font-size: 1.3rem; } .brand-card { min-width: 220px; height: 120px; } .category-card { min-width: 140px; height: 120px; } .products-grid { grid-template-columns: 1fr; } .product-card { min-height: 420px; } } </style> `; } function generateModernPremiumJavaScript(config) { const timestamp = new Date().toISOString(); return ` <script> // Modern Premium Template JavaScript - Generated: ${timestamp} console.log("Modern Premium Template JavaScript loaded at ${timestamp}"); // Navigation scroll function function scrollContainer(containerId, scrollAmount) { console.log("scrollContainer called with:", containerId, scrollAmount); const container = document.getElementById(containerId); if (container) { const scrollElement = container.querySelector('.brands-scroll, .categories-scroll'); if (scrollElement) { scrollElement.scrollBy({ left: scrollAmount, behavior: 'smooth' }); // Update button states after scroll setTimeout(() => updateNavButtons(), 300); } else { console.error("Scroll element not found in container:", containerId); } } else { console.error("Container not found:", containerId); } } // Brand redirection function redirectToBrand(url) { console.log('Redirecting to brand:', url); window.open(url, '_blank'); } // Category redirection function redirectToCategory(url) { console.log('Redirecting to category:', url); window.open(url, '_blank'); } // Quick view functionality function openQuickView(productId) { console.log('Opening quick view for product:', productId); // Implement quick view modal here } // Product click with transaction API function handleProductClick(productId, productUrl, campaignId) { console.log('handleProductClick called with:', productId, productUrl, campaignId); // Get user ID from localStorage or generate let userId = localStorage.getItem('ondc_user_id'); if (!userId) { userId = crypto.randomUUID(); localStorage.setItem('ondc_user_id', userId); } // Update transaction status fetch('https://ondc-sdk.samhita.org/api/transactions/update', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ campaign_id: campaignId, product_id: productId, status: 'initiated', user_id: userId }) }) .then(response => { if (response.ok) { window.open(productUrl, '_blank'); } else { console.error('Failed to update transaction'); window.open(productUrl, '_blank'); // Still redirect even if tracking fails } }) .catch(err => { console.error('Error updating transaction:', err); window.open(productUrl, '_blank'); // Still redirect even if tracking fails }); } // Initialize navigation buttons state function updateNavButtons() { const containers = ['brandsContainer', 'categoriesContainer']; containers.forEach(containerId => { const container = document.getElementById(containerId); if (!container) return; const scrollElement = container.querySelector('.brands-scroll, .categories-scroll'); if (!scrollElement) return; const section = container.closest('section'); if (!section) return; const prevBtn = section.querySelector('.prev-btn'); const nextBtn = section.querySelector('.next-btn'); if (prevBtn && nextBtn) { const isAtStart = scrollElement.scrollLeft <= 0; const isAtEnd = scrollElement.scrollLeft >= (scrollElement.scrollWidth - scrollElement.clientWidth); prevBtn.disabled = isAtStart; nextBtn.disabled = isAtEnd; } }); } // Initialize when DOM is loaded document.addEventListener('DOMContentLoaded', function() { console.log("DOM Content Loaded - Initializing Modern Premium Template"); // Update navigation buttons on scroll const scrollElements = document.querySelectorAll('.brands-scroll, .categories-scroll'); console.log("Found scroll elements:", scrollElements.length); scrollElements.forEach(element => { element.addEventListener('scroll', updateNavButtons); }); // Initial button state updateNavButtons(); // Update on window resize window.addEventListener('resize', updateNavButtons); console.log("Modern Premium Template initialization complete"); }); </script> `; }