// License Management UI (function() { // Inject license modal HTML injectModal('license-modal', `

DashCaddy License

Activate a license code to unlock premium features.

Enter your license code to activate premium features

`); const modal = document.getElementById('license-modal'); const codeInput = document.getElementById('license-code-input'); const activateBtn = document.getElementById('license-activate'); const deactivateBtn = document.getElementById('license-deactivate'); const errorEl = document.getElementById('license-error'); const successEl = document.getElementById('license-success'); const badgeIcon = document.getElementById('license-badge-icon'); const badgeText = document.getElementById('license-badge-text'); const badge = document.getElementById('license-badge'); const detailsEl = document.getElementById('license-details'); const featureList = document.getElementById('license-feature-list'); const activateSection = document.getElementById('license-activate-section'); let currentStatus = null; function hideMessages() { errorEl.style.display = 'none'; successEl.style.display = 'none'; } function showError(msg) { hideMessages(); errorEl.textContent = msg; errorEl.style.display = 'block'; } function showSuccess(msg) { hideMessages(); successEl.textContent = msg; successEl.style.display = 'block'; } function renderStatus(status) { currentStatus = status; if (status.active) { badge.style.background = 'rgba(46,204,113,0.15)'; badge.style.color = 'var(--ok-fg)'; badgeIcon.textContent = '\u2605'; badgeText.textContent = 'Premium Active'; const expiryLine = status.lifetime ? '
License: LIFETIME
' : `
Expires: ${new Date(status.expiresAt).toLocaleDateString()} (${status.daysRemaining} days remaining)
`; detailsEl.innerHTML = `
Code: ${status.code || '***'}
${expiryLine} `; activateSection.style.display = 'none'; activateBtn.style.display = 'none'; deactivateBtn.style.display = ''; } else { badge.style.background = 'rgba(149,165,166,0.15)'; badge.style.color = 'var(--muted)'; badgeIcon.textContent = '\u2606'; badgeText.textContent = status.expired ? 'License Expired' : 'Free Tier'; detailsEl.innerHTML = status.expired ? '
Your license has expired. Enter a new code to renew.
' : '
Enter a license code to unlock premium features.
'; activateSection.style.display = ''; activateBtn.style.display = ''; deactivateBtn.style.display = 'none'; } // Render feature list const features = status.premiumFeatures || {}; const activeFeatures = new Set(status.features || []); featureList.innerHTML = Object.entries(features).map(([key, info]) => { const active = activeFeatures.has(key); return `
${active ? '\u2705' : '\uD83D\uDD12'}
${info.name}
${info.description}
`; }).join(''); } async function loadStatus() { try { const resp = await fetch('/api/v1/license/status'); const data = await resp.json(); if (data.success) { renderStatus(data.license); updateHeaderBadge(data.license); } } catch (e) { console.warn('Failed to load license status:', e.message); } } async function activateLicense() { const code = codeInput.value.trim(); if (!code) { showError('Please enter a license code.'); return; } hideMessages(); activateBtn.disabled = true; activateBtn.textContent = 'Activating...'; try { const resp = await secureFetch('/api/v1/license/activate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code }) }); const data = await resp.json(); if (data.success) { showSuccess(data.message); codeInput.value = ''; renderStatus(data.license); showNotification('License activated! Premium features unlocked.', 'success', 5000); updateHeaderBadge(data.license); } else { showError(data.error || 'Activation failed'); } } catch (e) { showError('Network error: ' + e.message); } finally { activateBtn.disabled = false; activateBtn.textContent = 'Activate'; } } async function deactivateLicense() { if (!confirm('Deactivate your license? You can reuse the code on another machine.')) return; deactivateBtn.disabled = true; deactivateBtn.textContent = 'Deactivating...'; try { const resp = await secureFetch('/api/v1/license/deactivate', { method: 'POST' }); const data = await resp.json(); if (data.success) { showSuccess(data.message); await loadStatus(); showNotification('License deactivated.', 'info', 3000); updateHeaderBadge({ active: false }); } else { showError(data.error || 'Deactivation failed'); } } catch (e) { showError('Network error: ' + e.message); } finally { deactivateBtn.disabled = false; deactivateBtn.textContent = 'Deactivate'; } } function updateHeaderBadge(status) { const topbar = document.getElementById('license-status-topbar'); const iconEl = document.getElementById('license-topbar-icon'); const textEl = document.getElementById('license-topbar-text'); const timeEl = document.getElementById('license-topbar-time'); if (!topbar) return; topbar.className = 'license-status-topbar ' + (status.active ? 'premium' : 'free'); if (status.active) { iconEl.textContent = '\u2605'; textEl.textContent = 'PREMIUM'; if (status.lifetime) { timeEl.textContent = '\u00b7 LIFETIME'; } else { const days = status.daysRemaining; timeEl.textContent = days != null ? '\u00b7 ' + days + 'd remaining' : ''; } } else { iconEl.textContent = '\u2606'; textEl.textContent = status.expired ? 'EXPIRED' : 'FREE TIER'; timeEl.textContent = ''; } } function openLicenseModal() { hideMessages(); loadStatus(); modal.classList.add('show'); } // Format code input as user types (auto-add dashes) codeInput.addEventListener('input', function() { let val = this.value.toUpperCase().replace(/[^A-Z0-9-]/g, ''); // Don't auto-format if user is deleting if (val.length > this._prevLength) { val = val.replace(/-/g, ''); if (val.length > 2 && !val.startsWith('DC')) { val = 'DC' + val; } // Add dashes after DC and every 5 chars if (val.startsWith('DC') && val.length > 2) { const parts = ['DC']; const rest = val.substring(2); for (let i = 0; i < rest.length; i += 5) { parts.push(rest.substring(i, i + 5)); } val = parts.join('-'); } } this._prevLength = val.length; this.value = val; }); // Wire up events activateBtn.addEventListener('click', activateLicense); deactivateBtn.addEventListener('click', deactivateLicense); codeInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') activateLicense(); }); wireModal(modal, document.getElementById('license-cancel')); const topbarEl = document.getElementById('license-status-topbar'); if (topbarEl) topbarEl.addEventListener('click', () => window.openLicenseModal && window.openLicenseModal()); // Expose for other modules to open window.openLicenseModal = openLicenseModal; // Expose feature check for frontend gating window.checkPremiumFeature = async function(feature) { try { const resp = await fetch(`/api/v1/license/feature/${feature}`); const data = await resp.json(); return data.available; } catch { return false; } }; // Load status on page load loadStatus().then(status => { if (currentStatus) updateHeaderBadge(currentStatus); }); })();