Skip to content

feat(onboarding): wire onBlur events to job title eligibility check - #1406

Open
gabrielseco wants to merge 9 commits into
mainfrom
found-1869-hook-up-the-form-onblur-events-to-trigger-the-job
Open

gabrielseco wants to merge 9 commits into
mainfrom
found-1869-hook-up-the-form-onblur-events-to-trigger-the-job

Conversation

@gabrielseco

@gabrielseco gabrielseco commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

When filling in the contract details step, we now call the job title eligibility check as fields lose focus, needs job_title_eligibility FF

Why

Partners need to know as early as possible if a job title isn't eligible for a given role setup, not just at submit time. Hooking it to blur gives feedback while the form is still open, without waiting for the step transition.

What changed

Toggle details
  • OnboardingForm.tsx: added an onBlur on the form element that calls the new checkJobTitleEligibility(values) from the flow bag.
  • hooks/useJobTitleEligibilityCheck.tsx (new): owns the check's params state and the underlying useQuery; exposes check(), wired up in useOnboarding as checkJobTitleEligibility.
  • api.ts: adds jobTitleEligibilityCheckOptions (queryOptions factory) and the mutation-error normalization for the endpoint. Sets staleTime: Infinity — the query key fully encodes employmentId + params, so an identical request should never be considered stale within a session.
  • utils.ts: adds getJobTitleEligibilityParams, which derives the check's params from the contract details JSF fields (only when the role fields — role_description, role_is_onsite, role_requires_license — are visible, filled, and valid) and returns null otherwise.
  • Dedup: check() only fires the request when the derived params actually changed since the last check (fast-deep-equal, compared inside the setParams updater for freshness). This matters because queryClient.query() is an imperative call, not a mounted observer — react-query's automatic dedup only covers truly concurrent calls with an identical key, so without this the same blur-triggered params would refetch every time under the default staleTime: 0.
  • The check only runs on the contract_details step, and only when the job_title_eligibility feature flag is enabled.
  • Not done yet / follow-up: the check's result isn't consumed by any UI yet — this PR only wires up firing the request correctly. No debounce, no cancellation of a superseded in-flight request, and no normalization of near-identical params (e.g. whitespace-only edits count as "changed"). Tracking these separately.

Screenshots

N/A — no rendered UI changes yet; the eligibility result isn't surfaced to the user in this PR.

Related Resources

  • FOUND-1869

Testing

  • Tested against the example/ app in a browser
  • Feature flag: job_title_eligibility

Note

Medium Risk
Adds feature-flagged calls to the job title eligibility API during contract details editing; behavior is gated but touches employment eligibility logic before results are shown in UI.

Overview
With the job_title_eligibility feature flag, contract details onboarding now triggers a job title eligibility check while the user is still on the step—not only at submit.

OnboardingForm calls checkJobTitleEligibility on form blur. useOnboarding exposes that from a new useJobTitleEligibilityCheck hook, which validates/parses values, builds request params via getJobTitleEligibilityParams (only when the eligibility JSF slug is present and role fields are visible, filled, and valid), and imperatively fetches through jobTitleEligibilityCheckOptions (postV2 + react-query, staleTime: Infinity, deduped when params are unchanged). The check runs only on contract_details with an employment id.

This PR does not surface the API result in the UI; it only fires the request. .gitignore keeps example/tsconfig.e2e.tsbuildinfo tracked with a newline fix.

Reviewed by Cursor Bugbot for commit 6b6034b. Bugbot is set up for automated code reviews on this repo. Configure here.

gabrielseco and others added 2 commits September 25, 2026 08:37
Splits the flag declaration out of #1395 so it can land on its own:
adds 'job_title_eligibility' to the OnboardingFeatures union (with its
JSDoc) and enables it in the example app. No behavior wired up yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds the useJobTitleEligibilityCheck hook and jobTitleEligibilityCheckOptions
query, and wires OnboardingForm's onBlur to call the flowBag's new
checkJobTitleEligibility(values). The check only runs on the
contract_details step, when 'job_title_eligibility' is enabled, and when
the role fields (role_description, role_is_onsite, role_requires_license)
are filled and valid.

Dedup: params are compared against the previous check (fast-deep-equal)
before firing a request, and jobTitleEligibilityCheckOptions sets
staleTime: Infinity since the query key already encodes employmentId +
params. Without both, react-query's built-in dedup only covers truly
concurrent calls with an identical key — an imperative queryClient.query()
call is not a mounted observer, so it treats every call as a fresh mount
and refetches under the default staleTime: 0.

The result isn't consumed yet (no UI reacts to eligibility yet) - follow-up.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

Metric Current Previous Change Status
Total (gzip) 224.01 kB 222.55 kB +1.46 kB (+0.7%) 🔴
Total (raw) 623.32 kB 620.13 kB +3.19 kB (+0.5%) 🔴
CSS (gzip) 21.94 kB 21.94 kB 0 B (0%) 🟢
CSS (raw) 114.43 kB 114.43 kB 0 B (0%) 🟢

Size Limits

  • ✅ Total gzipped: 224.01 kB / 350 kB (64.0%)
  • ✅ Total raw: 623.32 kB / 850 kB (73.3%)
  • ✅ CSS gzipped: 21.94 kB / 25 kB (87.8%)

