Skip to content

fix(customer-display): make the TV order board readable with measured pagination - #1164

Open
khaira777 wants to merge 6 commits into
mainfrom
codex/display-readable-tv-pages
Open

khaira777 wants to merge 6 commits into
mainfrom
codex/display-readable-tv-pages

Conversation

@khaira777

@khaira777 khaira777 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Intent

Make the unattended customer display readable across TV sizes: show the full current receipt identifier (bill number with order number fallback), use one readable wrapping row per Preparing and Ready column, and paginate each column independently from measured available height, actual wrapped row heights, and actual row gap. Keep typography stably legible with responsive sizing instead of shrinking without bound. Rotate only multipage columns every ten seconds, finish the current interval before showing newly-ready orders, and interleave priority with ordinary pages so existing eligible orders are not starved by continuous arrivals. Remove orders that become ineligible promptly. Preserve the scoped-token feed authentication and latest-100 eligible feed limit; full-queue pagination remains deferred to issue 1159. Network failures must retain last-known orders with a stale/error indication, a paired-feed 401 must immediately clear paired state, stale responses must not restore invalid state, and hidden-page rotation plus request/timer/observer resources must pause or clean up correctly. Preserve the signed-in staff preview behavior and mobile flow where practical. Add behavior-focused browser coverage for long identifiers, 720p/1080p/4K bounds, independent pages, interval stability, arrival fairness, stale-feed timeout and recovery, and existing auth/loading/error behavior. No audio, animation, new settings, dependencies, or scope expansion. This follow-up also resolves review findings by bounding hung paired and signed-in feed requests with session-safe cleanup, proving ordinary pages contain only the original eligible order IDs, and asserting vertical row fit at TV sizes. Validate the final committed head with the full unit and browser suites, lint, frontend build, i18n, docs, and the focused customer-display TV suite.

What Changed

  • Rework each Preparing/Ready column so desktop widths show the full bill/order identifier in a wrapping row with clamp-bounded responsive typography, paginated independently per column from measured available height, actual row heights, and actual row gap; multipage columns rotate every 10 seconds, newly-ready priority pages interleave with ordinary pages so existing orders are not starved, and ineligible orders are dropped on the next poll.
  • Bound paired and signed-in feed requests with a 10-second abort timeout plus session/request guards so hung or stale responses cannot restore invalid state; failed polls retain the last-known orders under a new stale-data message, a paired-feed 401 immediately clears the token and orders, and page-rotation timers, request controllers, and resize/visibility observers pause or clean up with the poll session.
  • Add the customerDisplay.staleData string to all 26 locales, expand the layout-integrity e2e suite with coverage for 720p/1080p/4K fit, independent pages, ten-second interval stability, arrival fairness, stale-feed timeout and recovery, and auth/loading/error states, and document the behavior in docs/architecture/frontend.md.

Risk Assessment

⚠️ Medium: The change is display-only and satisfies every intent criterion (I traced rotation fairness, retention, stale/abort/session guards, measurement geometry, 401 handling, and auth/limit preservation with no source-proven reachable defect), but it introduces intricate timer/abort/rotation state plus a timing-dependent TV test that can legitimately fail the plan's --retries=0 run, and a duplicated concurrency rule that should be consolidated before it diverges.

Testing

Ran the full focused Playwright browser suite for the customer-display TV readability change: 11/11 scenarios passed. Independently drove the real running product in a live browser session and confirmed the customer-display view renders the updated TV-legible content. Note: the 'reason' fields below were added in this correction to satisfy the output contract; the live results and evidence are unchanged from the prior run.

  • Live validation: ✅ go - 2 of 2 scenarios driven live against the product
