// home.jsx — Mobile-first home page (real data via /api/*).
// 5 widgets:
//   ① 간단 포트폴리오  ② 보유 주식 가로 스크롤
//   ③ 24h 스케줄 다이얼  ④ 알람 횟수  ⑤ 시스템 자원

const { useState: useHomeState, useMemo: useHomeMemo } = React;

// Fallback rate used only if /api/fx/rates is unavailable. Picked to be
// obviously not-current (low) so the operator notices in case of failure.
const FX_FALLBACK_KRW_PER_USD = 1380;

/* ─── data transforms ───────────────────────────────── */

function _num(s) {
  if (s == null) return 0;
  const v = parseFloat(s);
  return isNaN(v) ? 0 : v;
}

function buildPortfolioView(summary, fxRate, fxFresh) {
  if (!summary || summary.ok === false) {
    return { ok: false, total: 0, totalKRW: 0, kr: { krw: 0, pnl: 0 }, us: { usd: 0, krw: 0, pnl: 0 }, fxNote: null };
  }
  const fx = fxRate || FX_FALLBACK_KRW_PER_USD;
  const krOk = summary.kr && summary.kr.ok !== false;
  const usOk = summary.us && summary.us.ok !== false;
  const krEquityKRW = krOk ? _num(summary.kr.summary?.total_equity_krw) : 0;
  const krPnl       = krOk ? _num(summary.kr.summary?.unrealized_pnl_krw) : 0;
  const usEquityUSD = usOk ? _num(summary.us.summary?.total_equity_usd) : 0;
  const usPnlUSD    = usOk ? _num(summary.us.summary?.unrealized_pnl_usd) : 0;
  const usEquityKRW = usEquityUSD * fx;
  const usPnlKRW    = usPnlUSD * fx;
  const fxNote = fxFresh
    ? `$1 ≈ ${fx.toLocaleString(undefined, { maximumFractionDigits: 2 })}₩ (Toss 실시간)`
    : `$1 ≈ ${fx.toLocaleString(undefined, { maximumFractionDigits: 2 })}₩ (대체값)`;
  return {
    ok: krOk || usOk,
    krOk, usOk,
    total: krEquityKRW + usEquityKRW,
    kr: { krw: krEquityKRW, pnl: krPnl, ok: krOk, err: krOk ? null : summary.kr?.error },
    us: { usd: usEquityUSD, krw: usEquityKRW, pnl: usPnlUSD, pnlKRW: usPnlKRW, ok: usOk, err: usOk ? null : summary.us?.error },
    fxNote,
  };
}

function buildHoldings(positions) {
  if (!positions || positions.ok === false) return [];
  const out = [];
  const list = (side) => {
    const inner = positions[side];
    if (!inner || inner.ok === false) return;
    for (const p of inner.positions || []) {
      const qty = _num(p.qty);
      const avg = _num(p.avg_price);
      const cur = _num(p.current_price);
      const pct = _num(p.pnl_pct);
      out.push({
        sym: p.symbol,
        name: p.name || p.symbol,
        market: p.market || (side === 'kr' ? 'KR' : 'NASDAQ'),
        qty, avg, cur, pct,
        marketValue: _num(p.market_value),
        currency: side === 'kr' ? 'KRW' : 'USD',
      });
    }
  };
  list('kr');
  list('us');
  return out;
}

function buildProblemView(problems) {
  if (!problems || problems.ok === false) return { counts: { CRITICAL: 0, ERROR: 0, WARN: 0, INFO: 0 }, latest: null, total: 0 };
  const counts = { CRITICAL: 0, ERROR: 0, WARN: 0, INFO: 0 };
  for (const it of problems.items || []) {
    const sev = (it.severity || '').toUpperCase();
    if (counts[sev] != null) counts[sev]++;
  }
  const total = counts.CRITICAL + counts.ERROR + counts.WARN;
  const latest = (problems.items || []).find(x => ['CRITICAL', 'ERROR', 'WARN'].includes((x.severity || '').toUpperCase()));
  return { counts, total, latest };
}

