Skip to content

[MPDX-9475] - Add Default and Simple view to PDS Goal Calculator#1765

Merged
wjames111 merged 23 commits into
mainfrom
MPDX-9475
May 12, 2026
Merged

[MPDX-9475] - Add Default and Simple view to PDS Goal Calculator#1765
wjames111 merged 23 commits into
mainfrom
MPDX-9475

Conversation

@wjames111
Copy link
Copy Markdown
Contributor

@wjames111 wjames111 commented May 8, 2026

Description

Wires the existing API field DesignationSupportCalculation.formType (values DETAILED and SIMPLE) through the PDS Goal Calculator UI so users can choose between a full Default view and a streamlined Simple view at goal creation time and switch between them later from the Settings step.

  • New CreateGoalDialog opens when the user clicks Create a New Goal on the goals list — the user must explicitly pick Default or Simple before the goal is created. The chosen form type is sent as formType on the create mutation.
  • Each PdsGoalCard shows a small Default / Simple chip near the goal name so users can identify form types from the list. Legacy goals with a null formType show no chip.
  • SetupStep has a new Form Type select (right under Goal Name) that lets the user toggle between Default and Simple at any time. Switching is silent — saved field values are preserved on the record so toggling back restores the original calculation instantly.
  • When formType === Simple:
    • The Reimbursable Expenses step is filtered out of the side-panel step list
    • The disabled 403b Contribution Percentage display field is hidden in the Setup step
    • Lines 2A (Reimbursable Expenses) and 2B (403b Contributions) are hidden in the summary table
    • The summary calculation (usePdsSummaryData) and per-card total calculation (calculatePdsGoalTotal) zero out reimbursableTotal and fourOThreeBPercentage when building the constants for calculateOtherExpenses (the pure helper itself is unchanged)
  • Defensive useEffect clamps stepIndex to 0 if the steps array shrinks after a formType change, preventing a currentStep === undefined crash.
  • Create-mutation error path: failures show an error snackbar, the dialog stays open, and the Create button re-enables so the user can retry.

Related to MPDX-9475.

Testing

Goal creation

  • Go to the PDS Goal Calculator goals list
  • Click Create a New Goal — a dialog opens with two radio cards (Default and Simple) and short descriptions of each
  • Confirm the Create button is disabled until a form type is picked
  • Pick Simple and click Create — you land on a new goal with 3 steps in the side panel (no Reimbursable Expenses)
  • Repeat with Default — you land on a goal with 4 steps

Form type toggle

  • On a Simple goal, go to Settings — confirm the 403b Contribution Percentage field is not shown
  • Change Form Type to Default — confirm the side panel updates to include Reimbursable Expenses and the 403b field reappears
  • Switch back to Simple — confirm everything hides again. If you previously entered reimbursable values, they remain on the record (toggle back to Default to verify they are restored).

Summary report

  • On a Simple goal, go to Summary Report — confirm rows 2A (Reimbursable Expenses) and 2B (403b Contributions) are not present
  • Confirm the Total Goal is correspondingly lower than for the same inputs in Default mode

Goal card

  • Return to the goals list — confirm each card shows a Default or Simple chip according to its form type
  • Existing goals created before this feature shipped show no chip (legacy formType: null behavior)

Checklist:

  • I have given my PR a title with the format "MPDX-(JIRA#) (summary sentence max 80 chars)"
  • I have applied the appropriate labels (Add the label "Preview" to automatically create a preview environment)
  • I have run the Claude Code `/pr-review` command locally and fixed any relevant suggestions
  • I have requested a review from another person on the project
  • I have tested my changes in preview or in staging
  • I have cleaned up my commit history

wjames111 and others added 13 commits May 7, 2026 15:01
…mple

Adds `formType` to `PdsGoalTotalFields` and short-circuits reimbursable
total and 403b percentage to zero inside `calculatePdsGoalTotal` when the
form type is Simple. Also pins `formType: Detailed` in the shared test
wrapper defaults so that `gqlMock`-generated enum values no longer cause
flaky dollar-amount assertions across the PdsGoalCalculator suite.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a useEffect that resets stepIndex to 0 whenever it falls outside the
bounds of the current steps array. Guards against a future crash if
formType switches from Detailed to Simple while the user is on a later
step (e.g. SummaryReport at index 3 becomes out-of-bounds when steps
shrinks from 4 to 3).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…acy case

