// Marketplace JS - Magazine Layout const API_BASE = '/marketplace/api'; const CACHE_TTL = 3600000; // 1 hour in ms class MarketplaceCache { constructor() { this.prefix = 'c4ai_market_'; } get(key) { const item = localStorage.getItem(this.prefix + key); if (!item) return null; const data = JSON.parse(item); if (Date.now() > data.expires) { localStorage.removeItem(this.prefix + key); return null; } return data.value; } set(key, value, ttl = CACHE_TTL) { const data = { value: value, expires: Date.now() + ttl }; localStorage.setItem(this.prefix + key, JSON.stringify(data)); } clear() { Object.keys(localStorage) .filter(k => k.startsWith(this.prefix)) .forEach(k => localStorage.removeItem(k)); } } class MarketplaceAPI { constructor() { this.cache = new MarketplaceCache(); this.searchTimeout = null; } async fetch(endpoint, useCache = true) { const cacheKey = endpoint.replace(/[^\w]/g, '_'); if (useCache) { const cached = this.cache.get(cacheKey); if (cached) return cached; } try { const response = await fetch(`${API_BASE}${endpoint}`); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); this.cache.set(cacheKey, data); return data; } catch (error) { console.error('API Error:', error); return null; } } async getStats() { return this.fetch('/stats'); } async getCategories() { return this.fetch('/categories'); } async getApps(params = {}) { const query = new URLSearchParams(params).toString(); return this.fetch(`/apps${query ? '?' + query : ''}`); } async getArticles(params = {}) { const query = new URLSearchParams(params).toString(); return this.fetch(`/articles${query ? '?' + query : ''}`); } async getSponsors() { return this.fetch('/sponsors'); } async search(query) { if (query.length < 2) return {}; return this.fetch(`/search?q=${encodeURIComponent(query)}`, false); } } class MarketplaceUI { constructor() { this.api = new MarketplaceAPI(); this.currentCategory = 'all'; this.currentType = ''; this.searchTimeout = null; this.loadedApps = 10; this.init(); } async init() { await this.loadStats(); await this.loadCategories(); await this.loadFeaturedContent(); await this.loadSponsors(); await this.loadMainContent(); this.setupEventListeners(); } async loadStats() { const stats = await this.api.getStats(); if (stats) { document.getElementById('total-apps').textContent = stats.total_apps || '0'; document.getElementById('total-articles').textContent = stats.total_articles || '0'; document.getElementById('total-downloads').textContent = stats.total_downloads || '0'; document.getElementById('last-update').textContent = new Date().toLocaleDateString(); } } async loadCategories() { const categories = await this.api.getCategories(); if (!categories) return; const filter = document.getElementById('category-filter'); categories.forEach(cat => { const btn = document.createElement('button'); btn.className = 'filter-btn'; btn.dataset.category = cat.slug; btn.textContent = cat.name; btn.onclick = () => this.filterByCategory(cat.slug); filter.appendChild(btn); }); } async loadFeaturedContent() { // Load hero featured const featured = await this.api.getApps({ featured: true, limit: 4 }); if (!featured || !featured.length) return; // Hero card (first featured) const hero = featured[0]; const heroCard = document.getElementById('featured-hero'); if (hero) { const imageUrl = hero.image || ''; heroCard.innerHTML = `
${hero.description}
${(app.description || '').substring(0, 100)}...