Skip to content

feat: integrate DashboardFlow with standalone PaymentMethod management block#2006

Open
serikjensen wants to merge 3 commits into
mainfrom
migrate-payment-method-card-to-block
Open

feat: integrate DashboardFlow with standalone PaymentMethod management block#2006
serikjensen wants to merge 3 commits into
mainfrom
migrate-payment-method-card-to-block

Conversation

@serikjensen
Copy link
Copy Markdown
Member

@serikjensen serikjensen commented Jun 3, 2026

Summary

The PaymentMethod block already existed under management/ from an earlier migration, but DashboardFlow's JobAndPayView still inlined the Payment card markup and emitted its own un-scoped events. This finishes the migration by wiring the dashboard to consume the standalone pieces (matching the pattern Profile / HomeAddress now use) and carving the shared form screens into per-flow wrappers so each piece is partner-consumable on its own.

What changed

  • Dashboard consumes the block's pieces. JobAndPayView now renders <PaymentMethodCard /> directly; dashboardStateMachine and DashboardComponents listen for the card's / form wrappers' scoped events instead of the previous inlined callbacks. The card and the new form wrappers are public exports (EmployeeManagement.PaymentMethodCard, EmployeeManagement.PaymentMethodBankForm, EmployeeManagement.PaymentMethodSplitForm).
  • Per-flow form wrappers (new pattern). The onboarding BankForm / SplitView are stateful form screens shared with the management flow. Rather than translating events inside contextual adapters or making the forms presentational, this PR adds thin management-side wrappers (PaymentMethodBankForm, PaymentMethodSplitForm) that consume the shared form hooks, own their own i18n namespaces (Employee.Management.PaymentMethodBankForm.json, …SplitForm.json), and emit EMPLOYEE_MANAGEMENT_PAYMENT_METHOD_<BANK|SPLIT>_FORM_SUBMITTED / _CANCELLED. Onboarding stays put — translation overrides decouple by flow.
  • DeleteBankAccountDialog is now presentational. title / description / confirmLabel / cancelLabel are props, supplied per consumer from their own namespace. Same decoupling rationale as the form wrappers.
  • Naming convention swap. Every PaymentMethod-scoped event renamed from EMPLOYEE_PAYMENT_METHOD_MANAGEMENT_* to EMPLOYEE_MANAGEMENT_PAYMENT_METHOD_* (with the card / bank-form / split-form events split out per-component), and the i18n namespace renamed from Employee.PaymentMethod.Management.json to Employee.Management.PaymentMethod.json. Reads as "this is a management-context event for paymentMethod" instead of "this is a paymentMethod event that happens to be management." Pre-existing Employee.HomeAddress.Management.json / Employee.WorkAddress.Management.json files are left as-is and will migrate opportunistically when their owning cards are touched.
  • BoxHeader layout fix. The title column was wrapped in <Flex>, which renders `width: 100%` to support container queries. As a flex item inside .root's space-between, that meant the title and action each wanted 100% and shrank to 50/50, squeezing multi-button header actions. Replaced with a content-sized .titleColumn so the action gets all remaining free space; multi-button actions now wrap in <Flex justifyContent=\"flex-end\">. Card body switched to the edge-to-edge withPadding={!isShowingTable} + DataView isWithinBox pattern Compensation / Deductions / Paystubs already use.
  • + Add another bank account copy fix — the + was duplicated by the button's <PlusCircleIcon /> icon prop. Label is now plain Add another bank account.

Scoped event surface

Event Where it fires
EMPLOYEE_MANAGEMENT_PAYMENT_METHOD_CARD_ADD_REQUESTED PaymentMethodCard Add bank account button
EMPLOYEE_MANAGEMENT_PAYMENT_METHOD_CARD_SPLIT_REQUESTED PaymentMethodCard Split paycheck button
EMPLOYEE_MANAGEMENT_PAYMENT_METHOD_CARD_BANK_ACCOUNT_DELETED PaymentMethodCard row delete confirmation
EMPLOYEE_MANAGEMENT_PAYMENT_METHOD_BANK_FORM_SUBMITTED / _CANCELLED PaymentMethodBankForm save / cancel
EMPLOYEE_MANAGEMENT_PAYMENT_METHOD_SPLIT_FORM_SUBMITTED / _CANCELLED PaymentMethodSplitForm save / cancel
EMPLOYEE_MANAGEMENT_PAYMENT_METHOD_ALERT_DISMISSED Block-rendered success alert dismiss

