feat(SDK-1294): add ContractorListFlow - #2694
Conversation
…shboardFlow and onboarding
Adds a hub-and-spoke flow composing ContractorManagement.ContractorList with
DashboardFlow ("View details") and the contractor-onboarding Profile step
("Add contractor" / an onboarding-tab row's "Continue"/"Review"), so a host
app no longer has to hand-roll navigation between these components itself.
The onboarding spoke reuses the exact Profile -> Address -> Payment Method ->
New Hire Report -> Submit states ContractorOnboarding.OnboardingFlow already
defines (extracted into an exported, parameterized
createContractorOnboardingSteps factory in onboardingStateMachine.ts) rather
than mounting OnboardingFlow as a nested component -- nesting would have
produced a second, independent header and made cancel/submit resolve against
OnboardingFlow's own internal list instead of this flow's list.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…to the contractor list
ContractorSubmit fires contractor/submit/done twice for the admin (non-self-
onboarding) path: once immediately after the mutation succeeds (bare
{ message }), and again if the admin clicks "Done" on the SubmitDone
confirmation screen once the onboarding-status query refetches as completed
({ onboardingStatus, message }). OnboardingFlow's own list shows a success
banner on return, so reacting to the first fire is correct there. But
ContractorListFlow's list has no such banner, so the same immediate return
left the admin swept back to the list with zero feedback and without ever
seeing the confirmation screen.
Adds an opt-in waitForExplicitSubmitDone option to the shared
createContractorOnboardingSteps factory: it guards the submit-done
transition to hold on the bare-message fire (letting SubmitDone render and
requiring its "Done" click) while still transitioning immediately on the
self-onboarding invite path, which has no confirmation screen of its own.
OnboardingFlow leaves the option unset, so its existing behavior is
unchanged; ContractorListFlow opts in.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Active-tab row action fired contractor/dismiss but performed no mutation and had no flow to hand the event off to — clicking it looked like it worked but did nothing. Removing it from ManagementContractorList (and thus ContractorListFlow, which reuses it) until a dismissal flow is built under a separate ticket; "Rehire" is left as-is since that's tracked separately. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
| /** @internal */ | ||
| export function DashboardFlowContextual() { | ||
| const { contractorId, onEvent } = useFlow<ContractorListFlowContextInterface>() | ||
| return <DashboardFlow contractorId={ensureRequired(contractorId)} onEvent={onEvent} /> |
There was a problem hiding this comment.
does the <DashboardFlow/> have the same double header risk?
| contractorUpdateTransition, | ||
| ), | ||
| dashboard: state<MachineTransition>( | ||
| transition(componentEvents.CONTRACTOR_RETURN_TO_LIST, 'list', returnToList), |
There was a problem hiding this comment.
If an admin takes an action inside DashboardFlow that effectively removes or changes the contractor (say, a future dismiss/rehire flow, or anything that makes the current contractor no longer viewable), should we auto-navigate back to list, or is clicking "Back to contractors" always the only way out regardless of what happened inside?
| CONTRACTOR_DOCUMENTS_DONE: 'contractor/documents/done', | ||
| CONTRACTOR_VIEW: 'contractor/view', | ||
| CONTRACTOR_RETURN_TO_LIST: 'contractor/returnToList', | ||
| CONTRACTOR_DISMISS: 'contractor/dismiss', |
There was a problem hiding this comment.
since CONTRACTOR_DISMISS was removed in the other files, should it be removed here too?
| status={contractorList.status} | ||
| onEdit={handleEdit} | ||
| onView={handleView} | ||
| onDismiss={handleDismiss} |
There was a problem hiding this comment.
Should this removal happen in this PR, or would it be safer/cleaner as its own PR? If a host app was already listening for contractor/dismiss and relying on the menu item being there, couldn't that be a breaking change?
Summary
Adds
ContractorManagement.ContractorListFlow, a hub-and-spoke flow that composesContractorListwithDashboardFlowand contractor onboarding, so a host app no longer has to hand-roll navigation between these components itself. Surfaced while building a Contractor Management (Steady State) test fest —ContractorListhad no built-in way to reachDashboardFlowor onboarding.Changes
ContractorListFlow: routes "View details" →DashboardFlow, and "Add contractor" / an onboarding-tab row's "Continue"/"Review" → directly into the Profile step of contractor onboarding.onboardingStateMachine.tsinto an exported, parameterizedcreateContractorOnboardingStepsfactory, reused as-is by both the existingOnboardingFlow(behavior unchanged) and the newContractorListFlow. This avoids mountingOnboardingFlowas a nested flow, which would otherwise double-render headers and route cancel/submit-done to the wrong list.contractor/returnToListevent and abackToListCtatranslation key.contractor/dismissandcontractor/rehireare intentionally left unhandled (bubble throughonEvent) — no dismissal-style flow exists yet.Related
Testing
npm run test -- --run src/components/Contractor/ContractorListFlow/ContractorListFlow.test.tsx— 5/5 passednpm run test -- --run src/components/Contractor/OnboardingFlow/onboardingStateMachine.test.ts— 4/4 passed (regression check on the shared, refactored state machine)npm run test -- --run(full suite) — 3873/3874 passed, 1 pre-existing unrelated flake (confirmed passing in isolation, untouched by this PR)npx tsc --noEmit,npx eslint,npm run docs:lint,npm run docs:build— all clean/contractormanagement/ContractorListFlow→ "View details" opensDashboardFlowwith a "Back to contractors" header and returns cleanly; "Add contractor" and an onboarding-tab row's "Continue" both land directly on the Profile step (pre-filled for "Continue") with a single header — regression-tested against the double-header bug the nested-flow approach originally produced — and Cancel/Submit return to this same list