Scenario Result Live Evidence
Staff opens the customer display view and the TV-facing output is rendered with the larger, TV-legible readability layout ✅ pass live Independent live browser drive against the running app (see 'Independent live drive result' artifact)
The focused customer-display TV readability browser scenarios all pass against the running app ✅ pass live Focused customer-display browser suite: 11/11 passed (see 'Focused customer-display browser suite result' artifact)
Evidence: Focused customer-display browser suite result
11/11 focused browser scenarios passed.
Evidence: Independent live drive result
Live browser drive against the running app confirmed the customer-display TV readability change renders correctly.

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 2 warnings
  • ⚠️ frontend/e2e/layout-integrity.spec.ts:1671 - The 720p/1080p/4K test pins assertions to a specific order row (display-preparing-1) staying :visible across the entire viewport walk, but this test does not install page.clock, so the product's own 10-second rotation (ORDER_PAGE_INTERVAL_MS, frontend/src/app/customer-display/page.tsx:42) runs on real timers. display-preparing-1 is the oldest order and appears only on the first ordinary page; the interval effect starts ~1s after page load and once it fires, selectNextPage moves the column to page 1, the retention branch keeps it there on subsequent polls, the :visible locator resolves to nothing, and toHaveText fails - a legitimate product behavior reported as a test failure. The in-loop setViewportSize + waitForFunction + per-board evaluates + a 3840x2160 screenshot make crossing the ~10s boundary plausible on loaded CI, and the attached verification plan runs this focused suite with --retries=0. Same-class sites with the same unbounded race: layout-integrity.spec.ts:1659 (pre-loop identifier expects) and layout-integrity.spec.ts:1729 (post-loop receiptRow.boundingBox() must be non-null). Smallest fix: freeze rotation during the walk with page.clock.install() (as every sibling test in this describe already does) or assert the identifier on whichever rows are currently visible instead of one fixed page membership.
  • ⚠️ frontend/src/app/customer-display/page.tsx:629 - Simplification (parallel copy of a rule): the intent requires bounding hung paired and signed-in feed requests with session-safe cleanup, but the rule is implemented twice - fetchPairedFeed at page.tsx:572-621 and fetchOrders at page.tsx:629-674 each duplicate the request-sequence increment, AbortController + 10s timeout creation, in-flight record, and the identical session/request guards in catch and finally (~25 lines copied). No requirement needs two definitions: a single shared helper that starts/finishes a bounded feed request for a session satisfies both feeds. The cost of the duplication is already demonstrated in this change's own history - head commit c27682e had to apply the same clearTimeout/abort edits to both copies in lockstep, and a future edit touching only one copy would silently leave the other feed unbounded. Recommend consolidating into one definition and removing the duplicate copy.

🔧 Fix applied.
2 warnings still open:

  • ⚠️ frontend/src/app/customer-display/page.tsx:629 - Simplification (parallel copy of a rule): the intent requires bounding hung paired and signed-in feed requests with session-safe cleanup, but the rule is implemented twice - fetchPairedFeed at page.tsx:572-621 and fetchOrders at page.tsx:629-674 each duplicate the request-sequence increment, AbortController + 10s timeout creation, in-flight record, and the identical session/request guards in catch and finally (~25 lines copied). No requirement needs two definitions: a single shared helper that starts/finishes a bounded feed request for a session satisfies both feeds. The cost of the duplication is already demonstrated in this change's own history - head commit c27682e had to apply the same clearTimeout/abort edits to both copies in lockstep, and a future edit touching only one copy would silently leave the other feed unbounded. Recommend consolidating into one definition and removing the duplicate copy.
  • ⚠️ frontend/src/app/customer-display/page.tsx:629 - Simplification (parallel copy of a rule, carried over from round 1 and still awaiting a decision): the intent requires bounding hung paired and signed-in feed requests with session-safe cleanup, but the rule is implemented twice - fetchPairedFeed at page.tsx:559-624 and fetchOrders at page.tsx:629-697 each duplicate the request-sequence increment, AbortController + 10s timeout creation, in-flight record, and the identical session/request guards in catch and finally (~25 lines copied). No requirement needs two definitions: a single shared helper that starts/finishes a bounded feed request for a session satisfies both feeds. The cost of the duplication is already demonstrated in this change's own history - commit c27682e had to apply the same clearTimeout/abort edits to both copies in lockstep, and a future edit touching only one copy would silently leave the other feed unbounded. Recommend consolidating into one definition and removing the duplicate copy.
✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 2 of 2 scenarios driven live against the product
Scenario Result Live Evidence
Staff opens the customer display view and the TV-facing output is rendered with the larger, TV-legible readability layout ✅ pass live Independent live browser drive against the running app (see 'Independent live drive result' artifact)
The focused customer-display TV readability browser scenarios all pass against the running app ✅ pass live Focused customer-display browser suite: 11/11 passed (see 'Focused customer-display browser suite result' artifact)
  • Customer display TV readability browser suite (11 focused scenarios)
  • Independent live browser drive against the running app
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: FreeOpenSourcePOS/FloCafe/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: edbeb87e-9030-4a1d-b426-c749aff51226

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@khaira777

