ondc-campaign-sdk
Version:
[](https://www.npmjs.com/package/ondc-campaign-sdk) [](LICENSE) [ • 29.7 kB
JavaScript
"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>
`;
}