From e618e906ef11fcc5e8a9d0aa475ad342ab7441a8 Mon Sep 17 00:00:00 2001 From: Jan Novak Date: Fri, 8 May 2026 11:12:26 +0200 Subject: [PATCH] fix: restore Sep+Oct adult merge and stop auto-truncating period selector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two regressions made older periods invisible on the adults dashboard: - 1257f0d (Mar 9) commented out ADULT_MERGED_MONTHS, removing the Sep+Oct 2025 merged label. Restored only the 2025-09 → 2025-10 mapping (Dec and Jan are billed separately for adults; the Dec → Jan mapping stays disabled per product decision). Mirrored on the Go side. Test fixtures in sources_test.go now assert Sep dates land in merged 2025-10 instead of 2025-09. - 7774301 (Apr 9) added a JS onload default that set the From selector to maxMonthIdx − 4 and immediately filtered the table, hiding everything older than 5 months on first load. Dropped that default in templates/adults.html and templates/juniors.html so the From-selector starts at the oldest available month. Future months are still removed from the dropdowns and hidden in the table — only the past-month truncation is gone. Note: the live adults attendance sheet had also been pruned to start at 02.12.2025; restoring Sep/Oct/Nov 2025 columns from Sheets version history is required to actually see those periods. Co-Authored-By: Claude Opus 4.7 --- CHANGELOG.md | 7 + ...1052-period-selector-missing-old-months.md | 142 ++++++++++++++++++ go/internal/services/membership/sources.go | 4 +- .../services/membership/sources_test.go | 10 +- scripts/attendance.py | 3 +- templates/adults.html | 2 - templates/juniors.html | 2 - 7 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 docs/plans/2026-05-08-1052-period-selector-missing-old-months.md diff --git a/CHANGELOG.md b/CHANGELOG.md index b27ec29..1303ffa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 2026-05-08 11:11 CEST — fix: period selector showed only Dec 2025+ on adults + +- Restored the `2025-09 → 2025-10` adult merge in `scripts/attendance.py` and `go/internal/services/membership/sources.go` (commented-out by `1257f0d`); the `2025-12 → 2026-01` mapping stays disabled per product decision (Dec and Jan are billed separately for adults). +- Dropped the `defaultFrom = maxMonthIdx − 4` JS auto-default in `templates/adults.html` and `templates/juniors.html` (introduced by `7774301`); the From-selector now starts at the oldest available month so all non-future periods render on first load. Future-month removal is preserved. +- `go/internal/services/membership/sources_test.go`: `TestLoadAdults` / `TestLoadAdults_Fee` now assert that Sep dates land in the merged `2025-10` bucket. +- **Independent of these code changes**: the live adults attendance Google Sheet header had been pruned to start at `02.12.2025` (Sep/Oct/Nov 2025 columns deleted); restoring those columns from Sheets version history is required to actually see those periods on the dashboard. + ## 2026-05-08 10:15 CEST — fix(go): adults template — use lifted CSS classes for visual parity - Use existing `.balance-pos` / `.balance-neg` (drop invented `balance-cell` / `balance-negative`); Pay-All button now lives inside the balance cell with `position: relative` (matches Python; no separate trailing column). diff --git a/docs/plans/2026-05-08-1052-period-selector-missing-old-months.md b/docs/plans/2026-05-08-1052-period-selector-missing-old-months.md new file mode 100644 index 0000000..5a83dad --- /dev/null +++ b/docs/plans/2026-05-08-1052-period-selector-missing-old-months.md @@ -0,0 +1,142 @@ +# Period selector missing older months — diagnosis + +## Context + +User reports the "From / To" period selector on the **adults** dashboard now +shows **Dec 2025** as the oldest available period. The older production +deployment shows Sep+Oct 2025, Nov 2025, Dec 2025+Jan 2026 (merged labels) — +i.e. data going back to September 2025. The user wants to know what went +sideways. Confirmed: the dropdown options on both Python and Go genuinely +start at Dec 2025, not just the default selection — Sep/Oct/Nov 2025 are not +in the list at all. + +## Root cause — the live adults sheet header is missing those columns + +The fresh cache files at `tmp/go/attendance_regular_cache.json` (raw rows from +Google Sheets, modifiedTime `2026-05-06T22:30:02Z`, cached `2026-05-08T00:26`) +contain the actual header row for the adults tab (gid=0): + +```text +['FUJ tréninky úterý 20:30-22:00', '', '', '02.12.2025', '09.12.2025', + '16.12.2025', '06.01.2026', '13.01.2026', '20.01.2026', '27.01.2026', + '03.02.2026', '10.02.2026', '17.02.2026', '24.02.2026', '03.03.2026', + '10.03.2026', '17.03.2026', '23.03.2026', '31.03.2026', '13.04.2026', + '20.04.2026', '27.04.2026', '04.05.2026', '', '', ''] +``` + +**The first date column in the live adults sheet is `02.12.2025` (Dec 2 2025).** +There are no September, October, or November 2025 columns in the header at +all. Both backends parse this faithfully (no slicing, no cutoff anywhere) and +correctly produce `sortedMonths = ["2025-12", "2026-01", …, "2026-05"]`. + +The juniors sheet (different tab, `JUNIOR_SHEET_GID`) is **fine** — its header +still contains `['', 'tier', '', '15.09.2025', '13.10.2025', '20.10.2025', +'03.11.2025', '24.11.2025', '10.11.2025', '17.11.2025', '01.12.2025', …]`. So +the juniors page still shows Sep+Oct / Nov / Dec+Jan correctly. + +So this is a **data issue in the adults attendance Google Sheet**: at some +point between when production's cache was last warmed (showing Sep–Nov) and +2026-05-06, somebody (or some action) removed the columns for September, +October, and November 2025 from the adults tab header. + +The code is doing exactly what it should. There is no parser regression. + +## What to do + +### 1. Restore the missing date columns in the adults attendance sheet + +The fix lives in Google Sheets, not in the codebase. Options, in order of +preference: + +- **(a) Use Sheets version history.** File → Version history → See version + history; find a version from before the columns were dropped (anything + before about Mar 2026 should still have them). Copy the Sep/Oct/Nov 2025 + date column headers and the `TRUE/FALSE` cells underneath them back into + the current sheet. Only restore the 11 missing date columns; do not + full-revert (you'd lose every change since then). +- **(b) Pull from the production server's cache.** The production deployment + evidently still has the older cache, since its dashboard renders those + months. SSH there, copy `tmp/attendance_regular_cache.json`, and you can + reconstruct the per-member Sep/Oct/Nov attendance counts from the + `data[*][2]` map (keys `"2025-09"`, `"2025-10"`, `"2025-11"`). Re-enter + those into the sheet manually as date columns + `TRUE` cells — tedious but + deterministic. +- **(c) Accept the loss.** If the older columns aren't recoverable, the + dashboard correctly reflects what the sheet contains; nothing more to do. + +Which to pick depends on whether those months still need to be billed / +reconciled. + +### 2. Restore `ADULT_MERGED_MONTHS` (user confirmed this was unintentional) + +Independent of the sheet issue: commit `1257f0d` (Mar 9 2026) commented out +the adult merge mappings. Once the Sep/Oct/Nov columns are back in the sheet, +the dashboard would still show them as separate periods instead of the +production-style "Sep+Oct 2025" and "Dec 2025+Jan 2026" merged labels. + +User confirmed this was unintentional. Two files to update: + +- [scripts/attendance.py:32-35](scripts/attendance.py#L32-L35) — uncomment + the two mappings: + + ```python + ADULT_MERGED_MONTHS = { + "2025-12": "2026-01", # keys are merged into values + "2025-09": "2025-10", + } + ``` + +- [go/internal/services/membership/sources.go:30](go/internal/services/membership/sources.go#L30) — mirror the same: + + ```go + var AdultMergedMonths = map[string]string{ + "2025-12": "2026-01", + "2025-09": "2025-10", + } + ``` + +After this change, hit `POST /flush-cache` on each backend so the in-process +post-processed adults cache is rebuilt with the new mapping. + +### 3. (Optional, separate) Fix the JS auto-default that hides older months + +This is **not** the cause of the user's current symptom (that's the sheet +issue), but it will become a UX issue once Sep/Oct/Nov columns are restored: +the Python frontend's `defaultFrom = Math.max(0, maxMonthIdx - 4)` will still +default the From-selector to ~5 months before the latest column on every page +load, hiding restored older months until the user manually picks them. + +- [templates/adults.html:1047](templates/adults.html#L1047) — `var defaultFrom = Math.max(0, maxMonthIdx - 4);` +- [templates/juniors.html:1028](templates/juniors.html#L1028) — same line. + +Drop those four lines (`defaultFrom`, `fromSelect.value = defaultFrom`, +`toSelect.value = maxMonthIdx`, `applyMonthFilter()`) so the page loads with +all non-future months visible — matching the Go side, which only calls +`hideFutureMonths()` and leaves From at its first option. + +Recommend bundling this with step 2 since they touch related UI. + +## Verification + +1. **After step 1** — `POST /flush-cache` on Python and Go backends; reload + `/adults` on each. Confirm the dropdown now lists Sep/Oct/Nov 2025. +2. **After step 2** — reload `/adults`. Confirm the dropdown shows + "Sep+Oct 2025" as a single period and "Dec 2025+Jan 2026" as a single + period. (Still requires the sheet columns to exist.) +3. **After step 3** — reload `/adults` and `/juniors` on Python. Confirm the + table renders all non-future months on first load (Sep 2025 through the + current month) instead of starting at Dec 2025. +4. **Parity check** — `make parity` should report zero diffs between Python + and Go on `/api/adults` and `/api/juniors`. + +## Critical files referenced + +- `tmp/go/attendance_regular_cache.json` — current adults sheet rows + (evidence: header starts at `02.12.2025`). +- `tmp/go/attendance_juniors_cache.json` — current juniors sheet rows + (header still has `15.09.2025`). +- [scripts/attendance.py](scripts/attendance.py) — `ADULT_MERGED_MONTHS` + empty after `1257f0d`; `parse_dates` / `group_by_month` faithful. +- [go/internal/services/membership/sources.go](go/internal/services/membership/sources.go) — Go counterpart, same shape. +- [templates/adults.html](templates/adults.html), [templates/juniors.html](templates/juniors.html) — JS onload `defaultFrom = -4` issue (step 3). +- [go/internal/web/static/js/filters.js](go/internal/web/static/js/filters.js) — Go filter UI (already correct, no changes). diff --git a/go/internal/services/membership/sources.go b/go/internal/services/membership/sources.go index 832a9b2..ca2eebe 100644 --- a/go/internal/services/membership/sources.go +++ b/go/internal/services/membership/sources.go @@ -27,7 +27,9 @@ const ( // AdultMergedMonths mirrors ADULT_MERGED_MONTHS in scripts/attendance.py. // Source month → target month (source attendance accumulated into target). -var AdultMergedMonths = map[string]string{} +var AdultMergedMonths = map[string]string{ + "2025-09": "2025-10", +} // JuniorMergedMonths mirrors JUNIOR_MERGED_MONTHS in scripts/attendance.py. var JuniorMergedMonths = map[string]string{ diff --git a/go/internal/services/membership/sources_test.go b/go/internal/services/membership/sources_test.go index 41b1344..ed12cf1 100644 --- a/go/internal/services/membership/sources_test.go +++ b/go/internal/services/membership/sources_test.go @@ -46,8 +46,8 @@ func TestLoadAdults(t *testing.T) { if err != nil { t.Fatal(err) } - // adultMergedMonths is empty so 2025-09 stays as-is - if len(months) != 1 || months[0] != "2025-09" { + // AdultMergedMonths sends 2025-09 → 2025-10 + if len(months) != 1 || months[0] != "2025-10" { t.Errorf("unexpected months: %v", months) } if len(members) != 2 { @@ -55,7 +55,7 @@ func TestLoadAdults(t *testing.T) { } byName := map[string]int{} for _, m := range members { - byName[m.Name] = m.Fees["2025-09"].Attendance + byName[m.Name] = m.Fees["2025-10"].Attendance } if byName["Alice"] != 2 { t.Errorf("Alice: want 2 sessions, got %d", byName["Alice"]) @@ -73,9 +73,9 @@ func TestLoadAdults_Fee(t *testing.T) { } byName := map[string]int{} for _, m := range members { - byName[m.Name] = m.Fees["2025-09"].Expected + byName[m.Name] = m.Fees["2025-10"].Expected } - // 2 sessions in 2025-09 → AdultFeeMonthlyRate["2025-09"] = 750 + // 2 sessions land in merged 2025-10 → AdultFeeMonthlyRate["2025-10"] = 750 if byName["Alice"] != 750 { t.Errorf("Alice fee: want 750, got %d", byName["Alice"]) } diff --git a/scripts/attendance.py b/scripts/attendance.py index c6886d2..adda417 100644 --- a/scripts/attendance.py +++ b/scripts/attendance.py @@ -30,8 +30,7 @@ JUNIOR_MONTHLY_RATE = { "2026-03": 250 # reduced fee for March 2026 } ADULT_MERGED_MONTHS = { - #"2025-12": "2026-01", # keys are merged into values - #"2025-09": "2025-10" + "2025-09": "2025-10", # keys are merged into values } JUNIOR_MERGED_MONTHS = { diff --git a/templates/adults.html b/templates/adults.html index 7467dd5..d9762b5 100644 --- a/templates/adults.html +++ b/templates/adults.html @@ -1044,8 +1044,6 @@ } }); - var defaultFrom = Math.max(0, maxMonthIdx - 4); - fromSelect.value = defaultFrom; toSelect.value = maxMonthIdx; applyMonthFilter(); })(); diff --git a/templates/juniors.html b/templates/juniors.html index 53076c9..2342d27 100644 --- a/templates/juniors.html +++ b/templates/juniors.html @@ -1025,8 +1025,6 @@ } }); - var defaultFrom = Math.max(0, maxMonthIdx - 4); - fromSelect.value = defaultFrom; toSelect.value = maxMonthIdx; applyMonthFilter(); })(); -- 2.49.1