.vbw-planning/milestones/ui-fixes-and-smart-scraping/phases/03-dashboard-pagination/02-PLAN.md
Replace the in-memory grouping in UpcomingFetchSchedule with ActiveRecord scope-based bucket queries. Add per-bucket pagination using the existing Paginator (using has_next_page/has_previous_page -- not total_count, which is plan 01's work). Wrap each bucket in a Turbo Frame for independent pagination.
What: Replace the current approach (load ALL active sources, iterate in Ruby to assign to groups) with per-bucket AR queries using WHERE next_fetch_at range conditions.
Files to modify:
lib/source_monitor/dashboard/upcoming_fetch_schedule.rbImplementation details:
INTERVAL_DEFINITIONS and Group struct (add page, has_next_page, has_previous_page fields to Group)build_groups, scheduled_sources, unscheduled_sources, definition_for, minutes_until, sort_sources methodsbuild_groups that iterates INTERVAL_DEFINITIONS, builds an AR scope per bucket, paginates each:
scope.where(next_fetch_at: reference_time..(reference_time + 30.minutes))scope.where(next_fetch_at: (reference_time + 30.minutes)..(reference_time + 60.minutes))scope.where(next_fetch_at: (reference_time + 240.minutes)..) + scope.where(next_fetch_at: nil) combined via .or()pages parameter (hash of { bucket_key => page_number }) in initializer, default {}Paginator.new(scope: bucket_scope.order(:next_fetch_at, :name), page: pages[key] || 1, per_page: per_page) where per_page defaults to 10sources, page, has_next_page, has_previous_page.exists? to avoid loading data for empty buckets)Acceptance criteria:
page, has_next_page, has_previous_page fieldsgroups method returns only non-empty groupsWhat: Modify the dashboard controller to extract per-bucket page params from the request and pass them to UpcomingFetchSchedule.
Files to modify:
app/controllers/source_monitor/dashboard_controller.rbImplementation details:
schedule_pages from params: params.fetch(:schedule_pages, {}).permit!.to_hUpcomingFetchSchedule.new(scope: ..., pages: schedule_pages)schedule_pages to the view as @schedule_pages so the partial can build linksAcceptance criteria:
What: Wrap each schedule bucket in a Turbo Frame with a unique ID. Add prev/next pagination controls per bucket. Build inline pagination (simpler than the shared partial since we use has_next/previous without total count).
Files to modify:
app/views/source_monitor/dashboard/_fetch_schedule.html.erbImplementation details:
turbo_frame_tag "source_monitor_schedule_#{group.key}"schedule_pages[bucket_key]=N paramdata-turbo-frame to the bucket's frame ID so only that section reloadsAcceptance criteria:
What: Modify Queries#upcoming_fetch_schedule to accept and forward the pages parameter.
Files to modify:
lib/source_monitor/dashboard/queries.rbImplementation details:
upcoming_fetch_schedule to accept pages: {} keyword argumentUpcomingFetchSchedule.new(scope: ..., pages: pages)[:upcoming_fetch_schedule, pages]Acceptance criteria:
What: Add/update tests for the scope-based schedule with pagination.
Files to create:
test/lib/source_monitor/dashboard/upcoming_fetch_schedule_test.rbTest cases:
test "groups sources into correct time buckets using AR scopes" -- Create sources with different next_fetch_at values, verify they land in correct bucketstest "hides empty buckets" -- Create sources only in one bucket, verify other buckets are absent from groupstest "paginates within a bucket" -- Create 15 sources in one bucket (per_page 10), verify page 1 has 10, page 2 has 5test "includes unscheduled sources in the 240+ bucket" -- Source with nil next_fetch_at lands in last buckettest "respects per-bucket page params" -- Pass pages: { "0-30" => 2 }, verify second page loadsAcceptance criteria:
This plan modifies:
lib/source_monitor/dashboard/upcoming_fetch_schedule.rblib/source_monitor/dashboard/queries.rbapp/controllers/source_monitor/dashboard_controller.rbapp/views/source_monitor/dashboard/_fetch_schedule.html.erbtest/lib/source_monitor/dashboard/upcoming_fetch_schedule_test.rb (NEW)No overlap with Plan 01 (paginator lib, shared partial, application_helper) or Plan 04 (stats query, stats partial).