UI — full dark/light theme system: - CSS custom-property token system (--bg, --surface, --accent, --green, --red, etc.) with complete light-mode overrides via [data-theme="light"] - Sticky frosted-glass nav with animated brand icon, theme toggle persisted to localStorage, respects prefers-color-scheme on first visit - Cards with layered shadows, glass backdrop-filter, shimmer accent stripe on value cards - Glowing category color dots, colored P&L badges, budget bars with glow effect - All Chart.js instances use CSS-variable-aware grid/text colours - Scroll-reveal animations and animated money counters on every KPI card - 3-D donut portfolio chart recoloured to match palette; hover lifts the hovered slice - Accounts page shows type emoji icons; delete removes row in-place - Sharing page search dropdown themed with var() colours - Import preview: colour-coded left border on category select driven by category colour - Projections: second KPI card (monthly avg) + pace bars per category Seed data (seed.go): - SeedAdmin() runs in a goroutine at startup; idempotent (skips if transactions exist) - Resolves admin user ID via internal users service GET /admin/users?search=<email> (SEED_USER_EMAIL env var, defaults to admin@homelab.local) - Seeds 4 accounts (CGD Checking, CGD Savings, Visa Credit, Trade Republic) - Seeds 14 categories with colours and monthly budgets - Seeds ~65 realistic Portuguese household transactions spread across 6 months - Seeds 7 ETF buy trades across VWCE, SXR8 (S&P 500), EUNL (MSCI World) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
139 lines
5.5 KiB
HTML
139 lines
5.5 KiB
HTML
{{define "content"}}
|
||
{{$d := .}}
|
||
<h1 style="margin-bottom:24px;">Import</h1>
|
||
|
||
{{if $d.Error}}
|
||
<div class="card" style="border-color:var(--red); background:var(--red-dim);">
|
||
<span style="color:var(--red); font-size:13.5px;">⚠ {{$d.Error}}</span>
|
||
</div>
|
||
{{end}}
|
||
|
||
{{if $d.Preview}}
|
||
<!-- Preview table -->
|
||
<div class="card animate-on-scroll">
|
||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px; flex-wrap:wrap; gap:8px;">
|
||
<div>
|
||
<h2 style="font-size:15px; font-weight:700; color:var(--text); text-transform:none; letter-spacing:0;">Preview</h2>
|
||
<p class="text-muted" style="margin-top:3px;">{{$d.Preview.Total}} rows — review categories before confirming.</p>
|
||
</div>
|
||
<a href="/import" class="btn btn-outline btn-sm">← Back</a>
|
||
</div>
|
||
|
||
<form method="POST" action="/import/confirm">
|
||
<input type="hidden" name="account_id" value="{{$d.Preview.AccountID}}">
|
||
<input type="hidden" name="format" value="{{$d.SelectedFormat}}">
|
||
<input type="hidden" name="raw_data" value="{{$d.RawData}}">
|
||
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Date</th>
|
||
<th>Description</th>
|
||
<th class="text-right">Amount</th>
|
||
<th>Category</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{{range $i, $row := $d.Preview.Rows}}
|
||
<tr>
|
||
<td style="white-space:nowrap; color:var(--text2);">{{$row.Date}}</td>
|
||
<td style="max-width:280px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{$row.Description}}</td>
|
||
<td class="cents {{if lt $row.AmountCents 0}}negative{{else}}positive{{end}}" style="font-weight:600; white-space:nowrap;">
|
||
{{if lt $row.AmountCents 0}}−{{else}}+{{end}}€{{cents (centsAbs $row.AmountCents)}}
|
||
</td>
|
||
<td>
|
||
<select name="categories" class="cat-select" data-selected="{{$row.Category}}"
|
||
style="font-size:12.5px; padding:5px 8px; border:1.5px solid var(--border2);
|
||
border-radius:6px; background:var(--bg2); color:var(--text);
|
||
border-left-width:4px; cursor:pointer; min-width:130px;">
|
||
{{range $cat := $d.Categories}}
|
||
<option value="{{$cat}}" {{if eq $cat $row.Category}}selected{{end}}>{{$cat}}</option>
|
||
{{end}}
|
||
</select>
|
||
</td>
|
||
</tr>
|
||
{{end}}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div style="display:flex; gap:10px; margin-top:18px; flex-wrap:wrap;">
|
||
<button type="submit" class="btn btn-primary">✓ Confirm Import</button>
|
||
<a href="/import" class="btn btn-outline">Cancel</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<script>
|
||
const catColors = { {{range $k,$v := $d.CategoryColors}}"{{$k}}":"{{$v}}",{{end}} };
|
||
document.querySelectorAll('.cat-select').forEach(sel => {
|
||
function paint() {
|
||
const color = catColors[sel.value] || '';
|
||
sel.style.borderLeftColor = color || 'var(--border2)';
|
||
}
|
||
sel.addEventListener('change', paint);
|
||
paint();
|
||
});
|
||
</script>
|
||
|
||
{{else}}
|
||
<!-- Upload forms -->
|
||
<div class="grid-2" style="align-items:start;">
|
||
<div class="card animate-on-scroll">
|
||
<h2 style="font-size:15px; font-weight:700; color:var(--text); text-transform:none; letter-spacing:0; margin-bottom:18px;">
|
||
Bank Transactions
|
||
</h2>
|
||
<form method="POST" action="/import/preview" enctype="multipart/form-data">
|
||
<div class="form-group">
|
||
<label>Account</label>
|
||
<select name="account_id" required>
|
||
<option value="">Select account…</option>
|
||
{{range $d.Accounts}}
|
||
<option value="{{.ID}}">{{.Name}} ({{.Type}})</option>
|
||
{{end}}
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Bank / Format</label>
|
||
<select name="format">
|
||
<option value="cgd">Caixa Geral de Depósitos (CGD)</option>
|
||
<option value="traderepublic">Trade Republic Card</option>
|
||
<option value="generic">Generic CSV</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>CSV File</label>
|
||
<input type="file" name="file" accept=".csv,.txt" required
|
||
style="padding:10px; cursor:pointer; font-size:13px;">
|
||
</div>
|
||
<button type="submit" class="btn btn-primary" style="width:100%;">Preview Import</button>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="card animate-on-scroll">
|
||
<h2 style="font-size:15px; font-weight:700; color:var(--text); text-transform:none; letter-spacing:0; margin-bottom:18px;">
|
||
Securities Trades
|
||
</h2>
|
||
<p class="text-muted" style="margin-bottom:16px; font-size:13px; line-height:1.6;">
|
||
Upload your Trade Republic securities CSV to import buy/sell trades into your portfolio.
|
||
</p>
|
||
<form method="POST" action="/import/securities" enctype="multipart/form-data">
|
||
<div class="form-group">
|
||
<label>Trade Republic Securities CSV</label>
|
||
<input type="file" name="file" accept=".csv,.txt" required
|
||
style="padding:10px; cursor:pointer; font-size:13px;">
|
||
</div>
|
||
<button type="submit" class="btn btn-primary" style="width:100%;">Import Trades</button>
|
||
</form>
|
||
|
||
<div style="margin-top:20px; padding-top:16px; border-top:1px solid var(--border);">
|
||
<p class="text-muted" style="font-size:12px; line-height:1.6;">
|
||
After importing, visit <a href="/portfolio" style="color:var(--accent);">Portfolio</a> to see live prices and P&L.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
{{end}}
|