feat(gp): Global payroll demo flow#1077
Open
hamzaremote wants to merge 7 commits into
Open
Conversation
Base automatically changed from
pbyr-4043-gp-sdk-foundation-shared-hooks-and-flow-scaffolding
to
main
June 11, 2026 12:32
…app (PBYR-4044) - Add api.ts with useGPFormSchema, useGPCreateEmployment, useGPUpdateContractDetails, useGPUpdateAdministrativeDetails, useGPInviteEmployee (all via mutateAsyncOrThrow) - Expand usePayrollAdminOnboarding hook with step-aware form schemas, mutations, handleValidation, parseFormValues, onSubmit, and sendInvite - Implement SelectCountryStep, ContractDetailsStep, AdministrativeDetailsStep, InvitationStep, SubmitButton, BackButton, and PayrollAdminForm components - Extract useStepSubmitHandler to eliminate duplicated try/catch/navigate logic across form-based step components - Wire real components into PayrollAdminOnboardingFlow (replaces scaffolding stubs) - Add PayrollAdminOnboardingForm demo page to the example app Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Addresses Jordi's review comment on PR #1076 — aligns with the pattern used in the other flow contexts instead of asserting a redefined type. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ede84ee to
a458469
Compare
Contributor
📦 Bundle Size Report
Size Limits
Largest Files (Top 5)
View All Files (376 total)
✅ Bundle size check passed |
Contributor
📊 Coverage Report
|
| Metric | Current | Previous | Change | Status |
|---|---|---|---|---|
| Lines | 83.97% | 88.40% | -4.43% | 🔴 |
| Statements | 83.40% | 87.80% | -4.40% | 🔴 |
| Functions | 82.16% | 84.86% | -2.69% | 🔴 |
| Branches | 75.43% | 79.62% | -4.18% | 🔴 |
Detailed Breakdown
Lines Coverage
- Covered: 3854 / 4590
- Coverage: 83.97%
- Change: -4.43% (52 lines)
Statements Coverage
- Covered: 3919 / 4699
- Coverage: 83.40%
- Change: -4.40% (55 statements)
Functions Coverage
- Covered: 1041 / 1267
- Coverage: 82.16%
- Change: -2.69% (10 functions)
Branches Coverage
- Covered: 2398 / 3179
- Coverage: 75.43%
- Change: -4.18% (27 branches)
✅ Coverage check passed
Contributor
|
Deploy preview for remote-flows ready!
Deployed with vercel-action |
The new flow brings total raw size to ~614 kB, exceeding the prior 600 kB cap. Following the existing pattern of 50 kB bumps when new flows ship. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(gp): PayrollEmployeeOnboardingFlow — steps, mutations, and example app (PBYR-4045) - Add security field to putV1EmployeePersonalDetails, putV1EmployeeAddress, putV1EmployeeBankAccount in sdk.gen.ts so hey-api injects the Bearer token - Export getV1EmployeeBankAccount and putV1EmployeeBankAccount from client/index.ts - Extract GPStepCallbacks to src/flows/types.ts; both admin and employee flows re-export as GPAdminStepCallbacks / GPEmployeeStepCallbacks for compat - Add api.ts: useGPEmployeeFormSchema, useGPUpdatePersonalDetails, useGPUpdateHomeAddress, useGPUpdateBankAccount (all via mutateAsyncOrThrow) - Expand usePayrollEmployeeOnboarding with step-aware schemas, mutations, handleValidation, parseFormValues, onSubmit; add required countryCode prop - Implement PersonalDetailsStep, HomeAddressStep, BankAccountStep (self-guards when substep not required), SubmitButton, BackButton, PayrollEmployeeForm - Extract useEmployeeStepSubmitHandler mirroring the admin pattern - Export useGPOnboardingSteps from public SDK surface - Date picker: add captionLayout="dropdown" with year range from field constraints; add CSS for rdp caption_dropdowns in global.css - Example app: PayrollEmployeeOnboardingForm with employment ID input, two-context auth (company manager for step status, employee assertion for mutations) - Example backend: GET /api/fetch-employee-token/:employmentId via JWT assertion grant (sub: urn:remote-api:employee:employment:<id>) - Proxy: pass-through token type for /v1/employee/* preserves employee Bearer token instead of overwriting with company manager token Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(gp): personal_details field fixes and jsfModify stability (PBYR-4045) Strip read-only name field and clarify mobile_number format: - Strip 'name' from PUT personal_details payload (additionalProperties: false on the PUT endpoint rejects it — 422 "is not accepted") - Hide 'name' via jsfModify so it is never rendered in the employee form - Add description to mobile_number: 10 digits only, no + or country code (USA) - Add jsfModify param to useGPEmployeeFormSchema for per-schema field overrides Extract hook calls to variables in PersonalDetailsStep and HomeAddressStep: - Avoids calling useEmployeeStepSubmitHandler inline inside JSX prop expression Hoist PERSONAL_DETAILS_JSF_MODIFY to module constant: - The inline object literal was a new reference every render, causing createHeadlessForm to be called on every keystroke (select closure captures jsfModify; React Query re-runs select when its function reference changes). A stable module constant eliminates the unnecessary re-computation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
jordividaller
approved these changes
Jun 15, 2026
…ing (PBYR-4047) (#1080) * feat(gp): PayrollEmployeeFederalTaxesFlow — W-4 step for USA employees (PBYR-4047) - Add new src/flows/PayrollEmployeeFederalTaxes/ flow: hooks, api, context, types, PayrollEmployeeFederalTaxesFlow render-prop component - usePayrollEmployeeFederalTaxes probes /v1/employments/:id/onboarding-steps to derive isActive (completion.sub_steps[0].status === 'completed') and exposes isAvailable + unavailableReason ('unsupported_country' | 'pending_enrollment') - Schema fetch (global_payroll_federal_taxes) is gated on countryCode === 'USA' && active; mutation hits PUT /v1/employee/federal-taxes (employee assertion token) - Components: FederalTaxesStep (returns null when unavailable), SubmitButton, PayrollEmployeeFederalTaxesForm, useFederalTaxesStepSubmitHandler - Add security: [{ scheme: 'bearer', type: 'http' }] to putV1EmployeeFederalTaxes in sdk.gen.ts so hey-api injects the Bearer token (same fix PBYR-4045 applied to personal-details/address/bank-account) - Export PayrollEmployeeFederalTaxesFlow, usePayrollEmployeeFederalTaxes, and related types from public SDK surface - Example app: PayrollEmployeeFederalTaxes demo with employment ID entry and not-available state for non-USA / pre-active employments Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(gp): integrate federal_taxes + state_taxes into PayrollEmployeeOnboarding (PBYR-4047) Replace the standalone PayrollEmployeeFederalTaxesFlow with two new steps (federal_taxes, state_taxes) inside the existing PayrollEmployeeOnboarding flow, matching the merged-flow shape requested for the demo. - Delete src/flows/PayrollEmployeeFederalTaxes/ and its exports/example demo - Extend EmployeeStepKey with 'federal_taxes' and 'state_taxes' - Add jurisdiction?: string prop (US state code) — required for state_taxes - Add useGPUpdateFederalTaxes, useGPUpdateStateTaxes; extend GPEmployeeSchemaType with global_payroll_federal_taxes / global_payroll_state_taxes - Expose employeeBag.taxStepsAvailability keyed by step: { isAvailable, unavailableReason: 'unsupported_country' | 'pending_enrollment' | 'no_jurisdiction' } - Gating model: USA-only + onboarding completion-step probe + retroactive 404 catch. On submit, if Tiger returns 404 (`Tax task not found...`), flip the step's reason to 'pending_enrollment' so the consumer renders the not-available UI instead of a raw API error - New FederalTaxesStep / StateTaxesStep components that return null when the step is unavailable; consumer drives the not-available UI from the bag - Hand-add putV1EmployeeStateTaxes (with security: bearer) + EmploymentStateTaxesParams to sdk.gen.ts / types.gen.ts (prod gateway doesn't expose this endpoint yet, same hand-edit convention used in PBYR-4045) - Re-export TaxStepUnavailableReason + GPEmployeeStepCallbacks from public SDK - Example app: extend GP Employee Onboarding demo with both new steps + a TaxStepNotAvailable banner; jurisdiction from VITE_GP_STATE_JURISDICTION (default 'CA') Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(gp): surface schema_unavailable on tax steps when backend has no form schema The local Tiger gateway exposes `PUT /v1/employee/state-taxes/{jurisdiction}` but returns 400 from `GET /v1/countries/USA/global_payroll_state_taxes`, so the tax form was rendering with zero fields. Fold the schema query's error state into `taxStepsAvailability` so the bag exposes `unavailableReason: 'schema_unavailable'` and the consumer renders the not-available UI instead of a blank form. - Extend TaxStepUnavailableReason with 'schema_unavailable' - Compute taxStepsAvailability AFTER the schema queries so federalTaxesSchema.isError / stateTaxesSchema.isError can feed in; gate the schema queries on country + jurisdiction + post-enrollment (not on availability itself, to avoid a cycle) - Update example app's TaxStepNotAvailable banner with the new reason text Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(gp): derive countryCode and jurisdiction from the employment in the demo Instead of reading VITE_GP_COUNTRY_CODE / VITE_GP_STATE_JURISDICTION env vars (footgun: a USA env + DEU employment would silently submit to the wrong shape), fetch the employment in the outer (company-manager) context alongside useGPOnboardingSteps and hand country.code + work/home address state down to the inner (employee-token) context as props. - New useEmployeeFlowContext combines useGPOnboardingSteps + useEmploymentQuery, returns substeps/hasBankAccount/countryCode/jurisdiction/isLoading - Jurisdiction prefers work_address_details.state, falls back to address_details.state - EmployeeFlowInner now takes countryCode + jurisdiction as required props - Render an error card when the employment can't be loaded (e.g. bad ID or token without access) instead of running the flow with undefined country Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The PEO components landed without unit tests in #1078/#1080, dropping branch coverage to 74.99% (threshold 75%) and failing CI on the parent PR. Add a single tests/components.test.tsx that covers every render-prop component and the submit handler with mocked context, exercising the key branches: - SubmitButton: forwards form id, disabled-on-isSubmitting, no-button throws - BackButton: goToPreviousStep + consumer onClick chain, no-button throws - PersonalDetailsStep / HomeAddressStep: pass step-keyed initialValues - BankAccountStep: returns null when bank substep not in selfOnboardingSubsteps - FederalTaxesStep / StateTaxesStep: returns null when taxStepsAvailability.*.isAvailable === false - useEmployeeStepSubmitHandler: success path advances, MutationError path routes through onError without advancing, non-MutationError falls back to plain shape Branches: 74.99% → 75.38%. PayrollEmployeeForm is stubbed since its react-hook-form wiring isn't what we're exercising here; behaviour-level tests for the form itself belong in a follow-up flow integration test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #1078 changed the example app's proxy to route GET /v[12]/countries* to the user-token, which broke E2E tests in CI: the user token doesn't have access to the countries list, so the country dropdown never populates and selectOption('#country') times out. The original behaviour (pre-1078) used client_credentials for both GET /v[12]/countries$ and GET /v[12]/countries/:country_code/address_details since neither endpoint requires a user identity — they're public reference data. Restore that, leaving the schema endpoint GET /v[12]/countries/:country_code/:form on user-token where it belongs. Unblocks the CostCalculator E2E specs (add-estimation, annual-gross-salary, edit-estimation, hiring-budget) on the PR #1077 stack. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PayrollAdminOnboardingFlowfor Global Payroll admin onboardingPOST /v1/employmentswithtype: global_payroll_employeePUT /v2/employments/:id/contract_detailsandPUT /v2/employments/:id/administrative_detailsmutateAsyncOrThrow(no deprecatedmutateAsync)useStepSubmitHandlerhook extracted to eliminate duplicated try/catch/navigate logic across form step componentsCloses PBYR-4044 · Part of PBYR-3935
Test plan
npm run type-checkpassesnpm test -- --runpasses (781 tests)npm run check-formatclean🤖 Generated with Claude Code