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

Feature/integrate 2023 change request form #381

Merged
merged 38 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
54859ce
Add env variable for 2023 change request form path
courtneymyers Jan 29, 2024
9ba4c78
Add server routes and formio requests for 2023 change request form
courtneymyers Jan 29, 2024
3cdc35c
Add custom hooks for fetching and returning change request form submi…
courtneymyers Jan 30, 2024
69c0449
Update useChangeRequestsQuery() to return the query result and update…
courtneymyers Jan 30, 2024
dd0128c
Add change request column and button link to 2023 table of submissions
courtneymyers Jan 30, 2024
99edba5
Simplify naming of button link components in Submissions component
courtneymyers Jan 30, 2024
9188e9d
Update ChangeRequestButton to post data when clicked, and update Load…
courtneymyers Jan 30, 2024
c0384fd
Update use of ChangeRequestButton to pass required data (e.g. comboKe…
courtneymyers Jan 30, 2024
cedfd54
Update shape of data posted in a new change request form submission, …
courtneymyers Jan 30, 2024
4f8ab3e
Add initial ChangeRequest2023 component and its corresponding route
courtneymyers Jan 30, 2024
d8e9799
Rename ChangeRequest2023 component to Change2023 and update it's rend…
courtneymyers Jan 30, 2024
79b088e
Update change request server/api routes
courtneymyers Jan 30, 2024
ac3f8e0
Add formio utility functions and API endpoints for fetching and updat…
courtneymyers Jan 30, 2024
629b2c6
Update field names passed into a new 2023 change request form submiss…
courtneymyers Jan 30, 2024
ed4105e
Update Change2023 component to render change request form
courtneymyers Jan 30, 2024
deb9456
Add static change-request-intro.md content, update content api to fet…
courtneymyers Jan 30, 2024
a0bf2ff
Update UserDashboard component's onFormPage to include change request…
courtneymyers Jan 30, 2024
6888478
Add static change-requests-intro.md and serve it in the content api
courtneymyers Feb 1, 2024
8817cdb
Add initial change request form table to user's dashboard
courtneymyers Feb 1, 2024
b2436b9
Update display of change request form table on user's dashboard (incl…
courtneymyers Feb 5, 2024
165f4e6
Update appearance of ChangeRequestButton to look more like a link
courtneymyers Feb 6, 2024
932f8b2
Reduce dashboard table text size a bit and reduce height of gaps betw…
courtneymyers Feb 6, 2024
e6bcc10
Remove static change-requests-intro.md file and updaet change request…
courtneymyers Feb 8, 2024
b0c9cbe
Remove change request status column from change requests table (as it…
courtneymyers Feb 8, 2024
25b3825
Add form type to change requests table
courtneymyers Feb 8, 2024
60e3147
Update route of change request form to use it's mongo id, instead of …
courtneymyers Feb 8, 2024
540f7ff
Update path to change request form submission user is redirected to u…
courtneymyers Feb 8, 2024
61aad62
Move creation of new change request form submissions into its own com…
courtneymyers Feb 8, 2024
e8874ab
Create modal to display change request 2023 form
courtneymyers Feb 9, 2024
6fab758
Add server routes and logic to fetch 2023 change request form schema
courtneymyers Feb 10, 2024
3e023e5
Update ChangeRequest2023Form to fetch form schema, and display form i…
courtneymyers Feb 10, 2024
4ddef80
Ensure change request submissions are re-fetched when a change reques…
courtneymyers Feb 10, 2024
a2abb10
Update change request form to not re-fetch form schema if it has alre…
courtneymyers Feb 10, 2024
0fef6ec
Update ChangeRequest2023Dialog to fix Formio form's select inputs not…
courtneymyers Feb 10, 2024
5a22e27
Rename change-request-intro.md to new-change-intro.md and create subm…
courtneymyers Feb 10, 2024
ae283ff
Update Change2023 component to render the form read-only, and remove …
courtneymyers Feb 10, 2024
bbb8e64
Remove server api route and function for updating a change request fo…
courtneymyers Feb 10, 2024
7ff6ad5
Add formio 2023 change request path env variable to GitHub Actions wo…
courtneymyers Feb 10, 2024
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
2 changes: 2 additions & 0 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
FORMIO_2023_FRF_PATH: ${{ secrets.FORMIO_2023_FRF_PATH }}
FORMIO_2023_PRF_PATH: ${{ secrets.FORMIO_2023_PRF_PATH }}
FORMIO_2023_CRF_PATH: ${{ secrets.FORMIO_2023_CRF_PATH }}
FORMIO_2023_CHANGE_PATH: ${{ secrets.FORMIO_2023_CHANGE_PATH }}
FORMIO_BASE_URL: ${{ secrets.FORMIO_BASE_URL }}
FORMIO_PROJECT_NAME: ${{ secrets.FORMIO_PROJECT_NAME }}
FORMIO_API_KEY: ${{ secrets.FORMIO_API_KEY }}
Expand Down Expand Up @@ -142,6 +143,7 @@ jobs:
cf set-env $APP_NAME "FORMIO_2023_FRF_PATH" "$FORMIO_2023_FRF_PATH" > /dev/null
cf set-env $APP_NAME "FORMIO_2023_PRF_PATH" "$FORMIO_2023_PRF_PATH" > /dev/null
cf set-env $APP_NAME "FORMIO_2023_CRF_PATH" "$FORMIO_2023_CRF_PATH" > /dev/null
cf set-env $APP_NAME "FORMIO_2023_CHANGE_PATH" "$FORMIO_2023_CHANGE_PATH" > /dev/null
cf set-env $APP_NAME "FORMIO_BASE_URL" "$FORMIO_BASE_URL" > /dev/null
cf set-env $APP_NAME "FORMIO_PROJECT_NAME" "$FORMIO_PROJECT_NAME" > /dev/null
cf set-env $APP_NAME "FORMIO_API_KEY" "$FORMIO_API_KEY" > /dev/null
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
FORMIO_2023_FRF_PATH: ${{ secrets.FORMIO_2023_FRF_PATH }}
FORMIO_2023_PRF_PATH: ${{ secrets.FORMIO_2023_PRF_PATH }}
FORMIO_2023_CRF_PATH: ${{ secrets.FORMIO_2023_CRF_PATH }}
FORMIO_2023_CHANGE_PATH: ${{ secrets.FORMIO_2023_CHANGE_PATH }}
FORMIO_BASE_URL: ${{ secrets.FORMIO_BASE_URL }}
FORMIO_PROJECT_NAME: ${{ secrets.FORMIO_PROJECT_NAME }}
FORMIO_API_KEY: ${{ secrets.FORMIO_API_KEY }}
Expand Down Expand Up @@ -142,6 +143,7 @@ jobs:
cf set-env $APP_NAME "FORMIO_2023_FRF_PATH" "$FORMIO_2023_FRF_PATH" > /dev/null
cf set-env $APP_NAME "FORMIO_2023_PRF_PATH" "$FORMIO_2023_PRF_PATH" > /dev/null
cf set-env $APP_NAME "FORMIO_2023_CRF_PATH" "$FORMIO_2023_CRF_PATH" > /dev/null
cf set-env $APP_NAME "FORMIO_2023_CHANGE_PATH" "$FORMIO_2023_CHANGE_PATH" > /dev/null
cf set-env $APP_NAME "FORMIO_BASE_URL" "$FORMIO_BASE_URL" > /dev/null
cf set-env $APP_NAME "FORMIO_PROJECT_NAME" "$FORMIO_PROJECT_NAME" > /dev/null
cf set-env $APP_NAME "FORMIO_API_KEY" "$FORMIO_API_KEY" > /dev/null
Expand Down
3 changes: 3 additions & 0 deletions app/client/src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { CRF2022 } from "@/routes/crf2022";
import { FRF2023 } from "@/routes/frf2023";
import { PRF2023 } from "@/routes/prf2023";
// import { CRF2023 } from "@/routes/crf2023";
import { Change2023 } from "@/routes/change2023";
import { useDialogState, useDialogActions } from "@/contexts/dialog";

/** Custom hook to display a site-wide alert banner */
Expand Down Expand Up @@ -254,6 +255,8 @@ export function App() {
<Route path="prf/2023/:id" element={<PRF2023 />} />
{/* <Route path="crf/2023/:id" element={<CRF2023 />} /> */}

<Route path="/change/2023/:id" element={<Change2023 />} />

<Route path="*" element={<Navigate to="/" replace />} />
</Route>
</Route>,
Expand Down
319 changes: 319 additions & 0 deletions app/client/src/components/change2023New.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
import { Fragment, useRef, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { Dialog, Transition } from "@headlessui/react";
import { XMarkIcon } from "@heroicons/react/24/outline";
import { Form } from "@formio/react";
import clsx from "clsx";
import icons from "uswds/img/sprite.svg";
// ---
import { serverUrl, messages } from "@/config";
import {
type FormType,
type FormioChange2023Submission,
getData,
postData,
useContentData,
useChangeRequestsQuery,
} from "@/utilities";
import { Loading } from "@/components/loading";
import { Message } from "@/components/message";
import { MarkdownContent } from "@/components/markdownContent";
import { useNotificationsActions } from "@/contexts/notifications";

type ChangeRequestData = {
formType: FormType;
comboKey: string;
mongoId: string;
rebateId: string | null;
email: string;
title: string;
name: string;
};

type ServerResponse = { url: string; json: object };

/** Custom hook to fetch Formio schema */
function useFormioSchemaQuery() {
const url = `${serverUrl}/api/formio/2023/change`;

const query = useQuery({
queryKey: ["formio/2023/change"],
queryFn: () => getData<ServerResponse>(url),
refetchOnWindowFocus: false,
});

return { query };
}

export function ChangeRequest2023Button(props: {
disabled: boolean;
data: ChangeRequestData;
}) {
const { disabled, data } = props;

const [dialogShown, setDialogShown] = useState(false);

function closeDialog() {
setDialogShown(false);
}

return (
<>
<button
className={clsx(
"tw-border-0 tw-border-b-[1.5px] tw-border-transparent tw-p-0 tw-text-sm tw-leading-tight",
"enabled:tw-cursor-pointer",
"hover:enabled:tw-border-b-slate-800",
"focus:enabled:tw-border-b-slate-800",
)}
type="button"
disabled={disabled}
onClick={(_ev) => {
if (disabled) return;
setDialogShown(true);
}}
>
<span className={clsx("tw-flex tw-items-center")}>
<span className={clsx("tw-mr-1")}>Change</span>
<svg
className="usa-icon"
aria-hidden="true"
focusable="false"
role="img"
>
<use href={`${icons}#launch`} />
</svg>
</span>
</button>

<ChangeRequest2023Dialog
dialogShown={dialogShown}
closeDialog={closeDialog}
data={data}
/>
</>
);
}

function ChangeRequest2023Dialog(props: {
dialogShown: boolean;
closeDialog: () => void;
data: ChangeRequestData;
}) {
const { dialogShown, closeDialog, data } = props;

/*
* NOTE: For some reason select inputs from the Formio form won't receive
* click events if the Dialog.Panel component is used (strangely, they still
* receive keyboard events), so a div is used instead. The downside is we lose
* the triggering of the Dialog component's `onClose` event when a user clicks
* outside the panel.
*/

return (
<Transition.Root show={dialogShown} as={Fragment}>
<Dialog
as="div"
className={clsx("tw-relative tw-z-10")}
open={dialogShown}
onClose={(_value) => closeDialog()}
>
<Transition.Child
as={Fragment}
enter={clsx("tw-duration-300 tw-ease-out")}
enterFrom={clsx("tw-opacity-0")}
enterTo={clsx("tw-opacity-100")}
leave={clsx("tw-duration-200 tw-ease-in")}
leaveFrom={clsx("tw-opacity-100")}
leaveTo={clsx("tw-opacity-0")}
>
<div
className={clsx(
"tw-fixed tw-inset-0 tw-bg-black/70 tw-transition-colors",
)}
/>
</Transition.Child>

<div className={clsx("tw-fixed tw-inset-0 tw-z-10 tw-overflow-y-auto")}>
<div
className={clsx(
"tw-flex tw-min-h-full tw-items-center tw-justify-center tw-p-4",
)}
>
<Transition.Child
as={Fragment}
enter={clsx("tw-duration-300 tw-ease-out")}
enterFrom={clsx("tw-translate-y-0 tw-opacity-0")}
enterTo={clsx("tw-translate-y-0 tw-opacity-100")}
leave={clsx("tw-duration-200 tw-ease-in")}
leaveFrom={clsx("tw-translate-y-0 tw-opacity-100")}
leaveTo={clsx("tw-translate-y-0 tw-opacity-0")}
>
{/* <Dialog.Panel */}
<div
className={clsx(
"tw-relative tw-transform tw-overflow-hidden tw-rounded-lg tw-bg-white tw-p-4 tw-shadow-xl tw-transition-all",
"sm:tw-w-full sm:tw-max-w-7xl sm:tw-p-6",
)}
>
<div className="twpf">
<div
className={clsx(
"tw-absolute tw-right-0 tw-top-0 tw-pr-4 tw-pt-4",
)}
>
<button
className={clsx(
"tw-rounded-md tw-bg-white tw-text-gray-400 tw-transition-none",
"hover:tw-text-gray-700",
"focus:tw-text-gray-700",
)}
type="button"
onClick={(_ev) => closeDialog()}
>
<span className={clsx("tw-sr-only")}>Close</span>
<XMarkIcon
className={clsx("tw-h-6 tw-w-6 tw-transition-none")}
aria-hidden="true"
/>
</button>
</div>
</div>

<div className={clsx("tw-m-auto tw-max-w-6xl tw-p-4")}>
<ChangeRequest2023Form
data={data}
closeDialog={closeDialog}
/>
</div>
</div>
{/* </Dialog.Panel> */}
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
}

function ChangeRequest2023Form(props: {
data: ChangeRequestData;
closeDialog: () => void;
}) {
const { data, closeDialog } = props;
const { formType, comboKey, rebateId, mongoId, email, title, name } = data;

const content = useContentData();
const {
displaySuccessNotification,
displayErrorNotification,
dismissNotification,
} = useNotificationsActions();

const changeRequestsQuery = useChangeRequestsQuery("2023");

const { query } = useFormioSchemaQuery();
const formSchema = query.data;

/**
* Stores when the form is being submitted, so it can be referenced in the
* Form component's `onSubmit` event prop to prevent double submits.
*/
const formIsBeingSubmitted = useRef(false);

if (query.isInitialLoading) {
return <Loading />;
}

if (query.isError || !formSchema) {
return <Message type="error" text={messages.formSchemaError} />;
}

return (
<>
{content && <MarkdownContent children={content.newChangeIntro} />}

<div className="csb-form">
<Form
form={formSchema.json}
url={formSchema.url} // NOTE: used for file uploads
submission={{
data: {
_request_form: formType,
_bap_entity_combo_key: comboKey,
_bap_rebate_id: rebateId,
_mongo_id: mongoId,
_user_email: email,
_user_title: title,
_user_name: name,
},
}}
options={{
noAlerts: true,
}}
onSubmit={(onSubmitSubmission: {
data: { [field: string]: unknown };
metadata: { [field: string]: unknown };
state: "submitted";
}) => {
// account for when form is being submitted to prevent double submits
if (formIsBeingSubmitted.current) return;
formIsBeingSubmitted.current = true;

dismissNotification({ id: 0 });

postData<FormioChange2023Submission>(
`${serverUrl}/api/formio/2023/change/`,
onSubmitSubmission,
)
.then((res) => {
displaySuccessNotification({
id: Date.now(),
body: (
<p
className={clsx(
"tw-text-sm tw-font-medium tw-text-gray-900",
)}
>
Change Request <em>{res._id}</em> submitted successfully.
</p>
),
});

closeDialog();
changeRequestsQuery.refetch();
})
.catch((_err) => {
displayErrorNotification({
id: Date.now(),
body: (
<>
<p
className={clsx(
"tw-text-sm tw-font-medium tw-text-gray-900",
)}
>
Error creating Change Request for{" "}
<em>
{formType.toUpperCase()} {rebateId}
</em>
.
</p>
<p
className={clsx("tw-mt-1 tw-text-sm tw-text-gray-500")}
>
Please try again.
</p>
</>
),
});
})
.finally(() => {
formIsBeingSubmitted.current = false;
});
}}
/>
</div>
</>
);
}
10 changes: 8 additions & 2 deletions app/client/src/components/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import clsx from "clsx";
// ---
// NOTE: React JSX doesn't support namespaces, so `uswds/img/loader.svg` copied
// into app's `images/loader.svg` with namespace tags removed
import loader from "@/images/loader.svg";
Expand All @@ -12,12 +14,16 @@ export function Loading() {
);
}

export function LoadingButtonIcon() {
export function LoadingButtonIcon(props: { position: "start" | "end" }) {
const { position } = props;
return (
<img
src={loaderWhite}
alt="Loading..."
className="margin-left-105 margin-y-neg-05 height-2 is-loading"
className={clsx(
"height-2 is-loading margin-y-neg-05",
position === "start" ? "margin-right-105" : "margin-left-105",
)}
/>
);
}
Loading
Loading