Problem
PaymentsView (src/components/payments/payments-view.tsx) syncs the drawer with the URL by replacing history:
function openDrawer(id: string) {
setSelectedPaymentId(id);
router.replace(`/payments?paymentId=${id}`, { scroll: false });
}
function closeDrawer() {
setSelectedPaymentId(null);
router.replace("/payments", { scroll: false });
}
Using replace means opening the drawer does not create a history entry. Consequences:
- The browser back button cannot close the drawer: an operator who drilled into a payment and reaches for Back (muscle memory, or the browser's gesture) leaves the payments page entirely instead of closing the drawer — then Back again returns to the payments page with the drawer still open.
- The URL state is not navigable:
?paymentId=X cannot be shared in a way that opens the drawer and allows Back-to-list (replace destroys the list→detail history).
- The deliberate
scroll: false and replace choices show intent, but the interaction model (drawer as a state overlay vs. a navigation) is undocumented and inconsistent with how the app treats other detail states.
Root cause
The drawer was modeled as URL state (replace) rather than a navigation step (push), with no exploration of the back-button semantics.
Why this is architecturally hard
- Switching to
router.push makes Back close the drawer naturally but adds history entries for open/close cycles (a full drawer session pollutes history) — the design must decide between push-with-popstate-handling and replace-with-a-visible-close-affordance.
- The drawer's
onClose (via the PaymentDetailDrawer component) must coordinate with the popstate event so the browser Back and the X button stay in sync — a state-sync pattern (listen for popstate when the drawer is open) that must be tested.
- The same URL-sync choice will be needed by the outages detail drawer/work and the settings panels; the decision should be a documented convention, not per-view.
Proposed design
Either (a) use router.push for open and handle popstate to close on Back, or (b) keep replace and add an explicit, keyboard-accessible close affordance plus a "open in page" link; document the chosen convention and test the back-button behavior (Playwright or unit with mocked router).
Acceptance criteria
Service
Tests
Out of scope
The payments React Query migration (tracked separately).
Getting started
npm test -- payments-view
Good first files to read: src/components/payments/payments-view.tsx, src/components/payments/payment-detail-drawer.tsx.
Problem
PaymentsView(src/components/payments/payments-view.tsx) syncs the drawer with the URL by replacing history:Using
replacemeans opening the drawer does not create a history entry. Consequences:?paymentId=Xcannot be shared in a way that opens the drawer and allows Back-to-list (replace destroys the list→detail history).scroll: falseandreplacechoices show intent, but the interaction model (drawer as a state overlay vs. a navigation) is undocumented and inconsistent with how the app treats other detail states.Root cause
The drawer was modeled as URL state (replace) rather than a navigation step (push), with no exploration of the back-button semantics.
Why this is architecturally hard
router.pushmakes Back close the drawer naturally but adds history entries for open/close cycles (a full drawer session pollutes history) — the design must decide between push-with-popstate-handling and replace-with-a-visible-close-affordance.onClose(via thePaymentDetailDrawercomponent) must coordinate with the popstate event so the browser Back and the X button stay in sync — a state-sync pattern (listen forpopstatewhen the drawer is open) that must be tested.Proposed design
Either (a) use
router.pushfor open and handlepopstateto close on Back, or (b) keepreplaceand add an explicit, keyboard-accessible close affordance plus a "open in page" link; document the chosen convention and test the back-button behavior (Playwright or unit with mocked router).Acceptance criteria
Service
Tests
Out of scope
The payments React Query migration (tracked separately).
Getting started
npm test -- payments-viewGood first files to read:
src/components/payments/payments-view.tsx,src/components/payments/payment-detail-drawer.tsx.