Tightens the formType validation to only accept values in DesignationSupportFormType,
and adds a test documenting that a null formType (pre-feature records) falls back to
Default-view rendering (403b field shown).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add an optional `badge` prop to the shared GoalCard and render a
MUI Chip labelled "Default" (Detailed form type) or "Simple" in
PdsGoalCard, so users can distinguish goal types at a glance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements a presentational dialog that lets the user choose between
Default (Detailed) and Simple DesignationSupportFormType before creating
a new PDS goal. Follows TDD: failing test written first, then component.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion flow

- Wrap createPdsGoalCalculation in try/catch with notistack error snackbar so network/server errors surface to the user and the dialog stays open for retry
- Reset selected radio state on dialog close so the form starts clean if reopened
- Add aria-labelledby to Dialog referencing DialogTitle id for accessibility

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

Bundle sizes [mpdx-react]

Compared against 5869477

Route Size (gzipped) Diff
/accountLists/[accountListId]/hrTools/pdsGoalCalculator 160.58 KB +34.97 KB
/accountLists/[accountListId]/hrTools/pdsGoalCalculator/[pdsGoalId] 329.69 KB +1.3 KB

Copy link
Copy Markdown
Contributor Author

@wjames111 wjames111 left a comment

Choose a reason for hiding this comment

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

Multi-Agent Code Review

Verdict: APPROVED_WITH_SUGGESTIONS — No blockers. 3 Important findings (sev 7.0–7.9), several medium findings, and many polish suggestions.

Risk Score: 10/10 (CRITICAL by formula — capped). Pragmatically HIGH: feature-scoped to PDS Goal Calculator, well-tested, ~487 non-test lines, but touches financial calculations + a new GraphQL field + nested wizard state.

Mode: standard | Agents: 6 (Architecture, Testing, Standards, Data Integrity, UX, Financial Reporting) + Dependency Impact


Top-Line Summary

Good feature work. The refactor of calculatePdsGoalTotal and usePdsSummaryData to share buildPdsGoalConstants + buildOtherExpensesConstants is a clear win for consistency. Tests cover the new behavior thoroughly with the project's standard GqlMockedProvider + mutationSpy + toHaveGraphqlOperation patterns. Localization, named exports, generated types, and cache id selection are all clean.

The two themes worth attention before merge:

  1. Step-state UX when toggling Form Type — the activeStepstepIndex derivation can desync silently when steps shrink (Detailed → Simple while on ReimbursableExpenses). Consensus from Architecture, UX, and Data Integrity agents.
  2. A pre-existing display inconsistency between PdsGoalCard (shows assessment ~$1k) and PdsSummaryTable (shows overallTotal ~$10k) is now made more visible by the new badge. Worth a separate ticket — not introduced by this PR but amplified by it.

Important Findings (Severity 7.0–7.9)

These are strongly recommended fixes that don't block merge but cannot be dismissed via /dismiss (severity ≥ 7). Address them in this PR or create follow-up tickets.

  1. [Sev 7.0] Radio button accessible names include the full descriptionCreateGoalDialog.tsx:99 (UX)
  2. [Sev 7.0] PdsGoalCard goalAmount diverges from PdsSummaryTable Total GoalPdsGoalCard.tsx:43 (Financial Reporting, [Pre-existing])
  3. [Sev 7.0] handleStepChange snackbar path and silent-fallback no-snackbar invariant are untestedPdsGoalCalculatorContext.test.tsx (Testing)

