(function () { 'use strict'; // Server-side hint: did the user have paid privileges when the page // rendered? Used to decide whether to offer the 'Import & sync' button. // We still call CassandraSync.getStatus() at click time as the source // of truth, but this lets us skip rendering a button we know is dead. // Value is passed via data-paid attribute on #drop-zone. function ready(fn) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', fn); } else { fn(); } } ready(function () { var P = window.CassandraPortfolio; if (!P) return; var esc = P.esc, fmt = P.fmt, signed = P.signed, cls = P.cls; var dropZone = document.getElementById('drop-zone'); var fileInput = document.getElementById('file-input'); var browseLink = document.getElementById('browse-link'); var filenameEl = document.getElementById('dz-filename'); var previewEl = document.getElementById('import-preview'); var resultEl = document.getElementById('import-result'); if (!dropZone) return; var IS_PAID = dropZone.dataset.paid === 'true'; var currentPie = null; // most recently parsed pie, awaiting commit function showError(msg) { previewEl.hidden = true; resultEl.className = 'result result--err'; resultEl.innerHTML = '
✕ Import failed
' + '
' + esc(msg) + '
'; resultEl.hidden = false; } function showSuccess(headline, sub) { previewEl.hidden = true; resultEl.className = 'result result--ok'; resultEl.innerHTML = '
' + esc(headline) + '
' + (sub ? '
' + sub + '
' : '') + '
' + 'Open dashboard →' + '
'; resultEl.hidden = false; } function renderPreview(pie) { currentPie = pie; resultEl.hidden = true; var t = pie.totals || {}; var rows = (pie.positions || []).map(function (p) { var invested = (p.avg_cost != null && p.qty != null) ? p.avg_cost * p.qty : null; return '' + '' + esc(p.yahoo_ticker || p.t212_slice || '') + '' + '' + esc(p.name || '') + '' + '' + fmt(p.qty, { maximumFractionDigits: 6 }) + '' + '' + fmt(p.avg_cost) + '' + '' + fmt(invested) + '' + ''; }).join(''); var warnings = (pie.warnings || []).map(function (w) { return '
' + esc(w) + '
'; }).join(''); var syncBtn = IS_PAID ? ('
' + '' + '
' + 'Also stores an encrypted copy on the server, ' + 'restorable on any device with your PIN. Only you can decrypt ' + 'it — losing the PIN means losing the backup.' + '
' + '
') : ('
' + '' + '
' + 'Encrypted cloud backup is available on the paid tier.' + '
' + '
'); previewEl.innerHTML = '
' + '
' + '▸ Preview: ' + esc(pie.pie_name || 'pie') + '' + '
' + '
' + '
Positions
' + (pie.positions || []).length + '
' + '
Invested
' + fmt(t.invested) + '
' + '
Value
' + fmt(t.value) + '
' + '
Result
' + signed(t.result) + '
' + '
' + warnings + (rows ? '
' + '' + '' + '' + '' + '' + '' + '' + '' + rows + '' + '
TickerNameQtyAvgInvested
' + '
' : '' ) + '
' + '
' + '' + '
' + 'Saved to this browser only. No server-side copy of your holdings.' + '
' + '
' + syncBtn + '
' + '' + '
' + '
' + '
'; previewEl.hidden = false; document.getElementById('commit-local').addEventListener('click', commitLocal); document.getElementById('commit-cancel').addEventListener('click', resetUploader); var syncEl = document.getElementById('commit-sync'); if (syncEl) syncEl.addEventListener('click', commitSync); } function commitLocal() { if (!currentPie) return; P.savePie(currentPie); showSuccess('▸ Imported to this browser.', 'Pie kept locally; no server-side copy.'); currentPie = null; } async function commitSync() { if (!currentPie) return; // Save locally first so the cloud-sync flow uses the freshly-imported // pie (the enable-PIN modal in this same page reads from localStorage). P.savePie(currentPie); var S = window.CassandraSync; if (!S) { showError('Cloud sync module not loaded.'); return; } var status; try { status = await S.getStatus(); } catch (e) { showError('Could not check sync status: ' + (e.message || e)); return; } if (!status.paid) { showError('Cloud sync requires the paid tier.'); return; } if (status.exists) { // Already enabled — try a direct push using the cached session // key. If no key is cached (fresh browser session), this throws, // and we fall back to the enable-PIN modal so the user can // re-enter their PIN. try { await S.pushSync(currentPie, null); showSuccess('▸ Imported and synced.', 'Encrypted copy updated on the server.'); currentPie = null; if (window.cassandraRefreshSyncStatus) window.cassandraRefreshSyncStatus(); return; } catch (e) { // Fall through to modal so the user can re-auth with their PIN. console.warn('direct push failed, falling back to PIN modal', e); } } // !status.exists OR cached-key push failed → use the modal. if (window.cassandraOpenSyncModal) { window.cassandraOpenSyncModal({ onSuccess: function () { showSuccess('▸ Imported and synced.', 'Cloud sync is now enabled and the pie is stored encrypted.'); currentPie = null; }, }); } else { showError('Cloud sync UI unavailable on this page. ' + 'Use the Cloud sync section below to enable.'); } } function resetUploader() { currentPie = null; previewEl.hidden = true; previewEl.innerHTML = ''; resultEl.hidden = true; filenameEl.textContent = ''; fileInput.value = ''; } async function parseFile(file) { filenameEl.textContent = file.name + ' (' + Math.round(file.size / 1024) + ' KB) — parsing…'; previewEl.hidden = true; resultEl.hidden = true; try { var pie = await P.parseCsv(file); renderPreview(pie); filenameEl.textContent = file.name + ' (' + Math.round(file.size / 1024) + ' KB)'; } catch (e) { filenameEl.textContent = file.name + ' (failed)'; showError(e.message || 'Unknown error'); } } browseLink.addEventListener('click', function (e) { e.preventDefault(); fileInput.click(); }); fileInput.addEventListener('change', function () { if (fileInput.files[0]) parseFile(fileInput.files[0]); }); ['dragenter', 'dragover'].forEach(function (ev) { dropZone.addEventListener(ev, function (e) { e.preventDefault(); e.stopPropagation(); dropZone.classList.add('dz--over'); }); }); ['dragleave', 'drop'].forEach(function (ev) { dropZone.addEventListener(ev, function (e) { e.preventDefault(); e.stopPropagation(); dropZone.classList.remove('dz--over'); }); }); dropZone.addEventListener('drop', function (e) { var f = e.dataTransfer.files && e.dataTransfer.files[0]; if (f) parseFile(f); }); dropZone.addEventListener('click', function (e) { if (e.target.tagName !== 'A') fileInput.click(); }); }); })();