From acbb3aff8648bade0fc290b370879b8ac07596cc Mon Sep 17 00:00:00 2001
From: zachery with an e <45150570+zweatshirt@users.noreply.github.com>
Date: Wed, 22 Jul 2026 14:27:45 -0500
Subject: [PATCH 1/4] Return the account list ID from
useNewStaffGoalCalculation
---
.../GoalSettings/useNewStaffGoalCalculation.test.tsx | 2 ++
.../GoalSettings/useNewStaffGoalCalculation.tsx | 3 +++
2 files changed, 5 insertions(+)
diff --git a/src/components/HrTools/NsGoalCalculator/GoalSettings/useNewStaffGoalCalculation.test.tsx b/src/components/HrTools/NsGoalCalculator/GoalSettings/useNewStaffGoalCalculation.test.tsx
index 4f5b8e2633..c1d279e4f0 100644
--- a/src/components/HrTools/NsGoalCalculator/GoalSettings/useNewStaffGoalCalculation.test.tsx
+++ b/src/components/HrTools/NsGoalCalculator/GoalSettings/useNewStaffGoalCalculation.test.tsx
@@ -42,6 +42,7 @@ describe('useNewStaffGoalCalculation', () => {
await waitFor(() => expect(result.current.loading).toBe(false));
expect(result.current.isScenario).toBe(false);
+ expect(result.current.accountListId).toBe('account-list-1');
expect(result.current.goalCalculation?.id).toBe('goal-1');
expect(mutationSpy).toHaveGraphqlOperation('NewStaffGoalCalculation', {
accountListId: 'account-list-1',
@@ -57,6 +58,7 @@ describe('useNewStaffGoalCalculation', () => {
await waitFor(() => expect(result.current.loading).toBe(false));
expect(result.current.isScenario).toBe(true);
+ expect(result.current.accountListId).toBeNull();
expect(result.current.goalCalculation?.id).toBe('scenario-2');
expect(result.current.goalCalculation?.firstName).toBe('Grace');
expect(mutationSpy).toHaveGraphqlOperation('NewStaffGoalCalculation', {
diff --git a/src/components/HrTools/NsGoalCalculator/GoalSettings/useNewStaffGoalCalculation.tsx b/src/components/HrTools/NsGoalCalculator/GoalSettings/useNewStaffGoalCalculation.tsx
index b6e1f96cea..69da336072 100644
--- a/src/components/HrTools/NsGoalCalculator/GoalSettings/useNewStaffGoalCalculation.tsx
+++ b/src/components/HrTools/NsGoalCalculator/GoalSettings/useNewStaffGoalCalculation.tsx
@@ -30,6 +30,8 @@ interface UseNewStaffGoalCalculationResult {
fallback: ReactElement | null;
/** True when the source is a scenario goal (fetched/saved by id). */
isScenario: boolean;
+ /** The account list the goal belongs to, or `null` for a scenario goal. */
+ accountListId: string | null;
/**
* Save edits to the goal. Real goals save by account list + id; scenario goals
* by id alone. Both go through the updateNewStaffGoalCalculation mutation.
@@ -82,6 +84,7 @@ export const useNewStaffGoalCalculation = (
error,
fallback,
isScenario,
+ accountListId: isScenario ? null : source.accountListId,
save: async (attributes) => {
if (!goalCalculation) {
return;
From 9390f6d13b40662242a8470c703a721a702eabe6 Mon Sep 17 00:00:00 2001
From: zachery with an e <45150570+zweatshirt@users.noreply.github.com>
Date: Wed, 22 Jul 2026 14:28:01 -0500
Subject: [PATCH 2/4] Add over cap flags to the goal calculation query
---
.../GoalSettings/NewStaffGoalCalculation.graphql | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/components/HrTools/NsGoalCalculator/GoalSettings/NewStaffGoalCalculation.graphql b/src/components/HrTools/NsGoalCalculator/GoalSettings/NewStaffGoalCalculation.graphql
index cf50f82ad4..05db347679 100644
--- a/src/components/HrTools/NsGoalCalculator/GoalSettings/NewStaffGoalCalculation.graphql
+++ b/src/components/HrTools/NsGoalCalculator/GoalSettings/NewStaffGoalCalculation.graphql
@@ -44,6 +44,9 @@ fragment NewStaffGoalCalculationFields on NewStaffGoalCalculation {
specialNeedsTotal
specialNeedsDevelopedToDate
specialNeedsLeft
+
+ salaryOverCap
+ debtOverCap
}
# Identity (read-only display in the header; prepopulated from HCM).
@@ -135,6 +138,8 @@ mutation PreviewNewStaffGoalCalculation(
id
calculations {
monthlyGoal
+ salaryOverCap
+ debtOverCap
}
}
}
From 624415cac56ab1f676bb557acf8b10e9f34581a5 Mon Sep 17 00:00:00 2001
From: zachery with an e <45150570+zweatshirt@users.noreply.github.com>
Date: Wed, 22 Jul 2026 14:28:11 -0500
Subject: [PATCH 3/4] Show admin warnings in Goal Settings
---
.../GoalSettings/GoalSettingsForm.test.tsx | 65 +++
.../GoalSettings/GoalSettingsForm.tsx | 103 ++--
.../GoalSettings/GoalSettingsHeader.test.tsx | 2 -
.../GoalSettings/GoalSettingsHeader.tsx | 12 +-
.../GoalSettingsPreviewContext.tsx | 89 +++
.../GoalSettings/GoalSettingsWarning.test.tsx | 550 ++++++++++++++++++
.../GoalSettings/GoalSettingsWarning.tsx | 50 ++
.../GoalSettings/MpdGoalPreview.test.tsx | 34 +-
.../GoalSettings/MpdGoalPreview.tsx | 35 +-
.../GoalSettings/goalSettingsWarnings.ts | 179 ++++++
.../GoalSettings/useMpdGoalPreview.ts | 86 +--
11 files changed, 1084 insertions(+), 121 deletions(-)
create mode 100644 src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsPreviewContext.tsx
create mode 100644 src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsWarning.test.tsx
create mode 100644 src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsWarning.tsx
create mode 100644 src/components/HrTools/NsGoalCalculator/GoalSettings/goalSettingsWarnings.ts
diff --git a/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsForm.test.tsx b/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsForm.test.tsx
index 2b42d3ef5a..2cb6d4acc5 100644
--- a/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsForm.test.tsx
+++ b/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsForm.test.tsx
@@ -339,6 +339,71 @@ describe('GoalSettingsForm', () => {
);
});
+ // Before the shared provider, each consumer ran its own preview request.
+ it('previews an edit exactly once with every consumer mounted', async () => {
+ const { findByRole } = render();
+
+ const salary = await findByRole('spinbutton', {
+ name: 'Annual Requested Salary — John',
+ });
+ userEvent.clear(salary);
+ userEvent.type(salary, '250000');
+
+ await waitFor(() =>
+ expect(mutationSpy).toHaveGraphqlOperation(
+ 'PreviewNewStaffGoalCalculation',
+ ),
+ );
+
+ const previewCalls = mutationSpy.mock.calls.filter(
+ ([{ operation }]) =>
+ operation.operationName === 'PreviewNewStaffGoalCalculation',
+ );
+ expect(previewCalls).toHaveLength(1);
+ });
+
+ it('shows the salary over-cap warning', async () => {
+ const { findByText, findByRole } = render(
+ ,
+ );
+
+ await findByRole('button', { name: 'Save & Share' });
+ expect(
+ await findByText('Total salary is over the standard cap'),
+ ).toBeInTheDocument();
+ });
+
+ it('shows the debt over-cap warning', async () => {
+ const { findByText, findByRole } = render(
+ ,
+ );
+
+ await findByRole('button', { name: 'Save & Share' });
+ expect(
+ await findByText('Annual debt is over the standard cap'),
+ ).toBeInTheDocument();
+ });
+
it('clears spouse attributes when marital status changes from married to single', async () => {
const { findByRole, getByRole } = render();
diff --git a/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsForm.tsx b/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsForm.tsx
index f983bcd3ac..d7e67b4d14 100644
--- a/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsForm.tsx
+++ b/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsForm.tsx
@@ -11,7 +11,9 @@ import { Form, Formik } from 'formik';
import { useTranslation } from 'react-i18next';
import { NewStaffQuestionnaireMaritalStatusEnum } from 'src/graphql/types.generated';
import { GoalSettingsHeader } from './GoalSettingsHeader';
+import { GoalSettingsPreviewProvider } from './GoalSettingsPreviewContext';
import { GoalSettingsScrollContainer } from './GoalSettingsScrollContainer';
+import { GoalSettingsWarning } from './GoalSettingsWarning';
import { ContactInformationSection } from './Sections/ContactInformationSection';
import { ExemptionsSection } from './Sections/ExemptionsSection';
import { FinancialInformationSection } from './Sections/FinancialInformationSection';
@@ -67,6 +69,8 @@ export const GoalSettingsForm: React.FC = (props) => {
goalCalculation: calculation,
fallback,
isScenario,
+ // `null` for scenario goals, which have no account list.
+ accountListId,
save,
} = useNewStaffGoalCalculation(props);
@@ -155,51 +159,62 @@ export const GoalSettingsForm: React.FC = (props) => {
};
return (
-
+
);
}}
diff --git a/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsHeader.test.tsx b/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsHeader.test.tsx
index fa73f48aae..3f7ba3f893 100644
--- a/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsHeader.test.tsx
+++ b/src/components/HrTools/NsGoalCalculator/GoalSettings/GoalSettingsHeader.test.tsx
@@ -51,8 +51,6 @@ const TestComponent: React.FC = ({
>