Skip to content

Commit 8ccd113

Browse files
sirsamueljosephSirSamuelJoseph
authored andcommitted
feat: [AB#15848] Add success response for employer rates call
1 parent 3a8e2fa commit 8ccd113

File tree

13 files changed

+563
-59
lines changed

13 files changed

+563
-59
lines changed

content/src/fieldConfig/employer-rates.json

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,38 @@
1010
"employerAccessTrueText": "Yes",
1111
"employerAccessNoButtonText": "Register for Employer Access",
1212
"sectionHeaderText": "Employer Contribution Rates",
13-
"quarterOneLabel": "Jan.-Mar.",
14-
"quarterTwoLabel": "Apr.-June",
13+
"quarterOneLabel": "Jan-Mar.",
14+
"quarterTwoLabel": "Apr-June",
1515
"quarterThreeLabel": "July-Sept.",
16-
"quarterFourLabel": "Oct.-Dec.",
17-
"quarterDropdownLabelText": "**Quarter**",
16+
"quarterFourLabel": "Oct-Dec.",
17+
"quarterDropdownLabelText": "Quarter",
1818
"quarterDropdownTooltipText": "Rates for the upcoming year will be available on October 1.",
1919
"dolEinAlertLabelText": "DOL EIN",
20-
"dolEinLabelText": "***`DOL EIN|dol-ein`***",
20+
"dolEinLabelText": "`DOL EIN|dol-ein`",
2121
"dolEinErrorText": "DOL EIN must be 15 digits.",
22-
"serverErrorText": "**This service is temporarily unavailable.** Try again later.",
23-
"noAccountErrorText": "**No account could be found.**"
22+
"serverErrorText": "This service is temporarily unavailable. ",
23+
"serverErrorTryAgainText": "Try again later.",
24+
"noAccountErrorText": "No account could be found.",
25+
"configRatesSuccessHeaderText": "Your ${quarterLabel} Contribution Rates",
26+
"workerRatesTableHeaderText": "Worker Rates",
27+
"employerRatesTableHeaderText": "Employer Rates",
28+
"totalContributionsSuccessHeaderText": "Total Contribution Rates",
29+
"unemploymentInsuranceRow": "`Unemployment Insurance (UI)|unemployment-insurance`",
30+
"disabilityInsuranceRow": "`Disability Insurance (DI)|temporary-disability-insurance`",
31+
"workforceDevelopmentRow": "`Workforce Development (WF)|wf-swf`",
32+
"healthcareSubsidyFundRow": "Healthcare Subsidy Fund (HC)",
33+
"healthcareSubsidyFundTooltipText": "The Healthcare SUbsidy Fund (HC) is currently inactive. No contributions are required.",
34+
"familyLeaveInsuranceRow": "`Family Leave Insurance (FLI)|family-leave-insurance`",
35+
"successAlertText": "Your contribution rates for **${quarterLabel}** are available.",
36+
"uiHcWFLabelText": "Total UI/HC/WF",
37+
"diLabelText": "Total DI",
38+
"fliLabelText": "Total FLI",
39+
"taxableWageBaseLabelText": "Taxable Wage Base",
40+
"taxableWageBaseDiFliLabelText": "Taxable Wage Base DI/FLI",
41+
"baseWeekAmountLabelText": "Base Week Amount",
42+
"numberOfBaseWeeksLabelText": "Number of Base Weeks",
43+
"editQuarterButtonText": "Edit Quarter",
44+
"noEmployerRatesFliText": "None"
45+
2446
}
2547
}