Test plan

Screen.Recording.2026-06-03.at.4.00.39.PM.mov
Screen.Recording.2026-06-03.at.4.02.36.PM.mov

Made with Cursor

serikjensen and others added 3 commits June 3, 2026 16:07
…t block

The PaymentMethod block existed under management/ already, but DashboardFlow's
JobAndPayView still inlined the card markup and listened for its own un-scoped
events. This wires the dashboard to consume the standalone pieces the same way
the new Profile and HomeAddress migrations do, and finishes the per-piece
decoupling the block needed to be partner-consumable on its own.

What changed:

- Dashboard's JobAndPayView now renders <PaymentMethodCard/> directly; the
  Add / Split / delete flows are owned by the card and the block, not the
  dashboard. dashboardStateMachine and DashboardComponents target the new
  scoped events instead of forwarding inlined callbacks.
- Renamed ListView.tsx -> PaymentMethodCard.tsx and exported it publicly as
  EmployeeManagement.PaymentMethodCard so partners can compose just the card
  on their own routes.
- Carved the shared onboarding BankForm / SplitView usage into per-flow
  wrappers (PaymentMethodBankForm, PaymentMethodSplitForm) under management/.
  Each wrapper consumes the existing shared form hook, owns its own
  Employee.Management.PaymentMethod<Bank|Split>Form i18n namespace, and emits
  EMPLOYEE_MANAGEMENT_PAYMENT_METHOD_<BANK|SPLIT>_FORM_SUBMITTED / _CANCELLED
  events. Onboarding flow keeps its own copies, so partner overrides on one
  side don't bleed into the other.
- DeleteBankAccountDialog is now presentational: title / description /
  confirmLabel / cancelLabel are props, supplied per consumer from their own
  namespace, so the management override surface is decoupled from onboarding.
- Renamed every PaymentMethod-scoped event from
  EMPLOYEE_PAYMENT_METHOD_MANAGEMENT_* to EMPLOYEE_MANAGEMENT_PAYMENT_METHOD_*
  (and split the card / bank-form / split-form events out per-component) so
  the partner-visible vocabulary reads as "this is a management-context event
  for paymentMethod" rather than "this is a paymentMethod event that happens
  to be management."
- Renamed Employee.PaymentMethod.Management.json -> Employee.Management.PaymentMethod.json
  (plus the two new per-form namespaces) to match the new convention. Existing
  HomeAddress / WorkAddress suffix-style files are left as-is and will migrate
  opportunistically.
- BoxHeader: dropped a redundant <Flex> wrapper around the title and added a
  content-sized .titleColumn so the action element sits at its natural width
  on the right edge of the box. Without this the title column hogs 50% of the
  header via Flex's width: 100% .flexContainer, squeezing the action and
  cutting off multi-button headers.
- Partner docs (employee-dashboard.md, employee-management.md) refreshed for
  the new event names, the new exports, and the per-form wrapper composition.
- sdk-app/src/generated-registry-data.ts regenerated for the new exports.

Verification:

- npm run test -- --run -> 2973 passing
- npm run lint:check -> 0 errors

Co-authored-by: Cursor <cursoragent@cursor.com>
…ment-first naming

Fold the patterns learned from the PaymentMethod migration back into the
migrate-dashboard-card-to-block skill and its scout / documenter agents so
the next card migration starts from these defaults.

- New "When the shared piece is a stateful form — wrap, don't share"
  subsection. Documents the per-flow wrapper pattern (PaymentMethodBankForm,
  PaymentMethodSplitForm): when a shared onboarding form is too stateful to
  make presentational, extract its brains into a shared/ hook, then build a
  thin per-flow wrapper that owns its own Employee.Management.<Feature>
  <Component>.json namespace and emits EMPLOYEE_MANAGEMENT_<FEATURE>_
  <COMPONENT>_* events. Frames the wrappers as a real partner export
  alternative to event-translation inside contextual adapters.
- New "Card chrome layout" subsection under "The card component contract".
  Captures three Box / BoxHeader composition rules that aren't obvious from
  the primitives alone: withPadding={!isShowingTable} + DataView isWithinBox
  for edge-to-edge tables (matches Compensation / Deductions / Paystubs);
  multi-button header actions wrapped in <Flex justifyContent="flex-end">
  with an explanation of why (Flex.flexContainer is width: 100%); no glyph
  duplication in icon-button label strings.
