// ── My Portfolio view ────────────────────────────────────────────────── // Left panel: portfolio composition (add/remove/search + dates + start). // Right panel: empty by default; on "Start update" shows a support-contact box. const { useState: useStateP, useEffect: useEffectP, useRef: useRefP } = React; function PortfolioView({ tweaks, appPortfolio, setView }) { const today = new Date().toISOString().slice(0, 10); const now = new Date(); const hh = String(now.getHours()).padStart(2, "0"); const mm = String(now.getMinutes()).padStart(2, "0"); // portfolio: array of {entity_id, entity_name, kg_ticker} objects (loaded from API) const [portfolio, setPortfolio] = useStateP(appPortfolio || []); const [portfolioLoaded, setPortfolioLoaded] = useStateP(appPortfolio !== null); const [allCandidates, setAllCandidates] = useStateP([]); const [search, setSearch] = useStateP(""); const [showResults, setShowResults] = useStateP(false); const [updateDate, setUpdateDate] = useStateP(today); const [updateTime, setUpdateTime] = useStateP(`${hh}:${mm}`); const [showSupport, setShowSupport] = useStateP(false); const publicMode = window.DATA?.publicMode === true; const searchRef = useRefP(null); // Load portfolio and universe candidates on mount useEffectP(() => { if (!appPortfolio) { fetch("/api/frontend/portfolio") .then(r => r.json()) .then(data => { setPortfolio(data.portfolio || []); setPortfolioLoaded(true); }) .catch(() => setPortfolioLoaded(true)); } fetch("/api/frontend/portfolio/candidates") .then(r => r.json()) .then(data => setAllCandidates(data.candidates || [])) .catch(() => {}); }, []); // Close search dropdown on outside click useEffectP(() => { function handler(e) { if (searchRef.current && !searchRef.current.contains(e.target)) setShowResults(false); } document.addEventListener("mousedown", handler); return () => document.removeEventListener("mousedown", handler); }, []); const portfolioIds = new Set(portfolio.map(p => p.entity_id)); const portfolioCompanies = portfolio; // full objects from API const searchLower = search.trim().toLowerCase(); const searchResults = searchLower ? allCandidates .filter(c => !portfolioIds.has(c.id) && (c.name.toLowerCase().includes(searchLower) || (c.ticker || "").toLowerCase().includes(searchLower)) ) .slice(0, 8) : []; function addCompany(id) { setSearch(""); setShowResults(false); if (publicMode) { setShowSupport(true); return; } const candidate = allCandidates.find(c => c.id === id); fetch("/api/frontend/portfolio", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ entity_id: id, entity_name: candidate?.name, kg_ticker: candidate?.ticker }), }) .then(r => r.json()) .then(() => fetch("/api/frontend/portfolio").then(r => r.json()).then(d => setPortfolio(d.portfolio || []))) .catch(() => {}); } function removeCompany(id) { if (publicMode) { setShowSupport(true); return; } fetch(`/api/frontend/portfolio/${encodeURIComponent(id)}`, { method: "DELETE" }) .then(() => setPortfolio(prev => prev.filter(p => p.entity_id !== id))) .catch(() => {}); } function handleStart() { if (publicMode) { setShowSupport(true); return; } if (setView) setView("scan"); } return (
{/* LEFT: portfolio composition + controls */} {/* RIGHT: portfolio companies list → support box on Start */}
{!showSupport ? (
Holdings

{portfolioCompanies.length === 0 ? <>Your portfolio is empty. : <>{portfolioCompanies.length} {portfolioCompanies.length === 1 ? "company" : "companies"} tracked}

{portfolioCompanies.length === 0 ? "Search on the left to add your first company." : "Briefs are generated each morning for everything in this list. Remove any name to stop tracking."}

{portfolioCompanies.length > 0 ? (
    {portfolioCompanies.map(p => (
  • {p.kg_ticker || "PRIVATE"} {p.entity_name}
  • ))}
) : (
Your portfolio is empty. Search on the left to add your first company.
)}
) : (

Contact support to enable portfolio updates.

support@bigdata.com
)}
); } window.PortfolioView = PortfolioView;