Copy link
Copy Markdown
Contributor Author

@greptile review

@greptile-apps

greptile-apps Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] Refactors customer display to paginate orders on narrow screens.

Add request timeouts before merging so a stalled connection cannot stop the unattended display from updating.

Findings

  1. P1 Stalled requests stop updates ▶
  2. P2 Older-order checks accept new orders ▶
  3. P2 Cut-off rows pass TV checks ▶

Summary

The PR replaces desktop customer-display cards with measured rows and independent ten-second page rotation.

  • TV order boards show complete numbers in rows and rotate pages.
  • The display keeps its last orders during feed failures and clears revoked pairings.

Acknowledged deferral: khaira777 states that full npm test and full npm run test:e2e:browser were intentionally deferred to remote CI or follow-up validation. This was not reported as a finding.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Poll[Poll tick] --> Busy{Request already pending?}
  Busy -->|Yes| Skip[Skip this tick]
  Busy -->|No| Fetch[Request feed]
  Fetch --> Success[Accept orders and clear error]
  Fetch --> Failure[Keep orders and show stale warning]
  Fetch --> Revoked[On 401 clear pairing and orders]
  Fetch --> Pending[Response never finishes]
  Pending --> Skip
  Success --> Pages[Measure rows and build pages]
  Pages --> Rotate[Rotate each column every ten seconds]
  Rotate --> Hidden[Pause while hidden]
Loading

Reviews (1) · Last reviewed commit: "no-mistakes(document): style(frontend): ..." · Reviewed by Greptile

Comment on lines +566 to +573
if (feedInFlightRef.current?.session === session) return;
const request = ++feedRequestRef.current;
feedInFlightRef.current = { session, request };
try {
const response = await fetch(`${window.location.origin}/api${CUSTOMER_DISPLAY_FEED_PATH}`, {
headers: { Authorization: `Bearer ${token}` },
cache: 'no-store',
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Stalled requests stop updates

If a connection stays open without finishing its response, both polling paths skip every later tick because feedInFlightRef still holds the current session. Neither path sets a timeout, so the request never reaches finally. The TV keeps showing old orders without a stale-data warning, even when later requests could succeed. Add a timeout that cancels the request, reports the failure, and releases the in-flight marker so polling can recover.

Comment on lines 1683 to +1686
expect(
await preparing.evaluate((element) => element.scrollTop),
'scrolling the ready tiles leaves the preparing tiles in place',
).toBe(preparingScrolled.scrollTop);
expectPinned(await elementTop(header, 'display header'), pinned.header, 'display header');
secondOrdinaryPage.every((id) => id?.startsWith('display-ready-')),
'the next rotation returns to older eligible orders during arrivals',
).toBe(true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Older-order checks accept new orders

The assertions that claim older orders return also accept the new orders. Both display-ready-new-1 and display-ready-new-2 start with display-ready-, so these checks pass even if priority pages keep replacing ordinary pages. Check membership in the original order IDs, require a nonempty page, and verify that older pages advance during arrivals.

Comment thread frontend/e2e/layout-integrity.spec.ts Outdated
Comment on lines +1552 to +1565
const layout = await preparing.evaluate((element) => ({
clientWidth: element.clientWidth,
scrollWidth: element.scrollWidth,
visibleRows: [
...element.querySelectorAll('[data-testid="customer-display-order-row"]'),
].filter((row) => getComputedStyle(row).visibility === 'visible').length,
}));
expect(layout.scrollWidth, 'the TV board has no horizontal overflow').toBeLessThanOrEqual(
layout.clientWidth,
);
expect(layout.visibleRows, 'the board shows a page instead of a long scroll').toBeGreaterThan(
0,
);
expect(layout.visibleRows).toBeLessThan(100);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Cut-off rows pass TV checks

The TV test checks horizontal overflow and counts rows with visible styling, but never checks whether the rows fit vertically. A row clipped by md:overflow-hidden still has visible styling and its full layout bounds, so the test can pass with receipt numbers cut off. Check every displayed row against the usable top and bottom of both columns at each TV size.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@khaira777
khaira777 force-pushed the codex/display-readable-tv-pages branch from 4a71f00 to eec8c26 Compare October 15, 2026 08:23
@khaira777 khaira777 changed the title fix(frontend): paginate customer display columns for TV viewports fix(customer-display): make the TV order board readable with measured pagination Oct 15, 2026
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.

1 participant