- Naming convention: events use EMPLOYEE_MANAGEMENT_<FEATURE>_* (MANAGEMENT
  right after the actor); i18n namespaces use Employee.Management.<Feature>
  (prefix style). Pre-existing HomeAddress / WorkAddress suffix-style files
  are called out as exceptions that migrate opportunistically when their
  owning card is touched.
- Per-card checklist gains two items covering the card-chrome rules and the
  per-flow wrapper requirement, each linking back to the new subsections.
- Scout and documenter agent prompts updated for the same naming
  convention so they recommend the right shape on the next migration.
- Fixed a copy-paste typo on the management-namespace contrast where both
  sides of "rather than" said the same thing.

Co-authored-by: Cursor <cursoragent@cursor.com>
…l names

Review polish on the PaymentMethod management block extraction. Three
related cleanups; no partner-visible behavior change other than the
narrow-container button stacking described below.

Card header layout — keep the fix, drop the BoxHeader change

The previous commit landed a multi-button header layout fix inside the
shared BoxHeader primitive (replaced an inner Flex wrapper with a plain
title column div). That had real risk of regressing every other Box-using
card in the SDK for one feature's benefit. This commit:

- Reverts BoxHeader.tsx and BoxHeader.module.scss to byte-identical with
  origin/main. Zero blast radius outside PaymentMethod.
- Moves the layout fix into a new co-located PaymentMethodCard.module.scss
  with a .headerAction class. The wrapper is display: flex; flex-shrink: 0
  so BoxHeader's existing space-between pins it flush right at content
  width and the inner buttons (Button is flex-shrink: 0 + white-space:
  normal, so its min-content is the longest word, not the full button
  width) don't overflow when the outer flex tries to shrink the wrapper.
- Adds a container-query(40em) upgrade — defaults to column (stacked
  CTAs), upgrades to row + align-items: center at the SDK's standard
  small breakpoint. Reuses the container-query mixin from
  src/styles/_Responsive.scss (auto-injected via Vite's additionalData)
  and the 40em / \$global-breakpoints.small value other modules
  (CompensationHistory, EmployeeTable, TimeOffPolicyDetail) already use.
  Mirrors the responsive contract Flex ships with internally — Flex
  itself can't be used here because its outer .flexContainer is
  width: 100% and competes with the title for header space.
- Swaps the inline style={{ … }} on PaymentMethodCard's header action for
  a className referencing the new module, matching the codebase
  convention of per-card .module.scss files (Profile.module.scss,
  JobAndPayView.module.scss, etc.).

Contextual adapter naming

PaymentMethodComponents.tsx renamed its three contextuals to mirror the
exact name of the leaf component each wraps:

- CardContextual          -> PaymentMethodCardContextual
- BankFormContextual      -> PaymentMethodBankFormContextual
- SplitViewContextual     -> PaymentMethodSplitFormContextual

(The last rename also retires the lingering 'SplitView' legacy term in
the management folder — the wrapped component is PaymentMethodSplitForm.)
PaymentMethod.tsx and paymentMethodStateMachine.ts updated to import the
new names. HomeAddress and Profile still use the unqualified
CardContextual historically and are out of scope for this PR.

Skill + agents

- migrate-dashboard-card-to-block/SKILL.md naming-conventions table now
  recommends '<WrappedComponent>Contextual — mirror the exact name of
  the standalone component the adapter wraps' instead of '<Action>Contextual'.
  The aspirational Compensation example code in the State machine /
  Block file / Contextual adapters sections updated accordingly, with a
  follow-up paragraph noting older blocks still use CardContextual
  historically.
- Card chrome layout section now documents the CSS-module + mobile-first
  container-query recipe end-to-end: why display: flex (not Flex), why
  flex-shrink: 0 (Button white-space + min-content interaction), why a
  container query (narrow-container UX), and why the 40em breakpoint
  (matches \$global-breakpoints.small and existing module convention).
  Migration checklist item rewritten to enforce the full recipe.
- dashboard-card-scout.md and dashboard-block-documenter.md updated to
  use <Feature>CardContextual.

Verification

- npm run test -- --run src/components/Employee/PaymentMethod/management/
  src/components/Common/UI/Box/ src/components/Common/UI/BoxHeader/ -> 31 passing
- Storybook visual check via Playwright at 800px container (row, flush
  right, both buttons fully visible) and 400px container (stacked column).
- npm run lint:check -> 0 errors

Co-authored-by: Cursor <cursoragent@cursor.com>
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