124 lines
5.3 KiB
HTML
124 lines
5.3 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}{{ title }}{% endblock %}
|
|
{% block page_title %}{{ title }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="row justify-content-center">
|
|
<div class="col-12 col-md-8 col-lg-6">
|
|
|
|
<!-- Holding mini-card -->
|
|
<div class="pcard pcard-sm mb-3 d-flex align-items-center gap-3">
|
|
<div style="width:36px;height:36px;border-radius:9px;background:#dbeafe;color:#1e40af;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px;">
|
|
{{ (inv.ticker or inv.asset_name[:2]).upper()[:2] }}
|
|
</div>
|
|
<div>
|
|
<div style="font-size:14px;font-weight:600;">{{ inv.asset_name }}</div>
|
|
<div style="font-size:12px;color:var(--muted);">
|
|
{% if inv.ticker %}<span class="mono">{{ inv.ticker }}</span> · {% endif %}
|
|
{{ inv.shares|float|round(4) }} shares held
|
|
{% if inv.current_price %} · Current: {{ inv.current_price | currency }}{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pcard">
|
|
<form method="POST" novalidate>
|
|
{{ form.hidden_tag() }}
|
|
|
|
<div class="mb-3">
|
|
{{ form.transaction_type.label(class="form-label fw-medium", style="font-size:13px;") }}
|
|
{{ form.transaction_type(class="form-select", id="txnType") }}
|
|
</div>
|
|
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-6">
|
|
{{ form.shares.label(class="form-label fw-medium", style="font-size:13px;") }}
|
|
{{ form.shares(class="form-control" + (" is-invalid" if form.shares.errors else ""),
|
|
placeholder="0.00", id="sharesInput") }}
|
|
{% for e in form.shares.errors %}<div class="invalid-feedback">{{ e }}</div>{% endfor %}
|
|
</div>
|
|
<div class="col-6">
|
|
{{ form.price_per_share.label(class="form-label fw-medium", style="font-size:13px;") }}
|
|
<div class="input-group">
|
|
<span class="input-group-text" style="font-size:12px;">{{ current_user.currency_symbol }}</span>
|
|
{{ form.price_per_share(class="form-control", placeholder="0.0000", id="priceInput") }}
|
|
{% if inv.ticker %}
|
|
<button type="button" class="btn btn-outline-secondary" id="fillPrice" style="font-size:11px;" title="Fill current price">↓</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-6">
|
|
{{ form.fees.label(class="form-label fw-medium", style="font-size:13px;") }}
|
|
<div class="input-group">
|
|
<span class="input-group-text" style="font-size:12px;">{{ current_user.currency_symbol }}</span>
|
|
{{ form.fees(class="form-control", placeholder="0.00") }}
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
{{ form.date.label(class="form-label fw-medium", style="font-size:13px;") }}
|
|
{{ form.date(class="form-control") }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Total preview -->
|
|
<div class="mb-3 p-3" style="background:#f8fafc;border-radius:8px;font-size:13px;">
|
|
<div class="d-flex justify-content-between">
|
|
<span class="text-muted">Estimated Total</span>
|
|
<span class="mono fw-bold" id="totalPreview">{{ current_user.currency_symbol }}0.00</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
{{ form.notes.label(class="form-label fw-medium", style="font-size:13px;") }}
|
|
{{ form.notes(class="form-control", placeholder="Optional notes") }}
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
{{ form.submit(class="btn btn-primary") }}
|
|
<a href="{{ url_for('investments.detail', id=inv.id) }}" class="btn btn-outline-secondary">Cancel</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
const sym = '{{ current_user.currency_symbol }}';
|
|
const sharesInput = document.getElementById('sharesInput');
|
|
const priceInput = document.getElementById('priceInput');
|
|
const totalPreview = document.getElementById('totalPreview');
|
|
|
|
function updateTotal() {
|
|
const s = parseFloat(sharesInput.value) || 0;
|
|
const p = parseFloat(priceInput.value) || 0;
|
|
totalPreview.textContent = sym + (s * p).toLocaleString(undefined, {minimumFractionDigits:2, maximumFractionDigits:2});
|
|
}
|
|
sharesInput.addEventListener('input', updateTotal);
|
|
priceInput.addEventListener('input', updateTotal);
|
|
|
|
{% if inv.ticker %}
|
|
const fillBtn = document.getElementById('fillPrice');
|
|
if (fillBtn) {
|
|
fillBtn.addEventListener('click', function() {
|
|
fillBtn.textContent = '…';
|
|
fetch('/investments/api/price/{{ inv.ticker }}')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.price) {
|
|
priceInput.value = data.price.toFixed(4);
|
|
updateTotal();
|
|
}
|
|
fillBtn.textContent = '↓';
|
|
})
|
|
.catch(() => { fillBtn.textContent = '↓'; });
|
|
});
|
|
}
|
|
{% endif %}
|
|
</script>
|
|
{% endblock %}
|