Skip to content

feat(gp): Global payroll demo flow#1077

Open
hamzaremote wants to merge 7 commits into
mainfrom
pbyr-4044-featsdk-payrolladminonboardingflow
Open

feat(gp): Global payroll demo flow#1077
hamzaremote wants to merge 7 commits into
mainfrom
pbyr-4044-featsdk-payrolladminonboardingflow

Conversation

@hamzaremote

@hamzaremote hamzaremote commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Implements the full PayrollAdminOnboardingFlow for Global Payroll admin onboarding
  • Step sequence: Select Country → Contract Details → Administrative Details → Invite
  • Employment created via POST /v1/employments with type: global_payroll_employee
  • Subsequent steps use PUT /v2/employments/:id/contract_details and PUT /v2/employments/:id/administrative_details
  • All mutations via mutateAsyncOrThrow (no deprecated mutateAsync)
  • useStepSubmitHandler hook extracted to eliminate duplicated try/catch/navigate logic across form step components
  • Example app page added under "GP Admin Onboarding" using the same styles as the EOR Onboarding demo

Closes PBYR-4044 · Part of PBYR-3935

Test plan

  • npm run type-check passes
  • npm test -- --run passes (781 tests)
  • Lint: 0 errors
  • npm run check-format clean
  • Example app: "GP Admin Onboarding" tab shows country dropdown (225 countries), step nav bar, and card layout matching EOR Onboarding style
  • End-to-end: select a GP-supported country → schema loads → fill basic info → create employment → fill contract details → administrative details → send invitation (requires staging/partners env with GP schemas seeded)

🤖 Generated with Claude Code

@hamzaremote hamzaremote self-assigned this Jun 10, 2026
@hamzaremote hamzaremote added the enhancement New feature or request label Jun 10, 2026
Base automatically changed from pbyr-4043-gp-sdk-foundation-shared-hooks-and-flow-scaffolding to main June 11, 2026 12:32
hamzaremote and others added 2 commits June 11, 2026 15:01
…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>
@remotecom remotecom force-pushed the pbyr-4044-featsdk-payrolladminonboardingflow branch from ede84ee to a458469 Compare June 11, 2026 13:06
@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

Metric Current Previous Change Status
Total (gzip) 237.83 kB 220.83 kB +17 kB (+7.7%) 🔴
Total (raw) 629.85 kB 595.28 kB +34.56 kB (+5.8%) 🔴
CSS (gzip) 21.45 kB 20.89 kB +567 B (+2.7%) 🔴
CSS (raw) 110.5 kB 109.09 kB +1.41 kB (+1.3%) 🔴

Size Limits

  • ✅ Total gzipped: 237.83 kB / 250 kB (95.1%)
  • ✅ Total raw: 629.85 kB / 650 kB (96.9%)
  • ✅ CSS gzipped: 21.45 kB / 25 kB (85.8%)

Largest Files (Top 5)

  1. chunk-NWB4KMAG.js - 14 kB (new)
  2. styles.css - 10.73 kB (+283 B (+2.7%))
  3. index.css - 10.73 kB (+284 B (+2.7%))
  4. index.js - 6.47 kB (+182 B (+2.9%))
  5. chunk-ATSKXMI6.js - 6.37 kB (new)
View All Files (376 total)
File Size (gzip) Change
chunk-NWB4KMAG.js 14 kB new
styles.css 10.73 kB +283 B (+2.7%)
index.css 10.73 kB +284 B (+2.7%)
index.js 6.47 kB +182 B (+2.9%)
chunk-ATSKXMI6.js 6.37 kB new
chunk-OIR5PP5G.js 6.16 kB new
chunk-E4B57L3A.js 4.77 kB new
chunk-X2UCKJED.js 4.74 kB new
chunk-34YGNUCL.js 4.5 kB new
chunk-SFMYHQTV.js 3.89 kB new

✅ Bundle size check passed

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

📊 Coverage Report

⚠️ Coverage decreased

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

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Deploy preview for remote-flows ready!

Project:remote-flows
Status: ✅  Deploy successful!
Preview URL:https://remote-flows-bliqkbgb1-remotecom.vercel.app
Latest Commit:a778693

Deployed with vercel-action

hamzaremote and others added 2 commits June 11, 2026 15:33
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>
Comment thread example/src/App.tsx
…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>
@hamzaremote hamzaremote changed the title feat(gp): PayrollAdminOnboardingFlow feat(gp): Global payroll demo flow Jun 15, 2026
hamzaremote and others added 2 commits June 15, 2026 13:48
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants