fix(display): default from-selector to last N months; keep all months selectable
All checks were successful
Deploy to K8s / deploy (push) Successful in 12s
All checks were successful
Deploy to K8s / deploy (push) Successful in 12s
Instead of hiding older months entirely, show all months in the from/to selectors but default the from-select to the last MONTHS_TO_SHOW months on page load. The "All" button resets to full history as before. Python: passes months_to_show to render_template, IIFE sets fromSelect.value. Go: adds MonthsToShow to response structs, data-months-to-show attr in templates, filters.js reads it and defaults fromSelect after hideFutureMonths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
18
app.py
18
app.py
@@ -33,12 +33,6 @@ from cache_utils import get_sheet_modified_time, read_cache, write_cache, _LAST_
|
||||
from sync_fio_to_sheets import sync_to_sheets
|
||||
from infer_payments import infer_payments
|
||||
|
||||
|
||||
def _last_n_months(months):
|
||||
"""Return the last MONTHS_TO_SHOW months; 0 means show all."""
|
||||
return months[-MONTHS_TO_SHOW:] if MONTHS_TO_SHOW > 0 else months
|
||||
|
||||
|
||||
def get_cached_data(cache_key, sheet_id, fetch_func, *args, serialize=None, deserialize=None, **kwargs):
|
||||
mod_time = get_sheet_modified_time(cache_key)
|
||||
if mod_time:
|
||||
@@ -181,7 +175,7 @@ def api_adults():
|
||||
)
|
||||
result = reconcile(members, sorted_months, transactions, exceptions)
|
||||
vm = build_adults_view_model(
|
||||
members, _last_n_months(sorted_months), result, transactions,
|
||||
members, sorted_months, result, transactions,
|
||||
datetime.now().strftime("%Y-%m"),
|
||||
attendance_url=attendance_url, payments_url=payments_url, bank_account=BANK_ACCOUNT,
|
||||
)
|
||||
@@ -205,7 +199,7 @@ def api_juniors():
|
||||
adapted_members = adapt_junior_members(junior_members)
|
||||
result = reconcile(adapted_members, sorted_months, transactions, exceptions)
|
||||
vm = build_juniors_view_model(
|
||||
junior_members, adapted_members, _last_n_months(sorted_months), result, transactions,
|
||||
junior_members, adapted_members, sorted_months, result, transactions,
|
||||
datetime.now().strftime("%Y-%m"),
|
||||
attendance_url=attendance_url, payments_url=payments_url, bank_account=BANK_ACCOUNT,
|
||||
)
|
||||
@@ -254,14 +248,14 @@ def adults_view():
|
||||
record_step("reconcile")
|
||||
|
||||
vm = build_adults_view_model(
|
||||
members, _last_n_months(sorted_months), result, transactions,
|
||||
members, sorted_months, result, transactions,
|
||||
datetime.now().strftime("%Y-%m"),
|
||||
attendance_url=attendance_url,
|
||||
payments_url=payments_url,
|
||||
bank_account=BANK_ACCOUNT,
|
||||
)
|
||||
record_step("process_data")
|
||||
return render_template("adults.html", **vm)
|
||||
return render_template("adults.html", months_to_show=MONTHS_TO_SHOW, **vm)
|
||||
|
||||
@app.route("/juniors")
|
||||
def juniors_view():
|
||||
@@ -290,14 +284,14 @@ def juniors_view():
|
||||
record_step("reconcile")
|
||||
|
||||
vm = build_juniors_view_model(
|
||||
junior_members, adapted_members, _last_n_months(sorted_months), result, transactions,
|
||||
junior_members, adapted_members, sorted_months, result, transactions,
|
||||
datetime.now().strftime("%Y-%m"),
|
||||
attendance_url=attendance_url,
|
||||
payments_url=payments_url,
|
||||
bank_account=BANK_ACCOUNT,
|
||||
)
|
||||
record_step("process_data")
|
||||
return render_template("juniors.html", **vm)
|
||||
return render_template("juniors.html", months_to_show=MONTHS_TO_SHOW, **vm)
|
||||
|
||||
@app.route("/payments")
|
||||
def payments():
|
||||
|
||||
@@ -39,4 +39,5 @@ type AdultsResponse struct {
|
||||
PaymentsURL string `json:"payments_url"`
|
||||
BankAccount string `json:"bank_account"`
|
||||
CurrentMonth string `json:"current_month"`
|
||||
MonthsToShow int `json:"months_to_show"`
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@ func buildAdultsResponse(
|
||||
PaymentsURL: "https://docs.google.com/spreadsheets/d/" + config.PaymentsSheetID + "/edit",
|
||||
BankAccount: cfg.QRAccount,
|
||||
CurrentMonth: currentMonth,
|
||||
MonthsToShow: cfg.MonthsToShow,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,7 @@ func buildJuniorsResponse(
|
||||
PaymentsURL: "https://docs.google.com/spreadsheets/d/" + config.PaymentsSheetID + "/edit",
|
||||
BankAccount: cfg.QRAccount,
|
||||
CurrentMonth: currentMonth,
|
||||
MonthsToShow: cfg.MonthsToShow,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ func (h *Handler) AssembleAdults(ctx context.Context) (AdultsResponse, error) {
|
||||
return AdultsResponse{}, err
|
||||
}
|
||||
result := domreconcile.Reconcile(members, sortedMonths, txns, exceptions, time.Now().Year())
|
||||
return buildAdultsResponse(members, lastNMonths(sortedMonths, h.Config.MonthsToShow), result, txns, h.Config, time.Now().Format("2006-01")), nil
|
||||
return buildAdultsResponse(members, sortedMonths, result, txns, h.Config, time.Now().Format("2006-01")), nil
|
||||
}
|
||||
|
||||
// ServeJuniors handles GET /api/juniors.
|
||||
@@ -74,16 +74,7 @@ func (h *Handler) AssembleJuniors(ctx context.Context) (JuniorsResponse, error)
|
||||
return JuniorsResponse{}, err
|
||||
}
|
||||
result := domreconcile.Reconcile(members, sortedMonths, txns, exceptions, time.Now().Year())
|
||||
return buildJuniorsResponse(members, lastNMonths(sortedMonths, h.Config.MonthsToShow), result, txns, h.Config, time.Now().Format("2006-01")), nil
|
||||
}
|
||||
|
||||
// lastNMonths returns the last n elements of months.
|
||||
// If n <= 0 or n >= len(months), the full slice is returned unchanged.
|
||||
func lastNMonths(months []string, n int) []string {
|
||||
if n > 0 && len(months) > n {
|
||||
return months[len(months)-n:]
|
||||
}
|
||||
return months
|
||||
return buildJuniorsResponse(members, sortedMonths, result, txns, h.Config, time.Now().Format("2006-01")), nil
|
||||
}
|
||||
|
||||
// ServePayments handles GET /api/payments.
|
||||
|
||||
@@ -38,4 +38,5 @@ type JuniorsResponse struct {
|
||||
PaymentsURL string `json:"payments_url"`
|
||||
BankAccount string `json:"bank_account"`
|
||||
CurrentMonth string `json:"current_month"`
|
||||
MonthsToShow int `json:"months_to_show"`
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
const container = document.getElementById('filterContainer');
|
||||
if (!container) return;
|
||||
|
||||
const currentMonth = container.dataset.currentMonth || '';
|
||||
const currentMonth = container.dataset.currentMonth || '';
|
||||
const monthsToShow = parseInt(container.dataset.monthsToShow || '0', 10);
|
||||
|
||||
const nameInput = document.getElementById('nameFilter');
|
||||
const fromSelect = document.getElementById('fromMonth');
|
||||
@@ -88,4 +89,10 @@
|
||||
// ── Initialise ────────────────────────────────────────────────────────────
|
||||
|
||||
hideFutureMonths();
|
||||
// Default the from-select to show only the last N months.
|
||||
if (monthsToShow > 0 && toSelect.value !== '') {
|
||||
const defaultFrom = Math.max(0, parseInt(toSelect.value, 10) - monthsToShow + 1);
|
||||
fromSelect.value = String(defaultFrom);
|
||||
applyMonthFilter();
|
||||
}
|
||||
}());
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<a href="{{.Data.PaymentsURL}}" target="_blank" rel="noopener">Payments Ledger</a>
|
||||
</div>
|
||||
|
||||
<div class="filter-container" id="filterContainer" data-current-month="{{.Data.CurrentMonth}}" data-page="adults" data-bank-account="{{.Data.BankAccount}}">
|
||||
<div class="filter-container" id="filterContainer" data-current-month="{{.Data.CurrentMonth}}" data-page="adults" data-bank-account="{{.Data.BankAccount}}" data-months-to-show="{{.Data.MonthsToShow}}">
|
||||
<div class="filter-item">
|
||||
<label class="filter-label" for="nameFilter">Member</label>
|
||||
<input id="nameFilter" class="filter-input" type="text" placeholder="Filter by name…">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<a href="{{.Data.PaymentsURL}}" target="_blank" rel="noopener">Payments Ledger</a>
|
||||
</div>
|
||||
|
||||
<div class="filter-container" id="filterContainer" data-current-month="{{.Data.CurrentMonth}}" data-page="juniors" data-bank-account="{{.Data.BankAccount}}">
|
||||
<div class="filter-container" id="filterContainer" data-current-month="{{.Data.CurrentMonth}}" data-page="juniors" data-bank-account="{{.Data.BankAccount}}" data-months-to-show="{{.Data.MonthsToShow}}">
|
||||
<div class="filter-item">
|
||||
<label class="filter-label" for="nameFilter">Member</label>
|
||||
<input id="nameFilter" class="filter-input" type="text" placeholder="Filter by name…">
|
||||
|
||||
@@ -143,6 +143,9 @@
|
||||
},
|
||||
"current_month": {
|
||||
"type": "string"
|
||||
},
|
||||
"months_to_show": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -161,7 +164,8 @@
|
||||
"attendance_url",
|
||||
"payments_url",
|
||||
"bank_account",
|
||||
"current_month"
|
||||
"current_month",
|
||||
"months_to_show"
|
||||
]
|
||||
},
|
||||
"Credit": {
|
||||
|
||||
@@ -187,6 +187,9 @@
|
||||
},
|
||||
"current_month": {
|
||||
"type": "string"
|
||||
},
|
||||
"months_to_show": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -205,7 +208,8 @@
|
||||
"attendance_url",
|
||||
"payments_url",
|
||||
"bank_account",
|
||||
"current_month"
|
||||
"current_month",
|
||||
"months_to_show"
|
||||
]
|
||||
},
|
||||
"MemberOtherEntry": {
|
||||
|
||||
@@ -1045,6 +1045,7 @@
|
||||
});
|
||||
|
||||
toSelect.value = maxMonthIdx;
|
||||
fromSelect.value = Math.max(0, maxMonthIdx - {{ months_to_show }} + 1);
|
||||
applyMonthFilter();
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -1026,6 +1026,7 @@
|
||||
});
|
||||
|
||||
toSelect.value = maxMonthIdx;
|
||||
fromSelect.value = Math.max(0, maxMonthIdx - {{ months_to_show }} + 1);
|
||||
applyMonthFilter();
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user