/** * TradeMate Popup - Main UI Logic */ import { login, translate, generateReply, searchLeads, generateMarketing, getBalance, getSubscriptionPlans, getCreditPackages, ApiError, } from '../api/client.js'; // ─── State ─────────────────────────────────────────── let creditsState = { balance: 0, dailyFreeCharsLeft: 0, planName: 'Free', }; // ─── DOM refs ──────────────────────────────────────── const $ = (id) => document.getElementById(id); const screenSettings = $('screen-settings'); const screenMain = $('screen-main'); const settingsUrl = $('settings-url'); const settingsUser = $('settings-username'); const settingsPass = $('settings-password'); const settingsLogin = $('settings-login'); const settingsStatus = $('settings-status'); const translateInput = $('translate-input'); const translateTarget = $('translate-target'); const translateBtn = $('translate-btn'); const translateResult = $('translate-result'); const replyInput = $('reply-input'); const replyTone = $('reply-tone'); const replyBtn = $('reply-btn'); const replyResult = $('reply-result'); const discoveryProduct = $('discovery-product'); const discoveryMarket = $('discovery-market'); const discoveryBtn = $('discovery-btn'); const discoveryResult = $('discovery-result'); const marketingName = $('marketing-name'); const marketingDesc = $('marketing-desc'); const marketingStyle = $('marketing-style'); const marketingBtn = $('marketing-btn'); const marketingResult = $('marketing-result'); const creditsDisplay = $('credits-display'); const btnSettings = $('btn-settings'); const upgradeBanner = $('upgrade-banner'); const upgradeBannerText = $('upgrade-banner-text'); const upgradeBannerBtn = $('upgrade-banner-btn'); const upgradeBannerClose = $('upgrade-banner-close'); const upgradeModalOverlay = $('upgrade-modal-overlay'); const modalClose = $('modal-close'); const packageModalOverlay = $('package-modal-overlay'); const pkgModalClose = $('pkg-modal-close'); // ─── CONSTANTS ─────────────────────────────────────── const CONSUMPTION_LABELS = { lead_search: { name: '客户搜索', cost: 10 }, marketing_content: { name: '营销生成', cost: 5 }, reply_suggest: { name: '智能回复', cost: 2 }, translate_per_1000chars: { name: '翻译', cost: 1 }, quotation: { name: '报价单', cost: 2 }, followup_scan: { name: '跟进扫描', cost: 2 }, }; // ─── Init ──────────────────────────────────────────── async function init() { const { apiUrl, token } = await chrome.storage.local.get(['trademate_api_url', 'trademate_token']); if (apiUrl) settingsUrl.value = apiUrl; if (apiUrl && token) { showScreen('main'); await refreshCredits(); } else { showScreen('settings'); } setupTabs(); setupActions(); setupUpgradeUI(); } function showScreen(screen) { screenSettings.style.display = screen === 'settings' ? 'block' : 'none'; screenMain.style.display = screen === 'main' ? 'block' : 'none'; } document.addEventListener('DOMContentLoaded', init); // ─── Credits ───────────────────────────────────────── async function refreshCredits() { try { const bal = await getBalance(); creditsState.balance = bal.balance; creditsState.dailyFreeCharsLeft = bal.dailyFreeTranslateCharsLeft; // Determine plan name from subscription data if (bal.subscription?.plan_id) { creditsState.planName = 'Starter'; // Could fetch plan name from subscription-plans endpoint for accuracy } else { creditsState.planName = 'Free'; } updateCreditsDisplay(); hideUpgradeBanner(); // clear any stale credit error return bal; } catch (err) { console.warn('Failed to fetch credits:', err.message); updateCreditsDisplay(); // show fallback return null; } } function updateCreditsDisplay() { const { balance, planName, dailyFreeCharsLeft } = creditsState; let parts = []; if (planName !== 'Free') { parts.push(`⚡ ${planName}`); } if (balance > 0) { parts.push(`${balance} 积分`); } parts.push(`剩余`); creditsDisplay.textContent = parts.join(' · '); // Make credits clickable to upgrade creditsDisplay.style.cursor = 'pointer'; creditsDisplay.title = '点击查看套餐'; creditsDisplay.onclick = () => showUpgradeModal(); } function showCreditErrorBanner(detail, actionLabel = '升级') { upgradeBannerText.textContent = detail || '次数不足'; upgradeBannerBtn.textContent = actionLabel; upgradeBanner.style.display = 'flex'; } function hideUpgradeBanner() { upgradeBanner.style.display = 'none'; } // ─── Low Credit Warning ────────────────────────────── function checkLowCredits(consumed) { const cost = consumed || 0; const remaining = creditsState.balance - cost; if (remaining <= 5 && creditsState.planName === 'Free') { // Almost out - show subtle hint in footer creditsDisplay.textContent = `⚠ 仅剩 ${Math.max(0, remaining)} 积分 · 升级 ↑`; creditsDisplay.style.cursor = 'pointer'; creditsDisplay.onclick = () => showLowCreditsPrompt(); } } function showLowCreditsPrompt() { showCreditErrorBanner('体验积分即将用完,升级后继续使用', '查看套餐'); } // ─── Upgrade Banner ────────────────────────────────── upgradeBannerBtn.addEventListener('click', () => { showUpgradeModal(); }); upgradeBannerClose.addEventListener('click', () => { hideUpgradeBanner(); }); // ─── Upgrade Modal ─────────────────────────────────── function showUpgradeModal() { upgradeModalOverlay.style.display = 'flex'; } function hideUpgradeModal() { upgradeModalOverlay.style.display = 'none'; } modalClose.addEventListener('click', hideUpgradeModal); upgradeModalOverlay.addEventListener('click', (e) => { if (e.target === upgradeModalOverlay) hideUpgradeModal(); }); // ─── Package Modal (low-commitment) ────────────────── function showPackageModal() { packageModalOverlay.style.display = 'flex'; } function hidePackageModal() { packageModalOverlay.style.display = 'none'; } pkgModalClose.addEventListener('click', hidePackageModal); packageModalOverlay.addEventListener('click', (e) => { if (e.target === packageModalOverlay) hidePackageModal(); }); // ─── Plan/Pkg button handlers ──────────────────────── function setupUpgradeUI() { document.querySelectorAll('.plan-btn[data-plan]').forEach(btn => { btn.addEventListener('click', () => { const plan = btn.dataset.plan; // Open backend payment page or settings page // For now, open the workspace credits page chrome.tabs.create({ url: `${settingsUrl.value?.replace(/\/+$/, '') || 'https://trade.yuzhiran.com'}/workspace/credits`, }); hideUpgradeModal(); }); }); document.querySelectorAll('.plan-btn[data-pkg]').forEach(btn => { btn.addEventListener('click', () => { const pkg = btn.dataset.pkg; chrome.tabs.create({ url: `${settingsUrl.value?.replace(/\/+$/, '') || 'https://trade.yuzhiran.com'}/workspace/credits`, }); hidePackageModal(); }); }); // Package link in upgrade modal footer const pkgLink = document.createElement('a'); pkgLink.href = '#'; pkgLink.textContent = '购买积分包(低至 ¥2.9)'; pkgLink.style.cssText = 'font-size:12px;color:#2563eb;cursor:pointer;'; pkgLink.onclick = (e) => { e.preventDefault(); hideUpgradeModal(); showPackageModal(); }; document.querySelector('.modal-footer .hint')?.after(pkgLink); } // ─── Loading / Result helpers ──────────────────────── function setLoading(btn, loading) { btn.disabled = loading; btn.textContent = loading ? '处理中...' : btn.dataset.originalText || btn.textContent; if (!btn.dataset.originalText) btn.dataset.originalText = btn.textContent; } function showResult(el, html) { el.innerHTML = html; } function showError(el, msg) { el.innerHTML = `