06/03 Optimize codes, Schwab account
This commit is contained in:
@@ -132,6 +132,95 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ── Reusable holdings table macro ──────────────────────────────────────── #}
|
||||
{% macro holdings_table(inv_list) %}
|
||||
<table class="pfm-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="padding-left:20px;">Asset</th>
|
||||
<th class="text-end">Price</th>
|
||||
<th class="text-end d-mob-none">1D Chg</th>
|
||||
<th class="text-end">Value</th>
|
||||
<th class="text-end" style="padding-right:8px;">P&L</th>
|
||||
<th style="width:36px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for inv in inv_list %}
|
||||
<tr id="row-{{ inv.id }}" style="cursor:pointer;"
|
||||
onclick="location.href='{{ url_for('investments.detail', id=inv.id) }}'">
|
||||
<td style="padding-left:20px;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div style="width:28px;height:28px;border-radius:7px;background:{{ asset_colors.get(inv.asset_type,'#94a3b8') }}22;color:{{ asset_colors.get(inv.asset_type,'#94a3b8') }};display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;">
|
||||
{{ (inv.ticker or inv.asset_name[:2]).upper()[:2] }}
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:13px;font-weight:500;">{{ inv.asset_name }}</div>
|
||||
<div style="font-size:11px;color:var(--muted);">
|
||||
{% if inv.ticker %}<span class="mono">{{ inv.ticker }}</span> · {% endif %}
|
||||
{{ asset_labels.get(inv.asset_type, inv.asset_type) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
{% if inv.current_price %}
|
||||
<span class="mono" style="font-size:12px;">{{ inv.current_price | currency }}</span>
|
||||
{% else %}
|
||||
<span class="text-muted" style="font-size:12px;">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end d-mob-none" id="chg-{{ inv.id }}">
|
||||
{% if inv.ticker %}<span class="chg-badge chg-loading">…</span>
|
||||
{% else %}<span class="text-muted" style="font-size:12px;">—</span>{% endif %}
|
||||
</td>
|
||||
<td class="text-end mono" style="font-size:13px;font-weight:600;">{{ inv.current_value | currency }}</td>
|
||||
<td class="text-end" style="padding-right:8px;">
|
||||
<div class="mono {% if inv.unrealized_gain >= 0 %}text-income{% else %}text-expense{% endif %}" style="font-size:12px;font-weight:600;">
|
||||
{% if inv.unrealized_gain >= 0 %}+{% endif %}{{ inv.unrealized_gain | currency }}
|
||||
</div>
|
||||
<div style="font-size:10px;color:var(--muted);">
|
||||
{% if inv.unrealized_gain_pct >= 0 %}+{% endif %}{{ inv.unrealized_gain_pct }}%
|
||||
</div>
|
||||
</td>
|
||||
<td onclick="event.stopPropagation();" style="padding:0 8px;">
|
||||
{% if inv.ticker %}
|
||||
<button class="expand-btn" id="expand-{{ inv.id }}"
|
||||
onclick="toggleChart({{ inv.id }}, '{{ inv.ticker }}', '{{ asset_colors.get(inv.asset_type,'#3b82f6') }}')"
|
||||
title="Show price chart">
|
||||
<i class="bi bi-bar-chart-line" style="font-size:14px;"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if inv.ticker %}
|
||||
<tr class="chart-row" id="chart-row-{{ inv.id }}" style="display:none;">
|
||||
<td colspan="6">
|
||||
<div class="chart-panel" id="chart-panel-{{ inv.id }}">
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||
<span style="font-size:13px;font-weight:600;color:var(--text);">{{ inv.asset_name }}</span>
|
||||
<span class="mono text-muted" style="font-size:11px;">{{ inv.ticker }}</span>
|
||||
<span id="chart-chg-{{ inv.id }}" class="chg-badge chg-flat" style="display:none;"></span>
|
||||
<div class="ms-auto d-flex gap-1 flex-wrap">
|
||||
{% for tf in ['1W','1M','3M','6M','1Y'] %}
|
||||
<button class="tf-btn{% if tf == '1M' %} active{% endif %}"
|
||||
onclick="loadChart({{ inv.id }}, '{{ inv.ticker }}', '{{ tf }}', '{{ asset_colors.get(inv.asset_type,'#3b82f6') }}')">
|
||||
{{ tf }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-meta mt-1" id="chart-info-{{ inv.id }}">Loading…</div>
|
||||
<div class="chart-wrap"><canvas id="canvas-{{ inv.id }}"></canvas></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endmacro %}
|
||||
|
||||
<!-- Chart + Allocation -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-12 col-lg-4">
|
||||
@@ -154,110 +243,45 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{# Per-account balance breakdown #}
|
||||
{% if portfolio.account_groups %}
|
||||
<div class="mt-3 pt-2" style="border-top:1px solid var(--border);">
|
||||
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px;">By Account</div>
|
||||
{% for grp in portfolio.account_groups %}
|
||||
<div class="d-flex justify-content-between py-1">
|
||||
<span style="font-size:12px;">{{ grp.account.name if grp.account else 'Unlinked' }}</span>
|
||||
<span class="mono text-invest" style="font-size:12px;font-weight:600;">{{ grp.total_value | currency }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Holdings table -->
|
||||
<!-- Holdings: one section per account when multi-account, single table otherwise -->
|
||||
<div class="col-12 col-lg-8">
|
||||
{% if portfolio.account_groups %}
|
||||
{% for grp in portfolio.account_groups %}
|
||||
<div class="pcard p-0 {% if not loop.last %}mb-3{% endif %}">
|
||||
<div class="d-flex justify-content-between align-items-center px-4 py-3" style="border-bottom:1px solid var(--border);">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<span class="pcard-title mb-0">{{ grp.account.name if grp.account else 'Unlinked Holdings' }}</span>
|
||||
<span class="mono text-invest" style="font-size:14px;font-weight:700;">{{ grp.total_value | currency }}</span>
|
||||
</div>
|
||||
<span class="text-muted" style="font-size:11px;">{{ grp.investments | length }} holding(s)</span>
|
||||
</div>
|
||||
{{ holdings_table(grp.investments) }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="pcard p-0 h-100">
|
||||
<div class="d-flex justify-content-between align-items-center px-4 py-3" style="border-bottom:1px solid var(--border);">
|
||||
<span class="pcard-title mb-0">Holdings</span>
|
||||
<span class="text-muted" style="font-size:11px;">Click <i class="bi bi-bar-chart-line"></i> to expand price chart</span>
|
||||
</div>
|
||||
<table class="pfm-table" id="holdingsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="padding-left:20px;">Asset</th>
|
||||
<th class="text-end">Price</th>
|
||||
<th class="text-end d-mob-none">1D Chg</th>
|
||||
<th class="text-end">Value</th>
|
||||
<th class="text-end" style="padding-right:8px;">P&L</th>
|
||||
<th style="width:36px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for inv in portfolio.investments %}
|
||||
{# Main holding row #}
|
||||
<tr id="row-{{ inv.id }}" style="cursor:pointer;"
|
||||
onclick="location.href='{{ url_for('investments.detail', id=inv.id) }}'">
|
||||
<td style="padding-left:20px;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div style="width:28px;height:28px;border-radius:7px;background:{{ asset_colors.get(inv.asset_type,'#94a3b8') }}22;color:{{ asset_colors.get(inv.asset_type,'#94a3b8') }};display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;">
|
||||
{{ (inv.ticker or inv.asset_name[:2]).upper()[:2] }}
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:13px;font-weight:500;">{{ inv.asset_name }}</div>
|
||||
<div style="font-size:11px;color:var(--muted);">
|
||||
{% if inv.ticker %}<span class="mono">{{ inv.ticker }}</span> · {% endif %}
|
||||
{{ asset_labels.get(inv.asset_type, inv.asset_type) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
{% if inv.current_price %}
|
||||
<span class="mono" style="font-size:12px;">{{ inv.current_price | currency }}</span>
|
||||
{% else %}
|
||||
<span class="text-muted" style="font-size:12px;">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end d-mob-none" id="chg-{{ inv.id }}">
|
||||
{% if inv.ticker %}
|
||||
<span class="chg-badge chg-loading">…</span>
|
||||
{% else %}
|
||||
<span class="text-muted" style="font-size:12px;">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end mono" style="font-size:13px;font-weight:600;">{{ inv.current_value | currency }}</td>
|
||||
<td class="text-end" style="padding-right:8px;">
|
||||
<div class="mono {% if inv.unrealized_gain >= 0 %}text-income{% else %}text-expense{% endif %}" style="font-size:12px;font-weight:600;">
|
||||
{% if inv.unrealized_gain >= 0 %}+{% endif %}{{ inv.unrealized_gain | currency }}
|
||||
</div>
|
||||
<div style="font-size:10px;color:var(--muted);">
|
||||
{% if inv.unrealized_gain_pct >= 0 %}+{% endif %}{{ inv.unrealized_gain_pct }}%
|
||||
</div>
|
||||
</td>
|
||||
<td onclick="event.stopPropagation();" style="padding:0 8px;">
|
||||
{% if inv.ticker %}
|
||||
<button class="expand-btn" id="expand-{{ inv.id }}"
|
||||
onclick="toggleChart({{ inv.id }}, '{{ inv.ticker }}', '{{ asset_colors.get(inv.asset_type,'#3b82f6') }}')"
|
||||
title="Show price chart">
|
||||
<i class="bi bi-bar-chart-line" style="font-size:14px;"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{# Hidden chart row for this holding #}
|
||||
{% if inv.ticker %}
|
||||
<tr class="chart-row" id="chart-row-{{ inv.id }}" style="display:none;">
|
||||
<td colspan="6">
|
||||
<div class="chart-panel" id="chart-panel-{{ inv.id }}">
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||
<span style="font-size:13px;font-weight:600;color:var(--text);">{{ inv.asset_name }}</span>
|
||||
<span class="mono text-muted" style="font-size:11px;">{{ inv.ticker }}</span>
|
||||
<span id="chart-chg-{{ inv.id }}" class="chg-badge chg-flat" style="display:none;"></span>
|
||||
<div class="ms-auto d-flex gap-1 flex-wrap">
|
||||
{% for tf in ['1W','1M','3M','6M','1Y'] %}
|
||||
<button class="tf-btn{% if tf == '1M' %} active{% endif %}"
|
||||
onclick="loadChart({{ inv.id }}, '{{ inv.ticker }}', '{{ tf }}', '{{ asset_colors.get(inv.asset_type,'#3b82f6') }}')">
|
||||
{{ tf }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-meta mt-1" id="chart-info-{{ inv.id }}">Loading…</div>
|
||||
<div class="chart-wrap">
|
||||
<canvas id="canvas-{{ inv.id }}"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ holdings_table(portfolio.investments) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user