shared/src/employerRates.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ export type EmployerRatesResponse = {
1717
workerDiRate: string;
1818
workerFliRate: string;
1919
totalDi: string;
20-
totalUiHcWf: string;
21-
totalFli: string;
20+
TotalUiHcWf: string;
21+
TotalFli: string;
2222
taxableWageBase: string;
2323
baseWeekAmt: string;
2424
numberOfBaseWeeks: string;

shared/src/test/factories.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -822,8 +822,8 @@ export const generateEmployerRatesResponse = (
822822
workerDiRate: `some-worker-di-rate-${randomInt()}`,
823823
workerFliRate: `some-worker-fli-rate-${randomInt()}`,
824824
totalDi: `some-total-di-${randomInt()}`,
825-
totalUiHcWf: `some-total-ui-hc-wf-${randomInt()}`,
826-
totalFli: `some-total-fli-${randomInt()}`,
825+
TotalUiHcWf: `some-total-ui-hc-wf-${randomInt()}`,
826+
TotalFli: `some-total-fli-${randomInt()}`,
827827
taxableWageBase: `some-taxable-wage-base-${randomInt()}`,
828828
baseWeekAmt: `some-base-week-amt-${randomInt()}`,
829829
numberOfBaseWeeks: `some-number-of-base-weeks-${randomInt()}`,

web/decap-config/collections/12-misc.yml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3027,6 +3027,11 @@ collections:
30273027
}
30283028
- { label: DOL EIN Alert Label Text, name: dolEinAlertLabelText, widget: string }
30293029
- { label: Server Error Alert Text, name: serverErrorText, widget: markdown }
3030+
- {
3031+
label: Server Error Alert Try Again Text,
3032+
name: serverErrorTryAgainText,
3033+
widget: markdown,
3034+
}
30303035
- {
30313036
label: Unable To Perform User Validation Alert Text,
30323037
name: noAccountErrorText,
@@ -3080,6 +3085,86 @@ collections:
30803085
name: employerAccessNoButtonLink,
30813086
widget: string,
30823087
}
3088+
- {
3089+
label: Config Rates Success Header Text,
3090+
name: configRatesSuccessHeaderText,
3091+
widget: string,
3092+
}
3093+
- {
3094+
label: Worker Rates Table Header Text,
3095+
name: workerRatesTableHeaderText,
3096+
widget: string,
3097+
}
3098+
- {
3099+
label: Employer Rates Table Header Text,
3100+
name: employerRatesTableHeaderText,
3101+
widget: string,
3102+
}
3103+
- {
3104+
label: Total Contribution Rates Table Header Text,
3105+
name: totalContributionsSuccessHeaderText,
3106+
widget: string,
3107+
}
3108+
- {
3109+
label: Unemployment Insurance Row Text,
3110+
name: unemploymentInsuranceRow,
3111+
widget: string,
3112+
}
3113+
- {
3114+
label: Disability Insurance Row Text,
3115+
name: disabilityInsuranceRow,
3116+
widget: string,
3117+
}
3118+
- {
3119+
label: Workforce Development Row Text,
3120+
name: workforceDevelopmentRow,
3121+
widget: string,
3122+
}
3123+
- {
3124+
label: Healthcare Subsidy Fund Row Text,
3125+
name: healthcareSubsidyFundRow,
3126+
widget: string,
3127+
}
3128+
- {
3129+
label: Healthcare Subsidy Fund Tooltip Text,
3130+
name: healthcareSubsidyFundTooltipText,
3131+
widget: string,
3132+
}
3133+
- {
3134+
label: Family Leave Insurance Row Text,
3135+
name: familyLeaveInsuranceRow,
3136+
widget: string,
3137+
}
3138+
- { label: Success Alert Text, name: successAlertText, widget: string }
3139+
- {
3140+
label: Unemployment Healthcare Family Leave Label Text,
3141+
name: uiHcWFLabelText,
3142+
widget: string,
3143+
}
3144+
- { label: Disability Insurance Label Text, name: diLabelText, widget: string }
3145+
- { label: Family Leave Insurance Label Text, name: fliLabelText, widget: string }
3146+
- {
3147+
label: Taxable Wage Base Label Text,
3148+
name: taxableWageBaseLabelText,
3149+
widget: string,
3150+
}
3151+
- {
3152+
label: Taxable Wage Base DiFli Label Text,
3153+
name: taxableWageBaseDiFliLabelText,
3154+
widget: string,
3155+
}
3156+
- {
3157+
label: Base Week Amount Label Text,
3158+
name: baseWeekAmountLabelText,
3159+
widget: string,
3160+
}
3161+
- {
3162+
label: Number of Base Weeks Label Text,
3163+
name: numberOfBaseWeeksLabelText,
3164+
widget: string,
3165+
}
3166+
- { label: Edit Quarter Button Text, name: editQuarterButtonText, widget: string }
3167+
- { label: No Employer Rates Fli Text, name: noEmployerRatesFliText, widget: string }
30833168

30843169
- label: "Fundings Onboarding Page"
30853170
name: "business-fundings-onboarding-page"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Content } from "@/components/Content";
2+
import { ScrollableFormFieldWrapper } from "@/components/data-fields/ScrollableFormFieldWrapper";
3+
import { GenericTextField } from "@/components/GenericTextField";
4+
import { useConfig } from "@/lib/data-hooks/useConfig";
5+
import { useUserData } from "@/lib/data-hooks/useUserData";
6+
import { ReactElement } from "react";
7+
8+
interface Props {
9+
disabled?: boolean;
10+
onValidation?: (fieldName: string, invalid: boolean) => void;
11+
handleChange?: (value: string) => void;
12+
value: string | undefined;
13+
error?: boolean;
14+
validationText?: string;
15+
}
16+
17+
export const DOL_EIN_CHARACTERS = 15;
18+
19+
export const DolEin = (props: Props): ReactElement => {
20+
const { Config } = useConfig();
21+
const { business } = useUserData();
22+
return (
23+
<>
24+
<strong>
25+
<Content>{Config.employerRates.dolEinLabelText}</Content>
26+
</strong>
27+
<ScrollableFormFieldWrapper fieldName={"dolEin"}>
28+
<div className="text-field-width-reduced padding-bottom-2">
29+
<GenericTextField
30+
numericProps={{
31+
maxLength: DOL_EIN_CHARACTERS,
32+
}}
33+
fieldName={"dolEin"}
34+
inputWidth={"default"}
35+
disabled={props.disabled}
36+
value={props.value ?? business?.profileData.deptOfLaborEin}
37+
onValidation={props.onValidation}
38+
error={props.error}
39+
validationText={props.validationText}
40+
onChange={props.handleChange}
41+
/>
42+
</div>
43+
</ScrollableFormFieldWrapper>
44+
</>
45+
);
46+
};