Largest Files (Top 5)

  1. index.esm-wIkXrqU6.js - 11.4 kB (0 B (0%))
  2. styles.css - 10.97 kB (0 B (0%))
  3. index.css - 10.97 kB (0 B (0%))
  4. internals-ddER6AK-.js - 6.14 kB (new)
  5. hooks-qtVNERY8.js - 5.81 kB (new)
View All Files (283 total)
File Size (gzip) Change
index.esm-wIkXrqU6.js 11.4 kB 0 B (0%)
styles.css 10.97 kB 0 B (0%)
index.css 10.97 kB 0 B (0%)
internals-ddER6AK-.js 6.14 kB new
hooks-qtVNERY8.js 5.81 kB new
sdk.gen-B5eOEeCW.js 5.51 kB new
index.js 5.45 kB -1 B (-0.0%)
flows/Onboarding/hooks.js 4.53 kB +105 B (+2.4%)
utils-CUgvJHl4.js 4.07 kB new
FieldSetField-ukmSEdN9.js 4.03 kB new

✅ Bundle size check passed

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Deploy preview for adp-cost-calculator ready!

Project:adp-cost-calculator
Status: ✅  Deploy successful!
Preview URL:https://adp-cost-calculator-pdr1qvrue-remotecom.vercel.app
Latest Commit:6b6034b

Deployed with vercel-action

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Deploy preview for remote-flows ready!

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

Deployed with vercel-action

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

📊 Coverage Report

⚠️ Coverage decreased

Metric Current Previous Change Status
Lines 86.01% 86.40% -0.39% 🔴
Statements 85.59% 85.97% -0.38% 🔴
Functions 84.49% 84.92% -0.44% 🔴
Branches 77.21% 77.93% -0.72% 🔴

Detailed Breakdown

Lines Coverage
  • Covered: 4880 / 5674
  • Coverage: 86.01%
  • Change: -0.39% (27 lines)
Statements Coverage
  • Covered: 4964 / 5800
  • Coverage: 85.59%
  • Change: -0.38% (27 statements)
Functions Coverage
  • Covered: 1296 / 1534
  • Coverage: 84.49%
  • Change: -0.44% (6 functions)
Branches Coverage
  • Covered: 3016 / 3906
  • Coverage: 77.21%
  • Change: -0.72% (7 branches)

✅ Coverage check passed

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread src/flows/Onboarding/hooks/useJobTitleEligibilityCheck.tsx
Comment thread src/flows/Onboarding/hooks/useJobTitleEligibilityCheck.tsx
gabrielseco and others added 2 commits September 25, 2026 10:43
Removes the console.log statements left over from development.

Also fixes a real bug surfaced while writing tests for this: paramsChanged
was read immediately after calling setParams(updaterFn), assuming the
functional updater runs synchronously. It doesn't reliably do that, so
paramsChanged came back false almost every time - meaning the eligibility
check never actually fired a request, for any input. Fixed by comparing
against the params value already in the closure before calling setParams,
instead of depending on a side effect inside the updater.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.claude/settings.json is a personal/local permission-allowlist file, not
meant for this PR - it got swept into the previous commit because it was
already staged from earlier local work.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread src/flows/Onboarding/hooks/useJobTitleEligibilityCheck.tsx
gabrielseco and others added 3 commits September 25, 2026 10:57
The JSDoc claimed the check also runs on entering the contract details
step and before submitting it, but nothing calls it that way - only
OnboardingForm's onBlur does. Since useOnboarding is reachable through
the public ./flows/* entry point, this overstated the contract for
anyone building a custom UI around the headless hook.

Reworded to describe only what's actually wired up, and pointed custom
UIs at calling checkJobTitleEligibility themselves for other trigger
points instead of implying it happens automatically.

Also drops stepValues, initialContractDetailsValues, and fieldValues
from useJobTitleEligibilityCheck's params - they were threaded through
from hooks.tsx but never used inside the hook, leftover scaffolding for
the same unimplemented triggers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
check() compared nextParams against params captured from the render
closure after two awaits (handleValidation, parseFormValues). If a second
blur's check() started before the first one's setParams had committed a
re-render, both calls would compare against the same stale params -
depending on timing, that can either fire a duplicate request or wrongly
treat a later complete result as unchanged and skip it. Flagged by
Bugbot on PR #1406, independently of the same race I'd already called
out in review.

Fixed by tracking params in a ref alongside the state (state still drives
the reactive useQuery's enabled/key). Ref writes are synchronous and
immediately visible to any concurrently-resolving check() call, so the
comparison always sees the latest known params regardless of render
timing, closing the window entirely rather than narrowing it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 38681bb. Configure here.

.catch(() =>
console.error('Failed to fetch job title eligibility check'),
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failed checks skip later retries

Medium Severity

setParams runs before the request finishes, and check() only calls queryClient.query when params changed. After a failed first attempt, later blurs with the same role values skip the request, so the server never records a check unless the user edits a role field again.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 38681bb. Configure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixing it in #1408

This branch has not been deployed

No deployments
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