Skip to content

Page truncated customer and movement tables with the shared usePagedList hook #511

Description

@mforce

Problem

Three SPA tables render one page from a server-paged endpoint with no paging affordance. Rows beyond that page silently disappear:

Surface Call sites Effective cap ORDER BY Consequence
Customer book web/src/routes/CustomersPage.tsx:42 default 100 customer name, then id ascending Alphabetically later customers vanish; a newly created late-sorting customer can disappear after refresh.
Flock bird-movement ledger web/src/routes/FlocksPage.tsx:217, :233 explicit 50; endpoint max 500 date, then id descending The oldest movements vanish after the 50th row, including after recording a new movement.
Inventory item movement ledger web/src/routes/InventoryPage.tsx:147 explicit 100; endpoint max 500 date, created timestamp, then id descending The oldest purchase, usage, and adjustment rows vanish after the 100th movement.

All three datasets can grow without a product-defined bound. The movement ledgers are append-only, so they become more likely to cross the cap over time.

Existing reusable primitive

web/src/components/usePagedList.ts already owns paging, request sequencing, deduplication, load-more state, error recovery, and loaded-window refresh. History, Expenses, Sales, Feed, Water, and Audit already use it.

The customer endpoint already accepts offset; listCustomers currently exposes only limit. Both movement API helpers already expose limit and offset.

Proposed scope

  • Migrate CustomersPage, the Flocks bird ledger, and the Inventory movement ledger to usePagedList.
  • Expose offset from listCustomers.
  • Render the existing translated Load more affordance while another page may exist.
  • Preserve the currently loaded window after create/record/correct mutations instead of collapsing back to page one.
  • Keep each repository's existing stable ordering.

Acceptance criteria

  • Every row remains reachable after the first page on all three surfaces.
  • A full page exposes Load more; a short page withdraws it; an in-flight load cannot be started twice.
  • A failed extension keeps already loaded rows and permits retry; a failed replacement does not leave stale rows under a new context.
  • Recording a bird movement or inventory mutation refreshes all pages the user had loaded.
  • Creating a customer refreshes the loaded customer window without silently hiding already reached rows.
  • Focused frontend tests cover crossing each old cap and loading the next page.
  • No hardcoded English is introduced.

Not in scope

The paged/searchable flock and customer selectors are tracked separately because a native <select> needs a different reusable UI primitive.

Implementation order

This is the first implementation group.

  1. Complete this issue by reusing the existing usePagedList hook for Customers, bird movements, and inventory movements.
  2. Follow with Add a reusable searchable paged entity picker for flock and customer selectors #512 for the larger cross-layer searchable picker work.
  3. Do not implement Five screens silently truncate the flock list at 100, ordered by name #509 separately; Add a reusable searchable paged entity picker for flock and customer selectors #512 is its implementation vehicle and should close both Add a reusable searchable paged entity picker for flock and customer selectors #512 and Five screens silently truncate the flock list at 100, ordered by name #509.

#511 and #512 are technically independent, but this issue goes first because it uses an established primitive and has the smaller design surface.

Related issues

Simulation and E2E fixture

Expand SimulationDataSeeder with deterministic over-cap bands owned by this issue:

  • at least 101 customers, including a named sentinel that sorts after the first 100;
  • at least 51 bird movements for one known flock, including an oldest-page sentinel;
  • at least 101 inventory movements for one known inventory item, including an oldest-page sentinel.

Use the normal handlers/domain paths and deterministic existence probes so a clean rerun converges to AlreadySeeded. Update ComputeExpectedCounts, SimulationManifestCounts/lifecycle metadata as applicable, ValidateCounts, the manifest fingerprint, and SimulationSeedCommandTests; the fixture must continue to certify exact counts rather than a >= floor.

Add focused Playwright coverage that reaches each sentinel through Load more. Keep the fixture names deterministic and verify the existing quick suite still targets intended rows after the customer catalog crosses 100; do not mask a broken write with tolerated statuses.

If new Simulation:* options are introduced instead of structural constants, update SimulationOptions, tools/simulation/bootstrap.sh, docker-compose.sim.yml, and verify-harness.sh in the same change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions