phase-2: extract shared utils
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import MarketContext from '$lib/MarketContext.svelte';
|
||||
import Spinner from '$lib/Spinner.svelte';
|
||||
import { addHolding, removeHolding } from '$lib/api.js';
|
||||
import { sigOrd, fmt, fmtShort, glClass, advClass } from '$lib/utils.js';
|
||||
|
||||
let { data: _data } = $props(); // unused — we load client-side
|
||||
|
||||
@@ -142,8 +143,6 @@
|
||||
let sortCol = $state('ticker');
|
||||
let sortDir = $state(1); // 1 = asc, -1 = desc
|
||||
|
||||
const sigOrd = s => ({'✅ Strong Buy':0,'⚡ Momentum':1,'🔄 Neutral':2,'⚠️ Speculation':3,'❌ Avoid':4})[s] ?? 5;
|
||||
|
||||
function toggleSort(col) {
|
||||
if (sortCol === col) sortDir = sortDir === 1 ? -1 : 1;
|
||||
else { sortCol = col; sortDir = 1; }
|
||||
@@ -172,23 +171,6 @@
|
||||
|
||||
const sortIcon = (col) => sortCol !== col ? '⇅' : sortDir === 1 ? '↑' : '↓';
|
||||
|
||||
const fmt = (n) => n != null
|
||||
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(n)
|
||||
: '—';
|
||||
|
||||
const fmtShort = (n) => n != null
|
||||
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(n)
|
||||
: '—';
|
||||
|
||||
const glClass = (pct) => parseFloat(pct) >= 0 ? 'green' : 'red';
|
||||
|
||||
const advClass = (a) => {
|
||||
if (a?.includes('🟢')) return 'green';
|
||||
if (a?.includes('🟡')) return 'yellow';
|
||||
if (a?.includes('🟠')) return 'orange';
|
||||
if (a?.includes('🔴')) return 'red';
|
||||
return 'gray';
|
||||
};
|
||||
|
||||
const totalValue = $derived(data?.advice?.reduce((s, a) => s + (parseFloat(a.marketValue) || 0), 0) ?? 0);
|
||||
const totalCost = $derived(data?.advice?.reduce((s, a) => s + (a.costBasis ?? 0) * a.shares, 0) ?? 0);
|
||||
@@ -221,20 +203,20 @@
|
||||
<div class="form-title">Add Holding</div>
|
||||
<div class="form-row">
|
||||
<div class="field">
|
||||
<label>Ticker</label>
|
||||
<input bind:value={form.ticker} placeholder="AAPL" maxlength="10" style="text-transform:uppercase" />
|
||||
<label for="form-ticker">Ticker</label>
|
||||
<input id="form-ticker" bind:value={form.ticker} placeholder="AAPL" maxlength="10" style="text-transform:uppercase" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Shares</label>
|
||||
<input bind:value={form.shares} placeholder="10" type="number" min="0" step="any" />
|
||||
<label for="form-shares">Shares</label>
|
||||
<input id="form-shares" bind:value={form.shares} placeholder="10" type="number" min="0" step="any" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Cost Basis / share</label>
|
||||
<input bind:value={form.costBasis} placeholder="150.00" type="number" min="0" step="any" />
|
||||
<label for="form-cost">Cost Basis / share</label>
|
||||
<input id="form-cost" bind:value={form.costBasis} placeholder="150.00" type="number" min="0" step="any" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Type</label>
|
||||
<select bind:value={form.type}>
|
||||
<label for="form-type">Type</label>
|
||||
<select id="form-type" bind:value={form.type}>
|
||||
<option value="stock">Stock</option>
|
||||
<option value="etf">ETF</option>
|
||||
<option value="bond">Bond</option>
|
||||
@@ -242,8 +224,8 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Source</label>
|
||||
<input bind:value={form.source} placeholder="Robinhood" />
|
||||
<label for="form-source">Source</label>
|
||||
<input id="form-source" bind:value={form.source} placeholder="Robinhood" />
|
||||
</div>
|
||||
<button class="btn-save" onclick={submitHolding} disabled={saving}>
|
||||
{saving ? 'Saving…' : 'Save'}
|
||||
@@ -569,11 +551,6 @@
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.field input.readonly {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-cancel-edit {
|
||||
background: transparent;
|
||||
border: 1px solid #2d3f55;
|
||||
|
||||
Reference in New Issue
Block a user