function buildResources(system) {
  if (!system || system.ok === false) return null;
  const r = system.resources || {};
  const cpu = r.cpu || {};
  const mem = r.memory || {};
  const disk = system.disk || {};
  return {
    cpu: { pct: cpu.percent, cores: cpu.cores, load1: cpu.load_1m },
    memory: { pct: mem.used_pct, usedMB: mem.used_bytes ? mem.used_bytes / 1e6 : null, totalMB: mem.total_bytes ? mem.total_bytes / 1e6 : null },
    disk: { pct: disk.data_used_pct, freeGB: disk.data_free_bytes ? disk.data_free_bytes / 1e9 : null, totalGB: disk.data_total_bytes ? disk.data_total_bytes / 1e9 : null },
    uptimeSec: system.host?.uptime_seconds,
    processUptimeSec: system.host?.process_uptime_seconds,
  };
}

function fmtUptime(seconds) {
  if (seconds == null || isNaN(seconds)) return '—';
  const s = Math.floor(seconds);
  const d = Math.floor(s / 86400);
  const h = Math.floor((s % 86400) / 3600);
  const m = Math.floor((s % 3600) / 60);
  if (d > 0) return `${d}d ${h}h`;
  if (h > 0) return `${h}h ${m}m`;
  return `${m}m`;
}

/* ─── cron next-occurrence (minimal, covers our 5 expressions) ─── */

function cronNextRun(expr, from) {
  if (!expr || expr === '@reboot') return null;
  const parts = expr.split(/\s+/).slice(0, 5);
  if (parts.length < 5) return null;
  const [mStr, hStr, domStr, monStr, dowStr] = parts;
  const m = parseInt(mStr, 10), h = parseInt(hStr, 10);
  if (isNaN(m) || isNaN(h)) return null;
  const dom = domStr === '*' ? null : parseInt(domStr, 10);
  let dowOk;
  if (dowStr === '*') dowOk = () => true;
  else if (dowStr.includes('-')) {
    const [a, b] = dowStr.split('-').map(x => parseInt(x, 10));
    dowOk = (d) => d >= a && d <= b;
  } else {
    const v = parseInt(dowStr, 10);
    dowOk = (d) => d === v;
  }
  const start = new Date(from);
  for (let i = 0; i < 60; i++) {
    const cand = new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate() + i, h, m, 0, 0));
    if (cand <= from) continue;
    if (dom != null && cand.getUTCDate() !== dom) continue;
    if (!dowOk(cand.getUTCDay())) continue;
    return cand;
  }
  return null;
}

function scheduleKind(label) {
  if (!label) return 'other';
  if (label.includes('KR close')) return 'kr_cycle';
  if (label.includes('US close')) return 'us_cycle';
  if (label.includes('KR open'))  return 'kr_trade';
  if (label.includes('US open'))  return 'us_trade';
  if (label.includes('backtest')) return 'backtest';
  if (label.includes('Dashboard')) return 'dashboard';
  return 'other';
}

const KIND_COLOR = {
  kr_cycle: 'var(--m-kr)', us_cycle: 'var(--m-us)',
  kr_trade: 'var(--m-kr-worker)',  us_trade: 'var(--accent)',
  backtest: 'var(--uncertain)', dashboard: 'var(--text-3)', other: 'var(--text-3)',
};

function buildScheduleJobs(schedule, now) {
  if (!schedule || schedule.ok === false) return { jobs: [], next: null, total: 0 };
  const jobs = [];
  for (const it of schedule.items || []) {
    const kind = scheduleKind(it.label);
    const next = cronNextRun(it.cron_expression, now);
    const lastT = it.last_run_utc ? new Date(it.last_run_utc) : null;
    jobs.push({
      id: it.label,
      name: it.label,
      kind,
      color: KIND_COLOR[kind] || KIND_COLOR.other,
      lastT,
      next,
      cron: it.cron_expression,
    });
  }
  jobs.sort((a, b) => (a.next ? +a.next : Infinity) - (b.next ? +b.next : Infinity));
  const nextJob = jobs.find(j => j.next && j.next > now);

  // "Today" window: 36h forward from start of today
  const today = new Date(now); today.setUTCHours(0, 0, 0, 0);
  const cutoff = new Date(+today + 36 * 3600 * 1000);
  const inWindow = (t) => t && t >= today && t < cutoff;

  const dialJobs = [];
  for (const j of jobs) {
    if (inWindow(j.lastT)) dialJobs.push({ t: j.lastT, label: j.name + ' (완료)', color: j.color, key: j.id + '-last' });
    if (inWindow(j.next))  dialJobs.push({ t: j.next, label: j.name, color: j.color, key: j.id + '-next' });
  }
  dialJobs.sort((a, b) => +a.t - +b.t);

  return { jobs, dialJobs, next: nextJob, total: dialJobs.length };
}

function relUntil(target, from) {
  if (!target) return '—';
  const diff = (+target - +from) / 1000;
  if (diff < 0) return '경과';
  if (diff < 60) return Math.floor(diff) + '초';
  if (diff < 3600) return Math.floor(diff / 60) + '분';
  if (diff < 86400) return Math.floor(diff / 3600) + '시간';
  return Math.floor(diff / 86400) + '일';
}

function fmtAbsKST(d) {
  if (!d) return '—';
  try {
    return new Intl.DateTimeFormat('ko-KR', {
      timeZone: 'Asia/Seoul', month: '2-digit', day: '2-digit',
      hour: '2-digit', minute: '2-digit', hour12: false,
    }).format(d);
  } catch (_) { return d.toISOString().slice(5, 16); }
}

/* ─── ① Hero ───────────────────────────────────────── */
function Hero({ pview, view, onViewChange, onTap, now }) {
  if (!pview.ok) {
    return (
      <div className="hero">
        <div className="hero-top">
          <div className="hero-top-left">
            <div className="hero-label"><span className="hero-label-dot" /><span>총 자산</span></div>
            <div className="hero-sub mono">데이터 없음</div>
          </div>
        </div>
        <div className="empty" style={{ padding: 24 }}>포트폴리오를 불러올 수 없음</div>
      </div>
    );
  }

  const main = view === 'total' ? pview.total
            : view === 'kr'    ? pview.kr.krw
            :                     pview.us.usd;
  const isUsd = view === 'us';
  const pnl  = view === 'total' ? (pview.kr.pnl + pview.us.pnlKRW)
            : view === 'kr'    ? pview.kr.pnl
            :                     pview.us.pnl;
  const pnlBase = view === 'total' ? pview.total - (pview.kr.pnl + pview.us.pnlKRW)
                : view === 'kr'    ? pview.kr.krw - pview.kr.pnl
                :                     pview.us.usd - pview.us.pnl;
  const pnlPct = pnlBase > 0 ? (pnl / pnlBase) * 100 : 0;
  const positive = pnl >= 0;

  return (
    <div className="hero" onClick={(e) => { if (e.target.closest('.seg')) return; onTap?.(); }} style={{ cursor: 'pointer' }}>
      <div className="hero-top">
        <div className="hero-top-left">
          <div className="hero-label">
            <span className="hero-label-dot" />
            <span>{view === 'total' ? '총 자산' : view === 'kr' ? '한국 자산' : '미국 자산'}</span>
          </div>
          <div className="hero-sub mono">{fmtAbsKST(now)} KST · 누적 평가손익</div>
        </div>
        <Segmented
          size="sm" value={view} onChange={(v) => onViewChange(v)}
          options={[
            { value: 'total', label: '통합' },
            { value: 'kr',    label: 'KR' },
            { value: 'us',    label: 'US' },
          ]}
        />
      </div>

      <div className="hero-amount mono tnum">
        <span>{isUsd ? main.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : fmtKRWFull(Math.round(main))}</span>
        <span className="hero-amount-unit">{isUsd ? '달러' : '원'}</span>
      </div>

      <div className="hero-meta">
        <DeltaChip value={pnlPct} size="lg" />
        <span className="mono tnum" style={{ color: positive ? 'var(--buy)' : 'var(--sell)', fontWeight: 600, fontSize: 13 }}>
          {fmtSigned(pnl)}{isUsd ? '$' : '₩'}
        </span>
        <span className="hero-meta-sep">·</span>
        <span className="hero-meta-sub">매수가 대비</span>
      </div>

      <div className="hero-split">
        <div className="hero-split-item">
          <div className="hero-split-head"><MarketBadge market="KR" /> 한국</div>
          <div>
            <span className="hero-split-amount tnum">{fmtKRW(pview.kr.krw)}</span>
            <span className="hero-split-unit">원</span>
          </div>
          <div className="hero-split-meta">
            <span className="mono tnum" style={{ color: pview.kr.pnl >= 0 ? 'var(--buy)' : 'var(--sell)' }}>
              {fmtSigned(pview.kr.pnl)}
            </span>
            {!pview.krOk && <span style={{ color: 'var(--warn)', fontSize: 10 }}>· 오류</span>}
          </div>
        </div>
        <div className="hero-split-item">
          <div className="hero-split-head"><MarketBadge market="NASDAQ" /> 미국</div>
          <div>
            <span className="hero-split-amount tnum">${pview.us.usd.toFixed(2)}</span>
            <span className="hero-split-unit">USD</span>
          </div>
          <div className="hero-split-meta">
            <span className="mono tnum" style={{ color: pview.us.pnl >= 0 ? 'var(--buy)' : 'var(--sell)' }}>
              {fmtSigned(pview.us.pnl, 2)}$
            </span>
            <span style={{ color: 'var(--text-4)', fontSize: 10 }}>≈ {fmtKRW(pview.us.krw)}원</span>
          </div>
        </div>
      </div>

      <div className="hero-sub mono" style={{ marginTop: 12, color: 'var(--text-4)', fontSize: 10.5 }}>
        {pview.fxNote}
      </div>
    </div>
  );
}