web/src/components/employer-rates/EmployerRates.test.tsx

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { EmployerRates } from "@/components/employer-rates/EmployerRates";
2-
import { DOL_EIN_CHARACTERS } from "@/components/employer-rates/EmployerRatesQuestions";
32
import {
43
generateBusiness,
54
generateEmployerRatesResponse,
@@ -17,6 +16,7 @@ import { createTheme, ThemeProvider } from "@mui/material";
1716
import * as api from "@/lib/api-client/apiClient";
1817
import { WithStatefulUserData } from "@/test/mock/withStatefulUserData";
1918
import { WithStatefulProfileData } from "@/test/mock/withStatefulProfileData";
19+
import { DOL_EIN_CHARACTERS } from "@/components/data-fields/DolEin";
2020

2121
jest.mock("@businessnjgovnavigator/shared/dateHelpers", () => {
2222
const actual = jest.requireActual("@businessnjgovnavigator/shared/dateHelpers");
@@ -459,4 +459,79 @@ describe("EmployerRates", () => {
459459
expect(screen.queryByTestId("noAccountError")).not.toBeInTheDocument();
460460
expect(await screen.findByRole("alert")).toBeInTheDocument();
461461
});
462+
463+
it("removes the question and shows the success tables on successful response", async () => {
464+
mockApi.checkEmployerRates.mockResolvedValue(
465+
generateEmployerRatesResponse({
466+
error: "",
467+
}),
468+
);
469+
470+
renderComponentsWithOwning({
471+
employerAccessRegistration: true,
472+
deptOfLaborEin: "123451234512345",
473+
businessName: "Test Business",
474+
});
475+
476+
const submit = await screen.findByRole("button", {
477+
name: Config.employerRates.employerAccessYesButtonText,
478+
});
479+
480+
await userEvent.click(submit);
481+
482+
expect(screen.getByRole("alert", { name: "success" })).toBeInTheDocument();
483+
expect(screen.getByRole("table", { name: "Quarterly Contribution Rates" })).toBeInTheDocument();
484+
expect(screen.getByRole("table", { name: "Total Contribution Rates" })).toBeInTheDocument();
485+
486+
expect(
487+
screen.queryByRole("heading", { name: Config.employerRates.employerAccessHeaderText }),
488+
).not.toBeInTheDocument();
489+
expect(
490+
screen.queryByRole("button", { name: Config.employerRates.employerAccessYesButtonText }),
491+
).not.toBeInTheDocument();
492+
});
493+
494+
it("goes back to the question view if user edits quarter", async () => {
495+
mockApi.checkEmployerRates.mockResolvedValue(
496+
generateEmployerRatesResponse({
497+
error: "",
498+
}),
499+
);
500+
501+
renderComponentsWithOwning({
502+
employerAccessRegistration: true,
503+
deptOfLaborEin: "123451234512345",
504+
businessName: "Test Business",
505+
});
506+
507+
const submit = await screen.findByRole("button", {
508+
name: Config.employerRates.employerAccessYesButtonText,
509+
});
510+
511+
await userEvent.click(submit);
512+
513+
expect(screen.getByRole("alert", { name: "success" })).toBeInTheDocument();
514+
expect(screen.getByRole("table", { name: "Quarterly Contribution Rates" })).toBeInTheDocument();
515+
expect(screen.getByRole("table", { name: "Total Contribution Rates" })).toBeInTheDocument();
516+
517+
const editQuarter = screen.getByRole("button", {
518+
name: Config.employerRates.editQuarterButtonText,
519+
});
520+
521+
await userEvent.click(editQuarter);
522+
expect(screen.queryByRole("alert", { name: "success" })).not.toBeInTheDocument();
523+
expect(
524+
screen.queryByRole("table", { name: "Quarterly Contribution Rates" }),
525+
).not.toBeInTheDocument();
526+
expect(
527+
screen.queryByRole("table", { name: "Total Contribution Rates" }),
528+
).not.toBeInTheDocument();
529+
530+
expect(
531+
screen.getByRole("heading", { name: Config.employerRates.employerAccessHeaderText }),
532+
).toBeInTheDocument();
533+
expect(
534+
screen.getByRole("button", { name: Config.employerRates.employerAccessYesButtonText }),
535+
).toBeInTheDocument();
536+
});
462537
});

web/src/components/employer-rates/EmployerRates.tsx

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
import { LookupOperatingPhaseById } from "@businessnjgovnavigator/shared/";
2-
import { ReactElement, useContext } from "react";
3-
import { Heading } from "../njwds-extended/Heading";
1+
import {
2+
EmployerRatesResponse,
3+
getCurrentDate,
4+
LookupOperatingPhaseById,
5+
} from "@businessnjgovnavigator/shared/";
6+
import { ReactElement, useContext, useState } from "react";
47
import { useConfig } from "@/lib/data-hooks/useConfig";
58
import { Content } from "@/components/Content";
6-
import { EmployerRatesQuestions } from "./EmployerRatesQuestions";
79
import { ProfileDataContext } from "@/contexts/profileDataContext";
10+
import { EmployerRatesSuccessResponse } from "@/components/employer-rates/EmployerRatesSuccessResponse";
11+
import {
12+
EmployerRatesQuarterObject,
13+
getEmployerAccessQuarterlyDropdownOptions,
14+
} from "@/lib/domain-logic/getEmployerAccessQuarterlyDropdownOptions";
15+
import { EmployerRatesQuestions } from "@/components/employer-rates/EmployerRatesQuestions";
16+
import { Heading } from "@/components/njwds-extended/Heading";
817

918
interface Props {
1019
CMS_ONLY_enable_preview?: boolean;
@@ -14,8 +23,16 @@ export const EmployerRates = (props: Props): ReactElement => {
1423
const { state } = useContext(ProfileDataContext);
1524
const { Config } = useConfig();
1625
const operatingPhase = LookupOperatingPhaseById(state.profileData.operatingPhase);
26+
const dropdownOptions = getEmployerAccessQuarterlyDropdownOptions(getCurrentDate());
27+
const [quarter, setQuarter] = useState<EmployerRatesQuarterObject>(dropdownOptions[0]);
28+
const [response, setResponse] = useState<EmployerRatesResponse | false>(false);
29+
const FEATURE_EMPLOYER_RATES_ENABLED = process.env.FEATURE_EMPLOYER_RATES === "true";
1730

18-
if (!operatingPhase.displayEmployerRatesInProfile && !props.CMS_ONLY_enable_preview) {
31+
if (
32+
!operatingPhase.displayEmployerRatesInProfile &&
33+
!props.CMS_ONLY_enable_preview &&
34+
!FEATURE_EMPLOYER_RATES_ENABLED
35+
) {
1936
return <></>;
2037
}
2138

@@ -25,7 +42,24 @@ export const EmployerRates = (props: Props): ReactElement => {
2542
<Heading level={3}>{Config.employerRates.sectionHeaderText}</Heading>
2643
<Content>{Config.employerRates.belowSectionHeaderText}</Content>
2744

28-
<EmployerRatesQuestions CMS_ONLY_enable_preview={props.CMS_ONLY_enable_preview} />
45+
{!response && (
46+
<EmployerRatesQuestions
47+
quarter={quarter}
48+
setQuarter={setQuarter}
49+
setResponse={setResponse}
50+
CMS_ONLY_enable_preview={props.CMS_ONLY_enable_preview}
51+
/>
52+
)}
53+
54+
{response && (
55+
<EmployerRatesSuccessResponse
56+
response={response}
57+
quarter={quarter}
58+
resetQuarter={() => {
59+
setResponse(false);
60+
}}
61+
/>
62+
)}
2963
</div>
3064
</>
3165
);

web/src/components/employer-rates/EmployerRatesQuarterDropdown.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ export const EmployerRatesQuarterDropdown = (props: Props): ReactElement => {
1818
return (
1919
<div className="text-field-width-reduced margin-y-2">
2020
<div className="fdr fac">
21-
<Content>{Config.employerRates.quarterDropdownLabelText}</Content>
21+
<strong>
22+
<Content>{Config.employerRates.quarterDropdownLabelText}</Content>
23+
</strong>
2224
<div className="margin-left-05">
2325
<ArrowTooltip title={Config.employerRates.quarterDropdownTooltipText}>
2426
<div className="fdr fac font-body-lg">

0 commit comments

Comments
 (0)