Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(actions): Updating action template and forms content #322

Merged
merged 21 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions src/services/ui/src/hooks/useActionFormController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,17 @@ import { submit } from "@/api/submissionService";
import { buildActionUrl } from "@/lib";
import { PlanType } from "shared-types";

type DataConditionError = {
message: string;
};

export const useActionSubmitHandler = <D extends FieldValues>({
authority,
addDataConditions,
}: {
formHookReturn: UseFormReturn<D>;
authority: PlanType;
/** Reserved for things zod cannot check. */
addDataConditions?: ((data: D) => DataConditionError | null)[];
}): SubmitHandler<D> => {
const { id, type } = useParams("/action/:id/:type");
const { data: user } = useGetUser();
const { setSuccessModalOpen, setErrorModalOpen } = useModalContext();
return async (data) => {
try {
if (addDataConditions?.length) {
const errors = addDataConditions
.map((fn) => fn(data))
.filter((err) => err) // Filter out nulls
.map((err) => err?.message);
if (errors.length)
throw Error(`Additional conditions were not met: ${errors}`);
}
// TODO: Type update for submit generic
Comment on lines -27 to -35
Copy link
Author

Choose a reason for hiding this comment

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

Replaced by ZodObject.superRefine, see below

await submit<D & { id: string }>({
data: { ...data, id: id! },
endpoint: buildActionUrl(type!),
Expand Down
3 changes: 3 additions & 0 deletions src/services/ui/src/pages/actions/IssueRai.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export const RaiIssue = ({
attachments={attachments}
attachmentFaqLink={"/faq/#medicaid-spa-rai-attachments"}
requireAddlInfo
addlInfoInstructions={
<p>Add anything else that you would like to share with the State.</p>
}
/>
);
};
1 change: 1 addition & 0 deletions src/services/ui/src/pages/actions/RespondToRai.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useActionSubmitHandler } from "@/hooks/useActionFormController";
import { ActionFormIntro } from "@/pages/actions/common";
import { ActionFormTemplate } from "@/pages/actions/template";
import { FormSetup } from "@/pages/actions/setups";
import { SetupOptions } from "@/pages";

export const RespondToRai = ({
item,
Expand Down
63 changes: 35 additions & 28 deletions src/services/ui/src/pages/actions/WithdrawPackage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,39 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { useActionSubmitHandler } from "@/hooks/useActionFormController";
import { ActionFormTemplate } from "@/pages/actions/template";
import { FormSetup } from "@/pages/actions/setups";
import { SetupOptions } from "@/pages";
import { ReactElement } from "react";

const attachmentInstructions: Record<SetupOptions, ReactElement> = {
"Medicaid SPA": (
<p>
Upload your supporting documentation for withdrawal or explain your need
for withdrawal in the Additional Information section.
</p>
),
"CHIP SPA": (
<p className="font-normal mb-4">
Official withdrawal letters are required and must be on state letterhead
signed by the State Medicaid Director or CHIP Director.
</p>
),
};

const addlInfoInstructions: Record<SetupOptions, ReactElement> = {
"Medicaid SPA": (
<p>
Explain your need for withdrawal, or upload supporting documentation.
<br />
<em>
Once you submit this form, a confirmation email is sent to you and to
CMS. CMS will use this content to review your package. If CMS needs any
additional information, they will follow up by email
</em>
.
</p>
),
"CHIP SPA": <p>Explain your need for withdrawal.</p>,
};

export const WithdrawPackage = ({
item,
Expand All @@ -21,20 +54,6 @@ export const WithdrawPackage = ({
const handleSubmit = useActionSubmitHandler({
formHookReturn: form,
authority: item?._source.authority as PlanType,
addDataConditions: [
(data) => {
if (
!data.attachments.supportingDocumentation &&
!data.additionalInformation
) {
return {
message: "An Attachment or Additional Information is required.",
};
} else {
return null;
}
},
],
});

if (!item) return <Navigate path={"/"} />; // Prevents optionals below
Expand All @@ -56,22 +75,10 @@ export const WithdrawPackage = ({
attachments={attachments}
attachmentFaqLink={"/faq"}
attachmentInstructions={
<p className="font-normal mb-4">
Official withdrawal letters are required and must be on state
letterhead signed by the State Medicaid Director or CHIP Director.
</p>
attachmentInstructions[item!._source.planType as string as SetupOptions]
}
addlInfoInstructions={
<p>
Explain your need for withdrawal, or upload supporting documentation.
<br />
<em>
Once you submit this form, a confirmation email is sent to you and
to CMS. CMS will use this content to review your package. If CMS
needs any additional information, they will follow up by email
</em>
.
</p>
addlInfoInstructions[item!._source.planType as string as SetupOptions]
}
/>
);
Expand Down
1 change: 1 addition & 0 deletions src/services/ui/src/pages/actions/WithdrawRai.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const WithdrawRai = ({
attachments={attachments}
attachmentFaqLink={"/faq/#medicaid-spa-rai-attachments"}
requireAddlInfo
addlInfoInstructions={<p>Explain your need for withdrawal.</p>}
/>
);
};
4 changes: 2 additions & 2 deletions src/services/ui/src/pages/actions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ToggleRaiResponseWithdraw } from "@/pages/actions/ToggleRaiResponseWith
import { RaiIssue } from "@/pages/actions/IssueRai";
import { WithdrawPackage } from "@/pages/actions/WithdrawPackage";
import { RespondToRai } from "@/pages/actions/RespondToRai";
import { opensearch, Action } from "shared-types";
import { Action } from "shared-types";
import { WithdrawRai } from "./WithdrawRai";
import { useGetItem, useGetPackageActions } from "@/api";
import {
Expand All @@ -24,7 +24,7 @@ import {
medicaidWithdrawPackageSetup,
} from "@/pages/actions/setups";

type SetupOptions = "CHIP SPA" | "Medicaid SPA";
export type SetupOptions = "CHIP SPA" | "Medicaid SPA";
const getFormSetup = (opt: SetupOptions, type: Action): FormSetup | null => {
switch (type) {
case "issue-rai":
Expand Down
4 changes: 2 additions & 2 deletions src/services/ui/src/pages/actions/setups/_.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ZodObject } from "zod";
import { ZodEffects, ZodObject } from "zod";
import { AttachmentRecipe } from "@/lib";

export type FormSetup = {
schema: ZodObject<any>;
schema: ZodObject<any> | ZodEffects<any>;
attachments: AttachmentRecipe<any>[];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks good. I was just curious what ZodEffects are. Is this for the super refine business

Copy link
Author

Choose a reason for hiding this comment

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

Yes, ZodEffects is the return type of superRefine so I plugged it in as a union here :)

};
15 changes: 8 additions & 7 deletions src/services/ui/src/pages/actions/setups/setupIssueRai.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { z } from "zod";
import {
zAdditionalInfo,
zAttachmentOptional,
zAttachmentRequired,
} from "@/pages/form/zod";

export const defaultIssueRaiSetup = {
schema: z.object({
additionalInformation: z.string().max(4000),
additionalInformation: zAdditionalInfo,
Copy link
Collaborator

Choose a reason for hiding this comment

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

good reuse opportunity there with additional information. It will also lead to better consistency across the board I bet

Copy link
Author

Choose a reason for hiding this comment

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

Yep! Merging submission forms into the same pattern will also help consolidate things.

attachments: z.object({
formalRaiLetter: z
.array(z.instanceof(File))
.refine((value) => value.length > 0, {
message: "Required",
}),
other: z.array(z.instanceof(File)).optional(),
formalRaiLetter: zAttachmentRequired({ min: 1 }),
other: zAttachmentOptional,
}),
}),
attachments: [
Expand Down
6 changes: 3 additions & 3 deletions src/services/ui/src/pages/actions/setups/setupRespondToRai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {

export const medicaidRespondToRaiSetup = {
schema: z.object({
additionalInformation: zAdditionalInfo,
additionalInformation: zAdditionalInfo.optional(),
attachments: z.object({
raiResponseLetter: zAttachmentRequired({ min: 1 }),
other: zAttachmentOptional,
Expand All @@ -29,7 +29,7 @@ export const medicaidRespondToRaiSetup = {

export const chipRespondToRaiSetup = {
schema: z.object({
additionalInformation: zAdditionalInfo,
additionalInformation: zAdditionalInfo.optional(),
attachments: z.object({
revisedAmendedStatePlanLanguage: zAttachmentRequired({ min: 1 }),
officialRaiResponse: zAttachmentRequired({ min: 1 }),
Expand All @@ -47,7 +47,7 @@ export const chipRespondToRaiSetup = {
},
{
name: "officialRaiResponse",
label: "Official Rai Response",
label: "Official RAI Response",
required: true,
},
{
Expand Down
28 changes: 22 additions & 6 deletions src/services/ui/src/pages/actions/setups/setupWithdrawPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,28 @@ import {
} from "@/pages/form/zod";

export const medicaidWithdrawPackageSetup = {
schema: z.object({
additionalInformation: zAdditionalInfo,
attachments: z.object({
supportingDocumentation: zAttachmentOptional,
schema: z
.object({
additionalInformation: zAdditionalInfo.optional(),
attachments: z.object({
supportingDocumentation: zAttachmentOptional,
}),
})
.superRefine((data, ctx) => {
if (
!data.attachments.supportingDocumentation?.length &&
data.additionalInformation === undefined
) {
ctx.addIssue({
message: "An Attachment or Additional Information is required.",
code: z.ZodIssueCode.custom,
fatal: true,
});
// Zod says this is to appease types
// https://github.com/colinhacks/zod?tab=readme-ov-file#type-refinements
return z.NEVER;
}
}),
}),
attachments: [
{
name: "supportingDocumentation",
Expand All @@ -23,7 +39,7 @@ export const medicaidWithdrawPackageSetup = {

export const chipWithdrawPackageSetup = {
schema: z.object({
additionalInformation: zAdditionalInfo,
additionalInformation: zAdditionalInfo.optional(),
attachments: z.object({
officialWithdrawalLetter: zAttachmentRequired({ min: 1 }),
}),
Expand Down
5 changes: 3 additions & 2 deletions src/services/ui/src/pages/actions/setups/setupWithdrawRai.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { z } from "zod";
import { zAdditionalInfo, zAttachmentOptional } from "@/pages/form/zod";

export const defaultWithdrawRaiSetup = {
schema: z.object({
additionalInformation: z.string().max(4000),
additionalInformation: zAdditionalInfo,
attachments: z.object({
supportingDocumentation: z.array(z.instanceof(File)).nullish(),
supportingDocumentation: zAttachmentOptional,
}),
}),
attachments: [
Expand Down
23 changes: 21 additions & 2 deletions src/services/ui/src/pages/actions/template.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LoadingSpinner } from "@/components";
import { Alert, LoadingSpinner } from "@/components";
import { PackageInfo } from "@/pages/actions/common";
import { AttachmentsSizeTypesDesc } from "@/pages/form/content";
import {
Expand Down Expand Up @@ -81,9 +81,28 @@ export const ActionFormTemplate = <D extends FieldValues>({
required: requireAddlInfo,
})}
/>
{Object.keys(formController.formState.errors).length !== 0 && (
<Alert className="my-6" variant="destructive">
Input validation error(s)
<ul className="list-disc">
{Object.values(formController.formState.errors).map(
(err, idx) =>
err?.message && (
<li className="ml-8 my-2" key={idx}>
{err.message as string}
</li>
)
)}
</ul>
</Alert>
)}
<div className="flex gap-2 my-8">
<Button type="submit">Submit</Button>
<Button onClick={() => setCancelModalOpen(true)} variant="outline">
<Button
type="button"
onClick={() => setCancelModalOpen(true)}
variant="outline"
>
Cancel
</Button>
</div>
Expand Down
5 changes: 4 additions & 1 deletion src/services/ui/src/pages/detail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,10 @@ export const DetailsContent = ({
<PackageActionsCard id={data._id} />
</section>
<div className="flex flex-col gap-3">
<DetailsSection id="package-details" title="Medicaid Package Details">
<DetailsSection
id="package-details"
title={`${data._source.planType} Package Details`}
Copy link
Author

Choose a reason for hiding this comment

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

Dynamic titling on Details page: "CHIP SPA Package Details", "Medicaid SPA Package Details", ...

>
<DetailItemsGrid displayItems={spaDetails(data._source)} />
<DetailItemsGrid displayItems={submissionDetails(data._source)} />
</DetailsSection>
Expand Down
3 changes: 1 addition & 2 deletions src/services/ui/src/pages/form/zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,4 @@ export const zAttachmentRequired = ({

export const zAdditionalInfo = z
.string()
.max(4000, "This field may only be up to 4000 characters.")
.optional();
.max(4000, "This field may only be up to 4000 characters.");
Copy link
Author

Choose a reason for hiding this comment

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

Leaving .optional() up to each schema instead of pre-defining in zod variable.

Loading