Scroll to Top
(function (window, document) { "use strict"; const selectors = { board: "[data-coupon-board]", card: "[data-coupon-card]", toggleBtn: ".coupon-toggle-btn", copyBtn: ".coupon-copy-btn", summarizeBtn: ".coupon-summarize-btn", ideasBtn: ".coupon-ideas-btn", summaryBox: ".coupon-summary-box", ideasBox: ".coupon-ideas-box", details: ".coupon-details", feedback: ".coupon-feedback", terms: ".coupon-desc-list li", title: ".coupon-title", dateEl: "#coupon-current-date" }; const config = Object.assign( { geminiApiKey: "", geminiModel: "gemini-2.5-flash-preview-09-2025" }, window.couponsConfig || {} ); document.addEventListener("DOMContentLoaded", init); document.addEventListener("click", handleClicks); function init() { updateCurrentDate(); } function updateCurrentDate() { const el = document.querySelector(selectors.dateEl); if (!el) return; const now = new Date(); const formatted = now.toLocaleDateString("en-IN", { day: "2-digit", month: "long", year: "numeric" }); el.textContent = `As of ${formatted}`; } function handleClicks(event) { const toggleBtn = event.target.closest(selectors.toggleBtn); if (toggleBtn) { event.preventDefault(); toggleDetails(toggleBtn); return; } const copyBtn = event.target.closest(selectors.copyBtn); if (copyBtn) { event.preventDefault(); handleCopy(copyBtn); return; } const summarizeBtn = event.target.closest(selectors.summarizeBtn); if (summarizeBtn) { event.preventDefault(); handleSummarize(summarizeBtn); return; } const ideasBtn = event.target.closest(selectors.ideasBtn); if (ideasBtn) { event.preventDefault(); handleIdeas(ideasBtn); } } function toggleDetails(btn) { const card = btn.closest(selectors.card); if (!card) return; const details = card.querySelector(selectors.details); if (!details) return; const isCollapsed = details.dataset.collapsed !== "false"; details.dataset.collapsed = isCollapsed ? "false" : "true"; details.setAttribute("aria-hidden", String(!isCollapsed)); btn.setAttribute("aria-expanded", String(isCollapsed)); btn.textContent = isCollapsed ? "Hide Details" : "Show Details"; } async function handleCopy(btn) { const card = btn.closest(selectors.card); if (!card) return; const codeBox = btn.closest(".coupon-code-box"); if (!codeBox) return; const code = codeBox.dataset.couponCode; const autoOpen = codeBox.dataset.autoOpen === "true"; const dealUrl = codeBox.dataset.dealUrl || ""; const feedbackEl = codeBox.querySelector(selectors.feedback); if (!code) return; if (code === "NOCODE") { openDealUrl(dealUrl); showFeedback(feedbackEl, "Deal opened in a new tab."); return; } try { await copyToClipboard(code); showFeedback(feedbackEl, `Copied ${code}!`); if (autoOpen && dealUrl) { window.setTimeout(() => openDealUrl(dealUrl), 250); } } catch (error) { console.error("Copy failed:", error); showFeedback(feedbackEl, "Please copy manually.", true); } } function copyToClipboard(text) { if (navigator.clipboard && window.isSecureContext) { return navigator.clipboard.writeText(text); } return new Promise((resolve, reject) => { const textarea = document.createElement("textarea"); textarea.value = text; textarea.style.position = "fixed"; textarea.style.opacity = "0"; document.body.appendChild(textarea); textarea.focus(); textarea.select(); try { const successful = document.execCommand("copy"); document.body.removeChild(textarea); successful ? resolve() : reject(new Error("execCommand failed")); } catch (err) { document.body.removeChild(textarea); reject(err); } }); } function showFeedback(el, message, isError = false) { if (!el) return; el.textContent = message; el.classList.toggle("is-visible", true); el.style.color = isError ? "#b93838" : "#1b8c32"; window.clearTimeout(el._timeout); el._timeout = window.setTimeout(() => { el.classList.remove("is-visible"); }, 1800); } async function handleSummarize(btn) { const card = btn.closest(selectors.card); if (!card) return; const summaryBox = card.querySelector(selectors.summaryBox); const details = card.querySelector(selectors.details); if (!summaryBox || !details) return; if (summaryBox.innerHTML.trim()) { summaryBox.classList.add("is-visible"); btn.hidden = true; details.dataset.collapsed = "false"; details.setAttribute("aria-hidden", "false"); card.querySelector(selectors.toggleBtn)?.setAttribute("aria-expanded", "true"); return; } const terms = collectTerms(card); if (!terms.length) { summaryBox.textContent = "No details available to summarize."; summaryBox.classList.add("is-visible"); btn.hidden = true; return; } const originalText = btn.textContent; btn.textContent = "✨ Summarizing…"; btn.disabled = true; try { const systemPrompt = "You are a helpful assistant. Summarize the coupon terms into one clear sentence. Start with 'In short:' and highlight the main benefit or restriction."; const response = await callGemini(systemPrompt, terms.join(" ")); const cleaned = response.replace(/^In short:/i, "").trim(); summaryBox.innerHTML = `In short: ${cleaned}`; summaryBox.classList.add("is-visible"); btn.hidden = true; forceExpand(details, card); } catch (error) { console.error("Gemini summarize error:", error); summaryBox.textContent = config.geminiApiKey ? "Sorry, we couldn’t load the summary right now." : "Add your Gemini API key to enable this summary."; summaryBox.classList.add("is-visible"); btn.textContent = originalText; btn.disabled = !config.geminiApiKey; } } async function handleIdeas(btn) { const card = btn.closest(selectors.card); if (!card) return; const ideasBox = card.querySelector(selectors.ideasBox); const details = card.querySelector(selectors.details); if (!ideasBox || !details) return; if (ideasBox.innerHTML.trim()) { ideasBox.classList.add("is-visible"); btn.hidden = true; forceExpand(details, card); return; } const terms = collectTerms(card); const title = (card.querySelector(selectors.title)?.textContent || "").trim(); if (!terms.length && !title) { ideasBox.textContent = "Not enough info to suggest ideas."; ideasBox.classList.add("is-visible"); btn.hidden = true; return; } const originalText = btn.textContent; btn.textContent = "✨ Gathering ideas…"; btn.disabled = true; try { const systemPrompt = "You are a shopping assistant. Suggest 3-5 specific products or categories that match the coupon. Output as bullet points without extra text."; const payload = `Coupon Title: ${title} Terms: ${terms.join(" ")}`; const response = await callGemini(systemPrompt, payload); const items = response .split(" ") .map((line) => line.replace(/^[\-\*\u2022]\s*/, "").trim()) .filter(Boolean); if (!items.length) { throw new Error("No suggestions returned."); } ideasBox.innerHTML = `Shopping Ideas: `; ideasBox.classList.add("is-visible"); btn.hidden = true; forceExpand(details, card); } catch (error) { console.error("Gemini ideas error:", error); ideasBox.textContent = config.geminiApiKey ? "Sorry, we couldn’t get ideas right now." : "Add your Gemini API key to enable ideas."; ideasBox.classList.add("is-visible"); btn.textContent = originalText; btn.disabled = !config.geminiApiKey; } } function collectTerms(card) { return Array.from(card.querySelectorAll(selectors.terms)).map((item) => `- ${item.textContent.trim()}`); } function forceExpand(details, card) { details.dataset.collapsed = "false"; details.setAttribute("aria-hidden", "false"); const toggleBtn = card.querySelector(selectors.toggleBtn); if (toggleBtn) { toggleBtn.textContent = "Hide Details"; toggleBtn.setAttribute("aria-expanded", "true"); } } function openDealUrl(url) { if (!url) return; const newWindow = window.open(url, "_blank", "noopener"); if (newWindow) { newWindow.opener = null; } } async function callGemini(systemPrompt, userQuery, retries = 2, delay = 900) { if (!config.geminiApiKey) { throw new Error("Gemini API key missing."); } const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/${config.geminiModel}:generateContent?key=${encodeURIComponent( config.geminiApiKey )}`; const payload = { contents: [{ parts: [{ text: userQuery }] }], systemInstruction: { parts: [{ text: systemPrompt }] } }; try { const response = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); if (!response.ok) { if (response.status === 429 && retries > 0) { await wait(delay); return callGemini(systemPrompt, userQuery, retries - 1, delay * 2); } throw new Error(`Gemini API error: ${response.status} ${response.statusText}`); } const json = await response.json(); const text = json?.candidates?.[0]?.content?.parts?.[0]?.text; if (!text) { throw new Error("Gemini response missing text."); } return text; } catch (error) { if (retries > 0) { await wait(delay); return callGemini(systemPrompt, userQuery, retries - 1, delay * 2); } throw error; } } function wait(ms) { return new Promise((resolve) => window.setTimeout(resolve, ms)); } })(window, document);