Medium Priority (Severity 5.0–6.9)

  1. [Sev 6.5] Silent fallback to Setup when activeStep is dropped from steps — multi-agent consensus (Architecture, UX, Data Integrity)
  2. [Sev 6.5] CreatePdsGoalCalculation mutation lacks update / refetchQueries / cache.modify — Standards [Pre-existing pattern, but worth fixing]
  3. [Sev 6.5] Cancel button disabled while creating removes only visible escape route — UX
  4. [Sev 6.5] Mocking useSteps in context test weakens 4 pre-existing integration tests — Testing
  5. [Sev 6.0] activeStep retains orphaned step value while stepIndex falls back to 0 — Architecture + Data Integrity
  6. [Sev 6.0] Variable shadowing of data and error inside handleCreateGoal — Architecture
  7. [Sev 6.0] Form Type select renders empty for legacy formType: null goals — Data Integrity
  8. [Sev 6.0] currentStep: PdsGoalCalculatorStep is typed non-nullable but steps[stepIndex] can be undefined — Data Integrity
  9. [Sev 6.0] No test pins the consistency invariant calculatePdsGoalTotal === usePdsSummaryData.otherTotals.assessment — Financial Reporting
  10. [Sev 5.5] CreateGoalDialog uses raw useState form state where the rest of the calculator is Formik-driven — Standards (judgment call)
  11. [Sev 5.0] PdsSummaryTable reuses line label 2A for different content between Simple/Detailed — UX
  12. [Sev 5.0] No defaults are seeded for ministryCellPhone/ministryInternet when creating Simple — switching back to Detailed leaves them un-seeded — Data Integrity
  13. [Sev 5.0] Simple formType test asserts only inequality, not a pinned dollar amount — Financial Reporting

Suggestions (Severity < 5.0, informational only)

  • [4.5] No user feedback when toggling Form Type silently removes 403b row + Reimbursable Expenses step — UX
  • [4.0] Form Type select default for legacy null goals could display "Default" — UX
  • [4.0] Add a unit test for formType.ts helpers (isSimpleFormType, isDesignationSupportFormType) — Testing
  • [4.0] useSteps test description says null/undefined but only tests null — Testing
  • [4.0] CreateGoalDialog onCreate test only covers Simple branch — also assert Detailed — Testing
  • [3.5] <Typography variant="subtitle1"> defaults to <h6> (block element) inside <label> — invalid HTML — Architecture
  • [3.5] CircularProgress size={16} deviates from project's size={20} convention — UX
  • [3.5] Chip badge has no color/variant differentiation between Default and Simple — UX
  • [3.5] Inline 403b sum logic was previously extracted; PR re-inlines it into JSX — Architecture
  • [3.0] PdsGoalsList error test should also assert the dialog stays open — Testing
  • [3.0] Spurious multi-line reformat of the React import block — Architecture
  • [3.0] Redundant getByRole after findByRole in PdsSummaryTable.test.tsx — Testing
  • [3.0] isDesignationSupportFormType parameter could be unknown for safer reuse — Data Integrity

Standards Checklist (from .claude/rules/code-review.md)