/* ─── ② Holdings Strip ─────────────────────────────── */
function HoldingsStrip({ holdings, onTap }) {
  if (!holdings || holdings.length === 0) {
    return (
      <div className="card holdings-card">
        <div className="card-head">
          <div className="card-title">보유 주식 <span className="card-title-sub">/portfolio.holdings · 0개</span></div>
        </div>
        <div className="empty" style={{ padding: 20 }}>보유 종목 없음</div>
      </div>
    );
  }
  return (
    <div className="card holdings-card">
      <div className="card-head">
        <div className="card-title">보유 주식 <span className="card-title-sub">/portfolio.holdings · {holdings.length}개</span></div>
        <button className="subtle-link" onClick={() => onTap?.()}>전체 →</button>
      </div>
      <div className="hstrip" role="list">
        {holdings.map(h => {
          const positive = h.pct >= 0;
          const c = positive ? 'var(--buy)' : 'var(--sell)';
          const evalNative = h.qty * h.cur;
          // synthetic sparkline derived from pct
          const series = (() => {
            const start = h.cur / (1 + h.pct / 100 || 1);
            const arr = [];
            for (let i = 0; i < 30; i++) {
              const p = i / 29;
              const noise = Math.sin(i * 0.5) * 0.012 + Math.sin(i * 0.21) * 0.008;
              arr.push(start * (1 + (h.pct / 100) * p + noise));
            }
            return arr;
          })();
          return (
            <button key={`${h.market}:${h.sym}`} className="hstrip-card" role="listitem" onClick={() => onTap?.(h)}>
              <div className="hstrip-head">
                <MarketBadge market={h.market} />
                <span className="mono" style={{ fontSize: 10, color: 'var(--text-3)', marginLeft: 'auto' }}>{h.sym}</span>
              </div>
              <div className="hstrip-name">{h.name}</div>
              <div className="hstrip-spark">
                <Sparkline data={series} fullWidth h={32} color={c} fill={c} strokeWidth={1.5} />
              </div>
              <div className="hstrip-foot">
                <div className="hstrip-pct mono tnum" style={{ color: c }}>
                  <span style={{ fontSize: 10, marginRight: 2 }}>{positive ? '▲' : '▼'}</span>
                  {positive ? '+' : ''}{h.pct.toFixed(2)}%
                </div>
                <div className="hstrip-val mono tnum">
                  {h.currency === 'KRW' ? fmtKRW(evalNative) + '원' : '$' + evalNative.toFixed(2)}
                </div>
              </div>
            </button>
          );
        })}
      </div>
    </div>
  );
}

/* ─── ③ Schedule Dial ──────────────────────────────── */
function ScheduleDial({ schedView, now, onTap }) {
  return (
    <div className="card schedule-card" onClick={() => onTap?.()} style={{ cursor: 'pointer' }}>
      <div className="card-head">
        <div className="card-title">스케줄 <span className="card-title-sub">/schedule · 오늘</span></div>
        <span className="mono" style={{ fontSize: 10.5, color: 'var(--text-3)' }}>{fmtAbsKST(now).slice(0, 5)}</span>
      </div>

      <div className="sched-dial-layout">
        <div className="sched-dial">
          <ClockDial now={now} jobs={schedView.dialJobs || []} size={180} />
        </div>
        <div className="sched-dial-info">
          <div className="sched-next-label">다음 자동 실행</div>
          {schedView.next ? (
            <>
              <div className="sched-next-count mono">
                <span className="big tnum">{relUntil(schedView.next.next, now)}</span>
                <span className="small">후</span>
              </div>
              <div className="sched-next-name">
                <span className="cal-dot" style={{ background: schedView.next.color }} />
                {schedView.next.name}
              </div>
              <div className="sched-next-time mono">{fmtAbsKST(schedView.next.next)} KST</div>
            </>
          ) : (
            <div className="empty" style={{ marginTop: 10, padding: 16 }}>예약된 잡 없음</div>
          )}

          <div className="sched-next-foot">
            <span className="mono" style={{ fontSize: 10.5, color: 'var(--text-3)' }}>
              총 {schedView.jobs?.length || 0}개 · 오늘 {schedView.total} 실행
            </span>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ─── ④ Alarms (Problems) ──────────────────────────── */
function AlarmsCard({ pv, onTap }) {
  const total = pv.total;
  const clean = total === 0;
  const p = pv.counts;
  const latest = pv.latest;

  return (
    <div className={cx('card alarms-card', clean && 'clean')} onClick={() => onTap?.()} style={{ cursor: 'pointer' }}>
      <div className="card-head">
        <div className="card-title">알람 <span className="card-title-sub">/problems · 미해결</span></div>
        <span className="subtle-link">상세 →</span>
      </div>
      {clean ? (
        <div className="alarms-clean">
          <div className="alarms-clean-glyph">
            <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="var(--success)" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M20 6L9 17l-5-5" />
            </svg>
          </div>
          <div>
            <div className="alarms-clean-title">이상 없음</div>
            <div className="alarms-clean-sub">미해결 ERROR/WARN 없음</div>
          </div>
        </div>
      ) : (
        <>
          <div className="alarms-main">
            <div className="alarms-bignum mono tnum">{total}</div>
            <div className="alarms-bignum-sub">건의 미해결 이슈</div>
            <div className="alarms-breakdown">
              {p.CRITICAL > 0 && <span className="alarms-chip" style={{ '--c': 'var(--critical)' }}><SeverityBadge level="CRITICAL" /><b className="mono tnum">{p.CRITICAL}</b></span>}
              {p.ERROR > 0    && <span className="alarms-chip" style={{ '--c': 'var(--buy)' }}><SeverityBadge level="ERROR" /><b className="mono tnum">{p.ERROR}</b></span>}
              {p.WARN > 0     && <span className="alarms-chip" style={{ '--c': 'var(--warn)' }}><SeverityBadge level="WARN" /><b className="mono tnum">{p.WARN}</b></span>}
              {p.INFO > 0     && <span className="alarms-chip" style={{ '--c': 'var(--info)' }}><SeverityBadge level="INFO" /><b className="mono tnum">{p.INFO}</b></span>}
            </div>
          </div>

          {latest && (
            <div className="alarms-latest">
              <SeverityBadge level={latest.severity} />
              <div className="alarms-latest-text">
                <div className="alarms-latest-msg">{(latest.body || '').split('\n')[0].replace(/^\*\*Message\*\*:\s*/, '').slice(0, 100)}</div>
                <div className="mono" style={{ fontSize: 10, color: 'var(--text-4)' }}>{fmtRelativeTime(latest.timestamp_utc)} · {latest.source}</div>
              </div>
            </div>
          )}
        </>
      )}
    </div>
  );
}

/* ─── ⑤ Resources ──────────────────────────────────── */
function ResourcesCard({ res, onTap }) {
  if (!res) {
    return (
      <div className="card resources-card">
        <div className="card-head">
          <div className="card-title">시스템 자원 <span className="card-title-sub">/system</span></div>
        </div>
        <div className="empty" style={{ padding: 20 }}>시스템 상태 로딩 중…</div>
      </div>
    );
  }
  const toneFor = (pct) => pct >= 80 ? 'var(--critical)' : pct >= 60 ? 'var(--uncertain)' : 'var(--success)';
  const cpuPct = res.cpu.pct == null ? 0 : res.cpu.pct;
  const memPct = res.memory.pct == null ? 0 : res.memory.pct;
  const diskPct = res.disk.pct == null ? 0 : res.disk.pct;
  return (
    <div className="card resources-card" onClick={() => onTap?.()} style={{ cursor: 'pointer' }}>
      <div className="card-head">
        <div className="card-title">시스템 자원 <span className="card-title-sub">/system · uptime {fmtUptime(res.uptimeSec)}</span></div>
        <span className="mono" style={{ fontSize: 10.5, color: 'var(--success)' }}>
          <span className="dotpulse" style={{ background: 'var(--success)', display: 'inline-block', marginRight: 4 }} />
          OK
        </span>
      </div>
      <div className="res-grid">
        <div className="res-cell">
          <GaugeRing pct={cpuPct} color={toneFor(cpuPct)} size={66} thick={5} big label="CPU" />
          <div className="res-cell-meta mono">{res.cpu.cores || '?'}코어 · L1 {res.cpu.load1 != null ? res.cpu.load1.toFixed(2) : '—'}</div>
        </div>
        <div className="res-cell">
          <GaugeRing pct={memPct} color={toneFor(memPct)} size={66} thick={5} big label="RAM" />
          <div className="res-cell-meta mono">
            {res.memory.usedMB != null ? (res.memory.usedMB / 1024).toFixed(1) : '?'} / {res.memory.totalMB != null ? (res.memory.totalMB / 1024).toFixed(0) : '?'} GB
          </div>
        </div>
        <div className="res-cell">
          <GaugeRing pct={diskPct} color={toneFor(diskPct)} size={66} thick={5} big label="DISK" />
          <div className="res-cell-meta mono">
            {res.disk.freeGB != null ? (res.disk.totalGB - res.disk.freeGB).toFixed(1) : '?'} / {res.disk.totalGB != null ? res.disk.totalGB.toFixed(0) : '?'} GB
          </div>
        </div>
      </div>
      <div className="res-trend mono" style={{ fontSize: 9.5, color: 'var(--text-4)', marginTop: 6 }}>
        backend uptime {fmtUptime(res.processUptimeSec)}
      </div>
    </div>
  );
}

/* ─── Home ─────────────────────────────────────────── */
function Home({ onNavigate }) {
  const [heroView, setHeroView] = useHomeState('total');
  const now = useHomeMemo(() => new Date(), []);

  const portfolio = useApi(() => API.portfolio.summary(), [], { refreshMs: 60000 });
  const positions = useApi(() => API.portfolio.positions(), [], { refreshMs: 60000 });
  const problems  = useApi(() => API.problems.list({ limit: 50 }), [], { refreshMs: 60000 });
  const schedule  = useApi(() => API.schedule.read(), [], { refreshMs: 60000 });
  const system    = useApi(() => API.system.state(), [], { refreshMs: 15000 });
  // FX backend caches for 60s; we poll at 5min on the client to avoid noise.
  const fx        = useApi(() => API.fx.rates(),       [], { refreshMs: 300000 });

  const fxRate    = fx.data && fx.data.ok !== false ? Number(fx.data.krw_per_usd) : null;
  const fxFresh   = !!fxRate;
  const pview     = useHomeMemo(() => buildPortfolioView(portfolio.data, fxRate, fxFresh), [portfolio.data, fxRate, fxFresh]);
  const holdings  = useHomeMemo(() => buildHoldings(positions.data), [positions.data]);
  const pv        = useHomeMemo(() => buildProblemView(problems.data), [problems.data]);
  const schedView = useHomeMemo(() => buildScheduleJobs(schedule.data, now), [schedule.data, now]);
  const res       = useHomeMemo(() => buildResources(system.data), [system.data]);

  return (
    <div className="home">
      <Hero pview={pview} view={heroView} onViewChange={setHeroView} onTap={() => onNavigate('portfolio')} now={now} />
      <HoldingsStrip holdings={holdings} onTap={() => onNavigate('portfolio')} />

      <div className="home-row">
        <ScheduleDial schedView={schedView} now={now} onTap={() => onNavigate('schedule')} />
        <AlarmsCard pv={pv} onTap={() => onNavigate('problems')} />
      </div>

      <ResourcesCard res={res} onTap={() => onNavigate('system')} />
    </div>
  );
}

Object.assign(window, { Home });
