Commit Graph

889 Commits

Author SHA1 Message Date
lukas f64b092012 perf(purchases): select_related(platform) on list_purchases (#68)
Final-review follow-up: LinkedPurchase reads purchase.platform per row;
eager-load it alongside the games prefetch to remove the residual N+1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 14:20:14 +02:00
lukas a4b4adb392 test(sorting): tidy re import + guard tbody match (#68)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 14:17:14 +02:00
lukas 4dae3a1845 test(sorting): default-order regression + all-keys smoke (#68) 2026-06-21 14:15:28 +02:00
lukas 939d3a9e33 feat(purchases): honor ?sort= on list_purchases + eager-load games (#68) 2026-06-21 14:11:45 +02:00
lukas 6c704c40a3 test(sessions): make duration sort test discriminate from default order (#68)
Test was passing for wrong reason: both default order (-date) and -duration sort
put Beta first because Beta had longer duration AND later date. Make order diverge
by swapping durations, so -duration must override date-based ordering to pass. Also
fix test to extract tbody to avoid matching "Beta" in header's last-session button.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 14:07:08 +02:00
lukas 5fc9cc03da feat(sessions): honor ?sort= on list_sessions + eager-load relations (#68) 2026-06-21 13:58:33 +02:00
lukas fb985fac9e test(games): make list_games sort test discriminate from default order (#68) 2026-06-21 13:56:45 +02:00
lukas 0fa1697310 feat(games): honor ?sort= on list_games + eager-load platform (#68) 2026-06-21 13:53:00 +02:00
lukas 65d9f29a3e test(sorting): drop unused imports/var in test_sorting (#68) 2026-06-21 13:50:45 +02:00
lukas 5ccf388ef5 feat(sorting): per-model maps, apply_sort, parse_find_filter (#68) 2026-06-21 13:46:25 +02:00
lukas 6459bb983e feat(sorting): SortSpec/SortTerm types + parse_sort_terms (#68) 2026-06-21 13:43:11 +02:00
lukas e19f0ee63b docs(sorting): implementation plan for list-view sort param (#68)
Six TDD tasks: sorting.py core types + parse_sort_terms; per-model maps +
apply_sort + parse_find_filter; wire each of the three list views (sort +
N+1 eager-load + unknown-key warning toast); regression smoke. Links new
follow-up #77 (presets persist/restore sort) in spec + plan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 13:40:03 +02:00
lukas 427179b886 docs(claude): name primitive roles with PEP 695 type aliases
Extend the "name compound types" convention to cover bare str/int that
stand for a domain concept (SortKey, AnnotationName, ...). Surfaced while
designing games/sorting.py (#68), where several distinct string roles meet
in one signature.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 13:35:00 +02:00
lukas 74805a7019 docs(sorting): design spec for sort query param on list views (#68)
Honor a signed comma-list ?sort= param on list_games/list_sessions/
list_purchases via a new games/sorting.py (SortSpec + per-model maps +
parse_sort_terms + apply_sort). Backend applies the full multi-term list.
Unknown sort keys are ignored with a user-facing warning toast (never a
400), surfacing #65 link drift without breaking hand-editable URLs.

Named string/compound roles throughout (SortKey, SortString, AnnotationName,
OrderField, SortTerm, SortResult) so signatures say which value goes where.

Includes an N+1 select_related/prefetch_related prereq on the list
querysets. Adjacent follow-ups filed: #73 (header UI), #74 (FindFilter
unify; also owns the unused FindFilter.direction/page/per_page fields),
#75 (purchase search), #76 (shared list_view helper).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 13:35:00 +02:00
lukas 32beec115c Merge pull request #69 from KucharczykL/feat/issue-67-playevent-datecriterion
feat(filters): date-range filtering on PlayEventFilter.started/ended (#67)
2026-06-21 12:47:48 +02:00
lukas 799d500bfc Merge remote-tracking branch 'origin/main' into feat/issue-67-playevent-datecriterion
# Conflicts:
#	common/components/filters.py
#	tests/test_filter_bars.py
2026-06-21 12:35:23 +02:00
lukas c5f957be0d Merge pull request #72 from KucharczykL/feat/playevent-date-filters-ui
feat(filters): surface started/ended date filters on the PlayEvent filter bar
2026-06-21 12:29:02 +02:00
lukas b711f9b845 Merge pull request #71 from KucharczykL/fix/playevent-filter-slider-label
fix(filters): label the Days to Finish slider on the PlayEvent filter bar
2026-06-21 12:27:43 +02:00
lukas 6e7f000e1c feat(filters): surface started/ended date filters on the PlayEvent filter bar
Add Started and Finished DateRangePicker widgets to the PlayEvent filter bar
and wire filter-started / filter-ended into the filter-bar date-range
serializer, so the started/ended DateCriterion fields (added for #67) are
reachable from the UI — enabling "finished in year Y" range filtering.

Builds on #67 (PlayEventFilter.started/ended are DateCriterion); the bare
field names round-trip through _parse_range like the Purchase date fields.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 12:08:27 +02:00
lukas 8ec8ba6c8c fix(filters): label the Days to Finish slider on the PlayEvent filter bar
RangeSlider does not render its own label — the field label is emitted by
the _filter_field wrapper. The PlayEvent filter bar added the Days to Finish
slider bare, so it showed no label. Wrap it in _filter_field like every
other slider (GameFilterBar/PurchaseFilterBar).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 12:05:39 +02:00
lukas ab4dae55eb feat(filters): date-range filtering on PlayEventFilter.started/ended (#67)
Change PlayEventFilter.started/ended from StringCriterion to DateCriterion
so they support GREATER_THAN / LESS_THAN / BETWEEN, enabling
"finished in year Y" to be expressed through the filter system.

PlayEvent.started/ended are DateField columns, so the criteria apply with
bare field names (no __date lookup, unlike SessionFilter.timestamp_start
which is a datetime). This mirrors the existing PurchaseFilter DateField
precedent. Deserialization auto-switches via the field annotation and the
serialized JSON shape is unchanged, so the type change is backward-compatible.

Prerequisite for #65 Tier-2 stats-page filtered links. Part of #61.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 11:47:39 +02:00
lukas 31ff5ae83e Merge pull request #62 from KucharczykL/feat/issue-56-filter-links
feat(filters): programmatic filter links + navbar playtime links (#56)
2026-06-21 10:52:32 +02:00
lukas 2d0b1d67b5 Merge pull request #63 from KucharczykL/fix/staging-comment-on-pr-open
fix(ci): comment staging URL when the PR is opened, not just on deploy
2026-06-21 10:51:25 +02:00
lukas b0b2194806 fix(ci): comment staging URL when the PR is opened, not just on deploy
The staging deploy runs on push to a non-main branch and tries to comment
the staging URL on the branch's PR. When the branch is pushed before the PR
exists (the common case), the comment is skipped and never reappears once the
PR is opened.

Add a pull_request [opened, reopened] trigger and move the comment into its
own job that runs both after a successful push-deploy and on PR open/reopen.
The branch is taken from github.head_ref on PR events and github.ref_name on
push; the existing dedupe-by-body keeps the two paths from double-posting.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RF5L4HtbcykTfY9YUYGds3
2026-06-21 09:26:31 +02:00
lukas 3fd02bbcf9 feat(filters): programmatic filter links + navbar playtime links (#56)
Add filter_url(), a reverse()-style helper that builds a URL to a filtered
list view from a filter object (target inferred from the filter type).

Add OperatorFilter.where(**lookups), a Django-.filter()-style ergonomic
constructor that resolves each field's criterion class from its annotation
(shared with from_json via _criterion_class_for, removing duplication).

Make SessionFilter.timestamp_start/timestamp_end DateCriterion applied via
the __date lookup, so date ranges over the timestamp columns are expressible.

Wire the navbar 'today' / 'last 7 days' totals as links to the matching
filtered session lists, and align the 'last 7 days' total to the same
calendar-day window so the number matches the list it links to.

Stats-table and game-detail links remain a follow-up (see spec).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RF5L4HtbcykTfY9YUYGds3
2026-06-21 08:53:06 +02:00
lukas b9545a780b docs(spec): add OperatorFilter.where() ergonomic constructor
Adds a Django-.filter()-style where(**lookups) classmethod (Component 1b)
to issue #56 spec. Additive: keeps the typed explicit constructor intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RF5L4HtbcykTfY9YUYGds3
2026-06-21 08:38:36 +02:00
lukas 260169b521 docs(spec): programmatic filter links for issue #56
Design for a reverse()-style filter_url() helper plus navbar 'today' /
'last 7 days' links. Stats-table and view_game-table links deferred to a
follow-up issue.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RF5L4HtbcykTfY9YUYGds3
2026-06-21 07:50:25 +02:00
lukas b7d667a07f Merge pull request #60 from KucharczykL/fix/issue-39-dropdown-clipped
Django CI/CD / test (push) Failing after 6m30s
Django CI/CD / build-and-push (push) Has been skipped
fix(dropdown): flip-up menu collapses off-screen (follow-up to #59)
2026-06-20 23:50:32 +02:00
lukas 184749bf6d fix(dropdown): flip-up menu must override top-[105%] class
The flip-up branch cleared inline `top` to "", which let the menu's
`top-[105%]` utility class reassert top:105% on the now-fixed element —
collapsing the menu to a 2px sliver below the viewport, so toggles near the
viewport bottom appeared not to open. Set the unused anchor to "auto" so the
inline value wins over the class. Add an e2e regression for the flip-up path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:47:46 +02:00
lukas 4c1d7a9a93 Merge pull request #59 from KucharczykL/fix/issue-39-dropdown-clipped
fix(dropdown): stop table wrapper clipping the device dropdown (#39)
2026-06-20 23:16:29 +02:00
lukas 3bd14e8c89 fix(dropdown): position menu fixed so it isn't clipped by table wrapper
The device/status dropdown menu is absolutely positioned inside the session
list's overflow-x-auto wrapper. Because overflow-x:auto forces overflow-y:auto,
a menu taller than a short table was clipped (issue #39). Open the menu with
position:fixed anchored to its toggle so it escapes the clipping ancestor,
bound it to the viewport with an internal scroll, flip it up when there is more
room above, and reposition on scroll/resize while open.

Fixes #39.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:14:06 +02:00
lukas 5f3271b3da Merge pull request #58 from KucharczykL/chore/untrack-base-css
chore: untrack generated base.css, build it in test/CI
2026-06-20 23:06:18 +02:00
lukas ccc3db81c7 chore: untrack generated base.css, build it in test/CI paths
base.css is a Tailwind build artifact already listed in .gitignore, but it
had been tracked since it was first committed (gitignore can't untrack an
already-tracked file), so it kept getting re-committed against intent. Untrack
it to match js/dist (the TS artifact, also gitignored + untracked).

Because nothing in the test path rebuilt it, e2e/static serving relied on the
committed copy. Add 'css' (and 'ts') as prerequisites of the test/test-e2e
make targets and a Build CSS step in CI so the stylesheet is generated before
tests run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:03:50 +02:00
lukas fdbd0cdea8 Merge pull request #57 from KucharczykL/feat/issue-53-session-row-fragment
fix(session): rebuild session row fragment via shared builder (#53)
2026-06-20 22:56:51 +02:00
lukas b6f6da309f fix(popover): remove hidden popover from layout to kill phantom scrollbar
Flowbite re-initialises popovers on every htmx swap. A popover hidden via
Tailwind `invisible` (visibility:hidden) still occupies layout, so once
Popper parks it with a transform offset it expands the table's
overflow-x-auto wrapper and a spurious scrollbar appears (horizontal here,
vertical in #40). Add `[&.invisible]:hidden` so the popover is removed from
layout while hidden; Flowbite drops `invisible` on show, restoring display.

Relates to #40. e2e regression covers no-overflow-after-swap plus
popover-still-shows-on-hover.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 22:39:49 +02:00
lukas 8637c547e4 refactor(session): address review minors for issue #53
- SessionRowData.cell_data: list[Node | str] (date cells are str)
- strengthen test_session_row_fragment_via_htmx to assert OOB navbar

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:37:44 +02:00
lukas ec6423cba5 style(session): apply ruff format to issue #53 changes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:33:25 +02:00
lukas 93252350bb test(e2e): in-place session-row finish swap
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:31:23 +02:00
lukas 4a3e40ef29 feat(session): in-place row swap for finish/reset with OOB navbar
Delete stale _session_row_fragment; end_session and reset_session_start
return the canonical row plus an OOB navbar-playtime fragment. Clone keeps
HX-Refresh since it changes row count. Fixes #53.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 21:27:03 +02:00
lukas 7d10884db7 feat(layout): extract NavbarPlaytime as OOB-swappable component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 21:21:14 +02:00
lukas ba1849e80e refactor(session): extract canonical session_row_data builder
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:17:21 +02:00
lukas 796753e3c9 docs: implementation plan for issue #53
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:14:39 +02:00
lukas 7a3b275d2f docs: return Node not SafeText in issue #53 spec
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:14:39 +02:00
lukas 644b9944da docs: design spec for issue #53 rebuild session row fragment
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:14:39 +02:00
lukas bda0657a6b Merge pull request #54 from KucharczykL/feat/issue-33-reset-session-start
feat(session): reset running session start time to now (#33)
2026-06-20 20:48:19 +02:00
lukas 2a1585831f feat(session): reset running session start time to now from list
Adds a confirm-gated button on running sessions in the session list that
sets timestamp_start to now (issue #33). The htmx path returns HX-Refresh;
ButtonGroup gains optional hx_confirm/hx_swap keys.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 20:38:48 +02:00
lukas bf60a2a06b docs: design spec for issue #33 reset session start to now
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 20:37:52 +02:00
lukas 7d17986e68 Merge pull request #52 from KucharczykL/feat/issue-31-submit-create-session
feat(game): add Submit & Create Session button to add-game form
2026-06-20 20:10:02 +02:00
lukas 3fbb0996e5 test(e2e): add Submit & Create Session redirect test for add-game form 2026-06-20 20:04:54 +02:00
lukas 8005e97f01 fix(session): use get_object_or_404 for game lookup in add_session 2026-06-20 19:59:17 +02:00