Files
fuj-management/go/internal/web/templates/adults.tmpl
Jan Novak c85748b3aa
All checks were successful
Deploy to K8s / deploy (push) Successful in 8s
feat(go): M6.2 — adults page (table, filters, credits/debts/unmatched, Pay buttons)
- Extract AssembleAdults(ctx) from ServeAdults so HTML and JSON API share one reconcile path.
- HTMLHandler gains *api.Handler; ServeAdults loads real data and renders adults.tmpl.
- AdultsPageData view model + qrHref/qrHrefAll funcMap (URL-encode /qr params, YYYY-MM→MM/YYYY).
- adults.tmpl: full reconcile table, per-cell status classes + cell-unpaid-current, Pay button hrefs,
  totals row, credits/debts/unmatched sections, filter controls, sheet links.
- static/js/filters.js: NFD-normalize name filter + month-range column hiding; future months hidden by default.
- TestAdultsPage asserts member name and cell text against fixture data.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 01:09:47 +02:00

138 lines
4.1 KiB
Cheetah
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "title"}}Adults{{end}}
{{define "content"}}
<h1>Adults Dashboard</h1>
{{if .Error}}
<p class="description error-banner">Error loading data: {{.Error}}</p>
{{else}}
<div class="filter-container" id="filterContainer" data-current-month="{{.Data.CurrentMonth}}">
<div class="filter-item">
<label class="filter-label" for="nameFilter">Member</label>
<input id="nameFilter" class="filter-input" type="text" placeholder="Filter by name…">
</div>
<div class="filter-item">
<label class="filter-label" for="fromMonth">From</label>
<select id="fromMonth" class="filter-select">
<option value="">All</option>
{{range $i, $m := .Data.Months}}
<option value="{{$i}}">{{$m}}</option>
{{end}}
</select>
</div>
<div class="filter-item">
<label class="filter-label" for="toMonth">To</label>
<select id="toMonth" class="filter-select">
<option value="">All</option>
{{range $i, $m := .Data.Months}}
<option value="{{$i}}">{{$m}}</option>
{{end}}
</select>
</div>
<div class="filter-item">
<button id="applyFilter" class="filter-btn">Apply</button>
<button id="clearFilter" class="filter-btn">All</button>
</div>
</div>
{{if .Data.Results}}
<div class="table-wrapper">
<table class="reconcile-table">
<thead>
<tr>
<th>Member</th>
{{range $i, $m := .Data.Months}}
<th data-month-idx="{{$i}}" data-raw-month="{{index $.Data.RawMonths $i}}">{{$m}}</th>
{{end}}
<th>Balance</th>
<th></th>
</tr>
</thead>
<tbody>
{{range $row := .Data.Results}}
<tr class="member-row" data-name="{{$row.Name}}">
<td class="member-name">{{$row.Name}}</td>
{{range $i, $cell := $row.Months}}
<td class="cell cell-{{$cell.Status}}{{if and (or (eq $cell.Status "unpaid") (eq $cell.Status "partial")) (ge $cell.RawMonth $.Data.CurrentMonth)}} cell-unpaid-current{{end}}{{if $cell.Overridden}} cell-overridden{{end}}"
data-month-idx="{{$i}}" title="{{$cell.Tooltip}}">
{{$cell.Text}}
{{if and (or (eq $cell.Status "unpaid") (eq $cell.Status "partial")) (lt $cell.RawMonth $.Data.CurrentMonth)}}
<a class="pay-btn" href="{{qrHref $.Data.BankAccount $cell.Amount $row.Name $cell.RawMonth}}">Pay</a>
{{end}}
</td>
{{end}}
<td class="balance-cell{{if lt $row.Balance 0}} balance-negative{{end}}">{{$row.Balance}} CZK</td>
<td class="payall-cell">
{{if gt $row.PayableAmount 0}}
<a class="pay-btn pay-all-btn" href="{{qrHrefAll $.Data.BankAccount $row.PayableAmount $row.Name $row.RawUnpaidPeriods}}">Pay All</a>
{{end}}
</td>
</tr>
{{end}}
<tr class="totals-row">
<td><strong>TOTAL</strong></td>
{{range $i, $t := .Data.Totals}}
<td class="total-cell total-cell-{{$t.Status}}" data-month-idx="{{$i}}" data-raw-month="{{index $.Data.RawMonths $i}}">{{$t.Text}}</td>
{{end}}
<td colspan="2"></td>
</tr>
</tbody>
</table>
</div>
{{else}}
<p class="description">No members found.</p>
{{end}}
{{if .Data.Credits}}
<h2 class="section-header">Credits</h2>
<ul class="credits-list">
{{range .Data.Credits}}
<li>{{.Name}}: <strong>+{{.Amount}} CZK</strong></li>
{{end}}
</ul>
{{end}}
{{if .Data.Debts}}
<h2 class="section-header">Debts</h2>
<ul class="debts-list">
{{range .Data.Debts}}
<li>{{.Name}}: <strong>{{.Amount}} CZK</strong></li>
{{end}}
</ul>
{{end}}
{{if .Data.Unmatched}}
<h2 class="unmatched-header section-header">Unmatched Transactions</h2>
<table class="unmatched-table">
<thead>
<tr>
<th>Date</th>
<th>Amount</th>
<th>Sender</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{{range .Data.Unmatched}}
<tr class="unmatched-row">
<td>{{.Date}}</td>
<td>{{printf "%.0f" .Amount}} CZK</td>
<td>{{.Sender}}</td>
<td>{{.Message}}</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
<p class="sheet-links">
<a href="{{.Data.AttendanceURL}}" target="_blank" rel="noopener">[Attendance sheet]</a>
&middot;
<a href="{{.Data.PaymentsURL}}" target="_blank" rel="noopener">[Payments sheet]</a>
</p>
{{end}}
<script src="/static/js/filters.js" defer></script>
{{end}}