Area Status Note
Named exports only PASS All new exports named
File naming PASS PascalCase components, .test.tsx colocated, .graphql PascalCase
GraphQL operation names PASS No Get/Load prefixes
Hook names start with use PASS useSteps, usePdsSummaryData, etc.
All user-visible strings via t() PASS No hardcoded JSX text, no string interpolation, no dynamic keys
id in selection sets PASS Fragment includes id and formType
Pagination handled PASS useFetchAllPages unaffected by additive field
Mutation cache updates WARN CreatePdsGoalCalculation missing — flagged above
No any in new code PASS Generated types used throughout
Forms use Formik + Yup WARN CreateGoalDialog raw useState — judgment call
Submit disabled while submitting PASS `disabled={!selected
Tests colocated *.test.{ts,tsx} PASS Every new file has tests
GqlMockedProvider typed generics PASS mocksOverride: ApolloErgonoMockMap properly typed
findBy* over waitFor(() => getBy*) PASS Idiomatic
No console.log/debugger/orphan TODO PASS Clean
No new Date() (use Luxon) N/A No date logic added

Financial Checklist

Item Status
Arithmetic on money values safe Yes (no new compounding float math)
Currency mixing prevented N/A (USD only)
Rounding at display boundary only Yes
Null/undefined amounts handled Yes (isSimpleFormType(null) correctly returns false)
Luxon used for dates N/A
Server-provided aggregations preferred N/A (client-projected goal)
Two-path consistency (calculatePdsGoalTotal vs usePdsSummaryData) Partial — paths share a helper, but display semantics still diverge (pre-existing)

Dependency Impact

  • PdsGoalCalculatorContext.tsx has 14+ direct dependents (provider). High blast radius for the step-state refactor.
  • useSteps() signature change: 1 production caller, updated. GoalCard badge? prop: optional, both callers compatible. No runtime breaks detected.
  • The formType field added to the PdsGoalCalculationFields fragment must be returned by the REST proxy datahandler — if missing, the field is null, which all consumers handle.

Files on Auto-Approval Eligibility

This PR scores LOW/MEDIUM in pragmatic risk terms but the formula caps at 10. Verdict logic uses non-dismissed, non-pre-existing findings only — no blockers, so verdict is APPROVED_WITH_SUGGESTIONS.

Comment thread src/components/HrTools/PdsGoalCalculator/GoalsList/CreateGoalDialog.tsx Outdated
Comment thread src/components/HrTools/PdsGoalCalculator/GoalsList/CreateGoalDialog.tsx Outdated
Comment thread src/components/HrTools/PdsGoalCalculator/GoalsList/CreateGoalDialog.tsx Outdated
Comment thread src/components/HrTools/PdsGoalCalculator/Shared/PdsGoalCalculatorContext.tsx Outdated
Comment thread src/components/HrTools/PdsGoalCalculator/Setup/SetupStep.tsx
Comment thread src/components/HrTools/PdsGoalCalculator/SummaryReport/PdsSummaryTable.tsx Outdated
@@ -38,6 +42,13 @@ export const PdsGoalCard: React.FC<PdsGoalCardProps> = ({ goal }) => {
return constants ? calculatePdsGoalTotal(goal, constants) : 0;
}, [goal, goalMiscConstants, goalGeographicConstantMap, hcmUser]);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Important] [Pre-existing] **`PdsGoalCard.goalAmount` shows `assessment` (~$1,038) while `PdsSummaryTable` "Total Goal" shows `overallTotal` (~$9,816)** — a ~9× discrepancy on the same goal.
  • calculatePdsGoalTotal() returns otherExpenses.assessment only (the assessment line: (subtotal + creditCardFees + attrition) * adminRate).
  • usePdsSummaryData.overallTotal = otherTotals.subtotal + otherTotals.attrition + otherTotals.creditCardFees + otherTotals.assessment.
  • The unit tests pin these distinct values: expect(result).toBeCloseTo(1038.21, 1) for calculatePdsGoalTotal vs expect(overallTotal).toBeCloseTo(9815.77, 0).

Pre-dates this PR (introduced in commit edfa70868 per the goal-card landing PR #1755), but this PR makes the inconsistency more visible by adding the Default/Simple badge directly next to the card amount. Staff comparing card-vs-summary will see two very different numbers labeled "goal."

Recommended follow-up (not blocking this PR):

  • Either change PdsGoalCard.goalTotal to use usePdsSummaryData(goal, hcmUser).overallTotal, or rename calculatePdsGoalTotal to calculatePdsAssessment and add a calculatePdsGoalOverallTotal helper. Current naming is semantically misleading.

Flag for a separate ticket — does not block this PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Bump

@wjames111 wjames111 changed the title MPDX-9475 Add Default and Simple view to PDS Goal Calculator [MPDX-9475] - Add Default and Simple view to PDS Goal Calculator May 8, 2026
@wjames111 wjames111 self-assigned this May 8, 2026
@wjames111 wjames111 added Preview Environment Add this label to create an Amplify Preview On Staging Will be merged to the staging branch by Github Actions labels May 8, 2026
@wjames111 wjames111 requested a review from zweatshirt May 8, 2026 19:18
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

Preview branch generated at https://MPDX-9475.d3dytjb8adxkk5.amplifyapp.com

Copy link
Copy Markdown
Contributor

@zweatshirt zweatshirt left a comment

Choose a reason for hiding this comment

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

This is great Will. This PR is big so there's a possibility I missed something, but from what I can tell, this is solid.


<StyledCard>
<StyledHeaderBox>
<StyledHeaderBox sx={badge ? { gap: 1 } : undefined}>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is opinion, but I'm wondering if it might look better to make Simple/Default a row in the card? I like the badge/chip but I'm just not sure I like where it's placed (right next to the name). What do you think of a 'Goal Type' row or similar? Either way, that's not blocking since it's just opinion

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think that's a good call. I've got a few PR's out on against this one, so I'll create a ticket for this and circle back.

@wjames111 wjames111 merged commit 9e1a327 into main May 12, 2026
65 of 66 checks passed
@wjames111 wjames111 deleted the MPDX-9475 branch May 12, 2026 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

On Staging Will be merged to the staging branch by Github Actions Preview Environment Add this label to create an Amplify Preview

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants