Skip to content

feat(schedule): shared filter bottom sheet#205

Open
chiptus wants to merge 28 commits into
mainfrom
claude/188-195-filter-sheet
Open

feat(schedule): shared filter bottom sheet#205
chiptus wants to merge 28 commits into
mainfrom
claude/188-195-filter-sheet

Conversation

@chiptus

@chiptus chiptus commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Moves day/time/stage filters into one bottom sheet (ScheduleFilterSheet) shared by both Schedule views, replacing the List view's old inline expanding panel. The Filters trigger now sits inline in the Timeline toolbar row, with an active-filter count badge.

Stacked on #193's branch (day-jump toolbar) and also carries #190's commits (shared filterScheduleDays, PR #198) until that merges — the filter-sheet work is the last commit on this branch.

Verification

  • Open the Timeline view; the Filters trigger sits in the toolbar row next to the day-jump buttons, never on its own line.
  • Open the List view; its filter row opens the same sheet.
  • Pick a day/time/stage in the sheet; both views show the same filtered results, and the badge count updates on both.
  • Apply a day filter; the Timeline strip collapses to that day without scrolling the viewport.
  • Clear filters from the sheet; the badge disappears and both views reset.
  • pnpm run lint, pnpm run typecheck, pnpm vitest run all pass.

Closes #195


Generated by Claude Code

claude added 11 commits July 14, 2026 18:19
Timeline and ListSchedule each had their own inline day/time/stage
filter predicates. Extract filterScheduleDays into src/lib/scheduleFilter.ts
so both views share one pure, unit-tested implementation, and remove
a stray debug console.log from ListSchedule in the process.

Closes #190

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
Extract timeToOffset/offsetToTime as the single place the 2px/minute
timeline scale lives; TimeScale and set positioning now consume them
instead of re-deriving the 2/120 constants.

Closes #191
differenceInMinutes truncates to whole minutes, so timeToOffset and
offsetToTime did not round-trip for sub-minute moments. Base the
conversion on epoch milliseconds; output is unchanged for
whole-minute inputs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
Add a scrollTo search param that captures the moment centered in the
Timeline viewport. A pure resolveTimelineMountMoment precedence
function (scrollTo -> day filter -> festival start) decides where to
center on mount; useTimelineScrollSync owns that one-time centering
plus debounced (~300ms), 5-minute-rounded, history-replace writes on
user scroll. useTimelineUrlState now selects its filter params with
structural sharing so scrollTo writes don't recompute the filtered
schedule.

Closes #192

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
Sticky toolbar above the Timeline strip with one button per festival
day (weekday + date, e.g. "Thu 13"). Tapping a day writes scrollTo and
smooth-scrolls to that day's first set, via a new jumpTo(moment) on
useTimelineScrollSync. When a day filter is active, only that day's
button renders.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
Position-based suppression of programmatic scroll events (a browser may
fire more than one per scrollLeft write), and the back-restore e2e half
now skips explicitly instead of silently passing when no set link is
rendered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
When a day jump clamps at the strip start (first festival day), the
requested moment is unreachable and the debounced post-scroll write
would silently replace scrollTo with a different value. Write the
clamped center moment up front so the URL never drifts; e2e now
asserts write stability and covers the first-day clamp.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
Move day/time/stage filters into a single ScheduleFilterSheet (shadcn
bottom sheet) rendered by both Timeline and List views, replacing the
List view's old inline expanding panel. The Filters trigger joins the
Timeline toolbar row inline, with an active-filter count badge. Filter
state stays shared URL state via useTimelineUrlState, applied through
filterScheduleDays on both views; opening/applying/clearing filters
never scrolls the viewport.

Closes #195
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@vercel

vercel Bot commented Jul 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
upline Ready Ready Preview, Comment Jul 18, 2026 12:30pm

claude added 11 commits July 15, 2026 19:33
Also drops a redundant schema comment, per review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
The browser clamps scrollLeft to the scrollable range, so the
suppression ref must record the read-back value, not the requested
target, for the scroll handler to recognize the mount event.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
Per review: shorter docblocks on the scroll-sync hook, toolbar, and
day-jump helper; JSX section comments removed; test helpers moved
below the cases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
Add a scrollTo search param that captures the moment centered in the
Timeline viewport. A pure resolveTimelineMountMoment precedence
function (scrollTo -> day filter -> festival start) decides where to
center on mount; useTimelineScrollSync owns that one-time centering
plus debounced (~300ms), 5-minute-rounded, history-replace writes on
user scroll. useTimelineUrlState now selects its filter params with
structural sharing so scrollTo writes don't recompute the filtered
schedule.

Closes #192

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
Position-based suppression of programmatic scroll events (a browser may
fire more than one per scrollLeft write), and the back-restore e2e half
now skips explicitly instead of silently passing when no set link is
rendered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
Also drops a redundant schema comment, per review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
The browser clamps scrollLeft to the scrollable range, so the
suppression ref must record the read-back value, not the requested
target, for the scroll handler to recognize the mount event.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
The rebase onto main brought in #199's removal of the view search
param; the select still read it, failing typecheck.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AziTqr3f12fxALYD6jhrW8
…e' into claude/188-193-day-jump-toolbar

# Conflicts:
#	src/hooks/useTimelineScrollSync.ts
#	src/hooks/useTimelineUrlState.ts
#	src/lib/timelineCalculator.test.ts
#	src/pages/EditionView/tabs/ScheduleTab/horizontal/TimelineContainer.tsx
… into claude/188-195-filter-sheet

# Conflicts:
#	src/lib/scheduleFilter.test.ts
#	src/lib/scheduleFilter.ts
#	src/pages/EditionView/tabs/ScheduleTab/horizontal/TimelineToolbar.tsx
Extracts the repeated clock-install/goto/skip-if-hidden block into
openTimeline(), and turns the set-detail-link skip in the back-navigation
test into an assertion so missing seed data fails loudly instead of
silently dropping that acceptance criterion's coverage.
@chiptus
chiptus force-pushed the claude/188-193-day-jump-toolbar branch 2 times, most recently from 4cfe485 to ff8ecf0 Compare July 19, 2026 05:17
Base automatically changed from claude/188-193-day-jump-toolbar to main July 19, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants