Skip to content

[STORY] Self-Monitoring Tab Pagination #344

@jsbattig

Description

@jsbattig

Story: Self-Monitoring Tab Pagination

As an admin user viewing the Self-Monitoring tab
I want to see paginated job history and paginated created issues, each with a selectable page size
So that the UI remains responsive and I can efficiently browse through large lists of jobs and issues without the page becoming unwieldy

Implementation Status:

  • PaginationController JavaScript class added to self_monitoring.html (shared reusable class for both tables)
  • Page-size dropdown (10/20/50/100, default 10) added to Scan History section
  • Pagination controls (prev/next/page indicator/info text) added to Scan History table
  • Page-size dropdown (10/20/50/100, default 10) added to Created Issues section
  • Pagination controls (prev/next/page indicator/info text) added to Created Issues table
  • Unit tests for PaginationController logic covering all algorithm paths
  • E2E manual testing completed by Claude Code

Completion: 0/7 tasks complete (0%)

Algorithm:

PaginationController (reusable class, one instance per table):
  state:
    currentPage = 1
    pageSize = 10           (default)
    allRows = []            (all <tr> elements captured from table body)

  initialize(tableId, dropdownId, controlsId):
    allRows = document.querySelectorAll("#{tableId} tbody tr")
    BIND dropdown onChange event -> onPageSizeChange()
    CALL render()

  onPageSizeChange(newSize):
    pageSize = parseInt(newSize)
    currentPage = 1         (ALWAYS reset to page 1 when size changes)
    CALL render()

  goToPage(page):
    totalPages = ceil(allRows.length / pageSize)
    IF totalPages == 0: totalPages = 1
    IF page < 1: page = 1
    IF page > totalPages: page = totalPages
    currentPage = page
    CALL render()

  render():
    totalPages = ceil(allRows.length / pageSize)
    IF totalPages == 0: totalPages = 1
    startIndex = (currentPage - 1) * pageSize
    endIndex = min(startIndex + pageSize, allRows.length)

    FOR EACH row AT index IN allRows:
      IF index >= startIndex AND index < endIndex:
        row.style.display = ""        (visible)
      ELSE:
        row.style.display = "none"    (hidden)

    UPDATE controls container:
      IF allRows.length == 0:
        info text = "Showing 0 of 0"
      ELSE:
        info text = "Showing {startIndex+1}-{endIndex} of {allRows.length}"
      Prev button: disabled = (currentPage == 1)
      Next button: disabled = (currentPage == totalPages)
      Page indicator text: "{currentPage} / {totalPages}"

Page initialization (runs after DOM ready):
  CREATE scanHistoryPaginator = new PaginationController()
  CALL scanHistoryPaginator.initialize(
    tableId    = "scan-history-table",
    dropdownId = "scan-history-page-size",
    controlsId = "scan-history-controls"
  )

  CREATE createdIssuesPaginator = new PaginationController()
  CALL createdIssuesPaginator.initialize(
    tableId    = "created-issues-table",
    dropdownId = "created-issues-page-size",
    controlsId = "created-issues-controls"
  )

Acceptance Criteria:

# AC1: Default pagination on page load
Given the Self-Monitoring tab has more than 10 scan history entries
When the admin loads the Self-Monitoring page
Then only the first 10 scan history rows are visible
And only the first 10 created issues rows are visible
And both page-size dropdowns show "10" selected
And the scan history pagination info shows "Showing 1-10 of N"
And the created issues pagination info shows "Showing 1-10 of N"

# AC2: Page size selection is independent per table
Given the admin is viewing the Self-Monitoring tab
When the admin selects "50" from the Scan History page-size dropdown
Then 50 scan history rows are visible (or all rows if fewer than 50 exist)
And the scan history pagination info updates to "Showing 1-50 of N"
And the Created Issues section pagination remains unchanged at its current page and size

# AC3: Page navigation - Next
Given the admin is viewing page 1 of scan history with page size 10 and 35 total entries
When the admin clicks the "Next" button in the scan history pagination controls
Then rows 11-20 of scan history are shown
And the scan history pagination info shows "Showing 11-20 of 35"
And the "Prev" button is enabled

# AC4: Page navigation - Prev
Given the admin is on page 2 of scan history with page size 10
When the admin clicks the "Prev" button in the scan history pagination controls
Then page 1 of scan history is shown
And the "Prev" button is disabled

# AC5: Page reset on size change
Given the admin is on page 3 of scan history with page size 10
When the admin changes the scan history page size dropdown to "50"
Then the scan history view resets to page 1
And the pagination info shows "Showing 1-50 of N"

# AC6: Boundary handling - last page
Given scan history has 35 entries and page size is 10
When the admin navigates to page 4 (the last page)
Then rows 31-35 are shown in scan history
And the pagination info shows "Showing 31-35 of 35"
And the "Next" button is disabled

# AC7: Empty state
Given scan history has 0 entries
When the admin loads the Self-Monitoring page
Then no scan history rows are shown
And the scan history pagination controls show "Showing 0 of 0"

Testing Requirements:

  • Unit tests for PaginationController covering all algorithm logic paths:
    • Default page size of 10 on initialization
    • Correct row visibility (startIndex/endIndex window) for first, middle, and last pages
    • Page reset to 1 when page size changes via onPageSizeChange()
    • goToPage() boundary clamping: below 1 clamps to 1, above totalPages clamps to totalPages
    • Correct "Showing X-Y of N" info text on all pages including the last page with a partial set
    • Prev button disabled on page 1, enabled on page 2+
    • Next button disabled on last page, enabled on all earlier pages
    • Empty table state (0 rows) produces "Showing 0 of 0" and does not crash
    • Two independent PaginationController instances do not interfere with each other
  • E2E manual testing via local CIDX server at localhost:8000:
    • Load Self-Monitoring tab, confirm both pagination controls render in the DOM
    • Verify default page size 10 is active: only 10 rows visible per table on load
    • Change page size on Scan History only; verify Created Issues table is unaffected
    • Navigate Next and Prev for both tables; verify correct rows are visible at each step
    • Verify Prev button disabled on page 1 and Next button disabled on last page
    • Navigate to last page with a partial set; verify correct "Showing X-Y of N" text
    • Capture HTML structure and interactive behavior as evidence

Definition of Done:

  • All acceptance criteria (AC1-AC7) satisfied
  • Greater than 90% unit test coverage for PaginationController logic
  • No backend/server-side changes required (implementation is client-side only)
  • Code review approved (tdd-engineer + code-reviewer workflow)
  • Manual end-to-end testing completed by Claude Code with captured evidence
  • No lint errors in modified HTML or JavaScript
  • Both tables paginate independently with no cross-contamination between instances
  • Working software deployable: pagination operates correctly within existing backend limits (SCAN_HISTORY_LIMIT=50, ISSUES_HISTORY_LIMIT=100)

Implementation Notes (codebase facts, not requirements):

  • Template file to modify: src/code_indexer/server/web/templates/self_monitoring.html
  • Backend reference (read-only): src/code_indexer/server/web/routes.py, lines 42-43
  • Current backend limits: SCAN_HISTORY_LIMIT = 50, ISSUES_HISTORY_LIMIT = 100
  • No backend changes required: pagination hides/shows existing DOM rows only
  • UI framework: PicoCSS + inline JavaScript with HTMX present but not used for pagination
  • Table IDs to assign: scan-history-table, created-issues-table
  • Dropdown IDs: scan-history-page-size, created-issues-page-size
  • Controls container IDs: scan-history-controls, created-issues-controls
  • Page size dropdown options: 10, 20, 50, 100 with 10 pre-selected as default

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions