const MARKETING_CAMPAIGN_PAGE_SIZE = 100; let marketingCampaignPage = 1; let marketingCampaignTotalRows = 0; let marketingCampaignLastQuery = null; let marketingCampaignLoading = false; function setMarketingCampaignState(message, mode = "") { if (!marketingCampaignState) { return; } marketingCampaignState.textContent = message; marketingCampaignState.classList.toggle("hidden", !message); marketingCampaignState.classList.toggle("loading", mode === "loading"); marketingCampaignState.classList.toggle("error", mode === "error"); } function clearMarketingCampaignRows() { if (marketingCampaignRows) { marketingCampaignRows.innerHTML = ""; } if (marketingCampaignCards) { marketingCampaignCards.innerHTML = ""; } } function marketingBigInt(value) { try { return BigInt(String(value || "0")); } catch (_) { return 0n; } } function formatMarketingInteger(value) { const text = marketingBigInt(value).toString(); return text.replace(/\B(?=(\d{3})+(?!\d))/g, ","); } function formatMarketingCents(value) { const cents = marketingBigInt(value); const sign = cents < 0n ? "-" : ""; const absolute = cents < 0n ? -cents : cents; const whole = absolute / 100n; const fraction = String(absolute % 100n).padStart(2, "0"); return `${sign}${formatMarketingInteger(whole)}.${fraction}`; } function formatMarketingAverage(totalValue, count) { const total = marketingBigInt(totalValue); const divisor = marketingBigInt(count); if (divisor === 0n) { return "--"; } return formatMarketingCents(total / divisor); } function setMarketingCampaignSummary(data = null) { const totalRecords = marketingBigInt(data?.total_records); const totalImpressions = marketingBigInt(data?.total_impressions); const totalClicks = marketingBigInt(data?.total_clicks); const totalImpressionValue = marketingBigInt(data?.total_impression_value); const totalClickValue = marketingBigInt(data?.total_click_value); const totalValue = totalImpressionValue + totalClickValue; if (marketingTotalRecords) { marketingTotalRecords.textContent = data ? formatMarketingInteger(totalRecords) : "--"; } if (marketingTotalImpressions) { marketingTotalImpressions.textContent = data ? formatMarketingInteger(totalImpressions) : "--"; } if (marketingTotalClicks) { marketingTotalClicks.textContent = data ? formatMarketingInteger(totalClicks) : "--"; } if (marketingAvgImpressionValue) { marketingAvgImpressionValue.textContent = data ? formatMarketingAverage(totalImpressionValue, totalImpressions) : "--"; } if (marketingAvgClickValue) { marketingAvgClickValue.textContent = data ? formatMarketingAverage(totalClickValue, totalClicks) : "--"; } if (marketingTotalValue) { marketingTotalValue.textContent = data ? formatMarketingCents(totalValue) : "--"; } } function setMarketingCampaignFooter(visibleCount = 0, totalCount = 0, page = 1) { const totalPages = Math.max(1, Math.ceil(totalCount / MARKETING_CAMPAIGN_PAGE_SIZE)); const pageStart = totalCount && visibleCount ? (page - 1) * MARKETING_CAMPAIGN_PAGE_SIZE : 0; if (marketingCampaignCount) { marketingCampaignCount.textContent = totalCount ? `Showing ${formatMarketingInteger(pageStart + 1)}-${formatMarketingInteger(pageStart + visibleCount)} of ${formatMarketingInteger(totalCount)}` : ""; } if (marketingCampaignPagination) { marketingCampaignPagination.hidden = totalPages <= 1; marketingCampaignPagination.querySelectorAll("[data-marketing-campaign-page]").forEach((button) => { const action = button.dataset.marketingCampaignPage; button.disabled = action === "first" || action === "previous" ? page <= 1 : page >= totalPages; }); } if (marketingCampaignPageInput) { marketingCampaignPageInput.value = String(page); marketingCampaignPageInput.max = String(totalPages); marketingCampaignPageInput.disabled = totalPages <= 1; } if (marketingCampaignPageTotal) { marketingCampaignPageTotal.textContent = formatNumber(totalPages); } } function marketingStatusClass(status) { return String(status || "").toLowerCase() === "pending" ? "pending" : "confirmed"; } function renderMarketingCampaignRows(rows = []) { clearMarketingCampaignRows(); const tableFragment = document.createDocumentFragment(); const cardFragment = document.createDocumentFragment(); rows.forEach((row) => { const tr = document.createElement("tr"); tr.innerHTML = ` ${formatHistoryDate(row.time)} ${htmlEscape(row.campaign)} ${htmlEscape(row.ad_type)} ${htmlEscape(row.keyword)} ${htmlEscape(row.displayed)} ${formatMarketingInteger(row.impression || 0)} ${formatMarketingInteger(row.click || 0)} ${formatMarketingCents(row.impression_value || 0)} ${formatMarketingCents(row.click_value || 0)} ${htmlEscape(row.status)} `; tableFragment.appendChild(tr); const card = document.createElement("article"); card.className = "history-card"; card.innerHTML = `
Time${formatHistoryDate(row.time)}
Campaign${htmlEscape(row.campaign)}
Ad Type${htmlEscape(row.ad_type)}
Keyword${htmlEscape(row.keyword)}
Displayed${htmlEscape(row.displayed)}
Impressions${formatMarketingInteger(row.impression || 0)}
Clicks${formatMarketingInteger(row.click || 0)}
Status${htmlEscape(row.status)}
`; cardFragment.appendChild(card); }); marketingCampaignRows?.appendChild(tableFragment); marketingCampaignCards?.appendChild(cardFragment); } function validateMarketingCampaignQuery() { const advertiser = String(marketingCampaignAdvertiserInput?.value || "").trim(); const campaign = String(marketingCampaignIdInput?.value || "").trim(); if (!walletLoaded) { return { error: "Load a wallet before viewing campaign data." }; } if (!activeBroadcastNode()) { return { error: "Select a broadcast node before viewing campaign data." }; } if (!advertiser) { return { error: "Agency wallet address is required." }; } if (!/^\d+$/.test(campaign)) { return { error: "Campaign ID must be a whole number." }; } return { advertiser, campaign }; } async function loadMarketingCampaignPage(page = marketingCampaignPage) { if (marketingCampaignLoading) { return; } const query = validateMarketingCampaignQuery(); if (query.error) { clearMarketingCampaignRows(); setMarketingCampaignSummary(null); setMarketingCampaignFooter(0, 0, 1); setMarketingCampaignState(query.error, "error"); return; } const invoke = window.__TAURI__?.core?.invoke; if (!invoke) { setMarketingCampaignState("Wallet bridge is not available.", "error"); return; } marketingCampaignLoading = true; marketingCampaignLoadButton?.toggleAttribute("disabled", true); marketingCampaignRefreshButton?.toggleAttribute("disabled", true); setMarketingCampaignState("Loading campaign activity...", "loading"); try { const normalizedPage = Math.max(1, Number.parseInt(page || 1, 10)); const skip = (normalizedPage - 1) * MARKETING_CAMPAIGN_PAGE_SIZE; const data = await invoke("marketing_campaign_history", { broadcastNode: activeBroadcastNode(), advertiser: query.advertiser, campaign: query.campaign, skip, limit: MARKETING_CAMPAIGN_PAGE_SIZE }); marketingCampaignLastQuery = query; marketingCampaignPage = normalizedPage; marketingCampaignTotalRows = Number(marketingBigInt(data?.total_records)); setMarketingCampaignSummary(data); renderMarketingCampaignRows(data?.rows || []); setMarketingCampaignFooter((data?.rows || []).length, marketingCampaignTotalRows, marketingCampaignPage); setMarketingCampaignState((data?.rows || []).length ? "" : "No campaign records found."); } catch (error) { setMarketingCampaignState(String(error), "error"); } finally { marketingCampaignLoading = false; marketingCampaignLoadButton?.toggleAttribute("disabled", false); marketingCampaignRefreshButton?.toggleAttribute("disabled", false); } } function prepareMarketingCampaignPage() { if (!marketingCampaignLastQuery) { setMarketingCampaignSummary(null); setMarketingCampaignFooter(0, 0, 1); if (!marketingCampaignRows?.children.length) { setMarketingCampaignState("Enter an agency wallet and campaign ID to load campaign activity."); } } } function resetMarketingCampaignPage() { marketingCampaignPage = 1; marketingCampaignTotalRows = 0; marketingCampaignLastQuery = null; marketingCampaignLoading = false; if (marketingCampaignAdvertiserInput) { marketingCampaignAdvertiserInput.value = ""; } if (marketingCampaignIdInput) { marketingCampaignIdInput.value = ""; } clearMarketingCampaignRows(); setMarketingCampaignSummary(null); setMarketingCampaignFooter(0, 0, 1); setMarketingCampaignState("Enter an agency wallet and campaign ID to load campaign activity."); } marketingCampaignForm?.addEventListener("submit", (event) => { event.preventDefault(); marketingCampaignPage = 1; loadMarketingCampaignPage(1); }); marketingCampaignRefreshButton?.addEventListener("click", () => { loadMarketingCampaignPage(marketingCampaignPage); }); marketingCampaignPagination?.querySelectorAll("[data-marketing-campaign-page]").forEach((button) => { button.addEventListener("click", () => { const action = button.dataset.marketingCampaignPage; const totalPages = Math.max(1, Math.ceil(marketingCampaignTotalRows / MARKETING_CAMPAIGN_PAGE_SIZE)); if (action === "first") { loadMarketingCampaignPage(1); } else if (action === "previous") { loadMarketingCampaignPage(Math.max(1, marketingCampaignPage - 1)); } else if (action === "next") { loadMarketingCampaignPage(Math.min(totalPages, marketingCampaignPage + 1)); } else if (action === "last") { loadMarketingCampaignPage(totalPages); } }); }); marketingCampaignPageInput?.addEventListener("change", () => { const totalPages = Math.max(1, Math.ceil(marketingCampaignTotalRows / MARKETING_CAMPAIGN_PAGE_SIZE)); const requestedPage = Math.min( totalPages, Math.max(1, Number.parseInt(marketingCampaignPageInput.value || "1", 10)) ); loadMarketingCampaignPage(requestedPage); });