diff --git a/containers/tefca-viewer/package.json b/containers/tefca-viewer/package.json index 41585e875d..9dea3b2c2e 100644 --- a/containers/tefca-viewer/package.json +++ b/containers/tefca-viewer/package.json @@ -37,6 +37,7 @@ "pg-promise": "^11.5.4", "react": "^18", "react-dom": "^18", + "react-toastify": "^10.0.5", "sharp": "^0.33.5" }, "devDependencies": { diff --git a/containers/tefca-viewer/src/app/query/components/CustomizeQuery.tsx b/containers/tefca-viewer/src/app/query/components/CustomizeQuery.tsx index 097569d7f1..5370b2bff3 100644 --- a/containers/tefca-viewer/src/app/query/components/CustomizeQuery.tsx +++ b/containers/tefca-viewer/src/app/query/components/CustomizeQuery.tsx @@ -12,6 +12,7 @@ import { } from "@/app/database-service"; import { UseCaseQueryResponse } from "@/app/query-service"; import LoadingView from "./LoadingView"; +import { showRedirectConfirmation } from "./RedirectionToast"; interface CustomizeQueryProps { useCaseQueryResponse: UseCaseQueryResponse; @@ -44,10 +45,6 @@ const CustomizeQuery: React.FC = ({ }); const [isExpanded, setIsExpanded] = useState(true); - if (!useCaseQueryResponse) { - return ; - } - // Keeps track of whether the accordion is expanded to change the direction of the arrow const handleToggleExpand = () => { setIsExpanded(!isExpanded); @@ -97,6 +94,11 @@ const CustomizeQuery: React.FC = ({ return acc; }, {} as ValueSet); goBack(); + showRedirectConfirmation({ + heading: QUERY_CUSTOMIZATION_CONFIRMATION_HEADER, + body: QUERY_CUSTOMIZATION_CONFIRMATION_BODY, + headingLevel: "h4", + }); }; useEffect(() => { @@ -302,6 +304,7 @@ const CustomizeQuery: React.FC = ({ Return to patient search +

Customize query

@@ -368,3 +371,8 @@ const CustomizeQuery: React.FC = ({ }; export default CustomizeQuery; + +export const QUERY_CUSTOMIZATION_CONFIRMATION_HEADER = + "Query Customization Successful!"; +export const QUERY_CUSTOMIZATION_CONFIRMATION_BODY = + "You've successfully customized your query. Once you're done adding patient details, submit your completed query to get results"; diff --git a/containers/tefca-viewer/src/app/query/components/RedirectionToast.tsx b/containers/tefca-viewer/src/app/query/components/RedirectionToast.tsx new file mode 100644 index 0000000000..e402bf5ae9 --- /dev/null +++ b/containers/tefca-viewer/src/app/query/components/RedirectionToast.tsx @@ -0,0 +1,77 @@ +import { Alert, HeadingLevel } from "@trussworks/react-uswds"; +import { toast } from "react-toastify"; + +import styles from "./redirectToast.module.css"; + +export type AlertType = "info" | "success" | "warning" | "error"; + +type RedirectionToastProps = { + toastVariant: AlertType; + heading: string; + body: string; + headingLevel?: HeadingLevel; +}; +/** + * Redirection toast to be invoked when there's a need to confirm with the user + * something has occurred + * @param root0 - The config object to specify content / styling of the toast + * @param root0.toastVariant - A string from the enum set "info" | "success" | "warning" | "error" + * indicating what type of toast variant we want. Will style the USWDS component accordingly + * @param root0.heading - The heading / title of the alert + * @param root0.body - The body content of the alert + * @param root0.headingLevel - string of h1-6 indicating which heading level the alert will be. + * defaults to h4 + * @returns A toast component using the USWDS alert + */ +const RedirectionToast: React.FC = ({ + toastVariant, + heading, + body, + headingLevel, +}) => { + return ( + + {body} + + ); +}; + +const options = { + hideProgressBar: false, + position: "bottom-left" as const, + closeOnClick: true, + closeButton: false, + className: styles.padding0, + bodyClassName: styles.padding0, + pauseOnFocusLoss: false, +}; + +/** + * + * @param content - content object to configure the redirect confirmation toast + * @param content.heading - heading of the redirect toast + * @param content.body - body text of the redirect toast + * @param content.headingLevel - h1-6 level of the heading tag associated with + * content.heading. defaults to h4 + */ +export function showRedirectConfirmation(content: { + heading: string; + body: string; + headingLevel?: HeadingLevel; +}) { + toast.success( + , + options, + ); +} + +export default RedirectionToast; diff --git a/containers/tefca-viewer/src/app/query/components/redirectToast.module.css b/containers/tefca-viewer/src/app/query/components/redirectToast.module.css new file mode 100644 index 0000000000..1b424a29c3 --- /dev/null +++ b/containers/tefca-viewer/src/app/query/components/redirectToast.module.css @@ -0,0 +1,7 @@ +.toastWidth { + width: 35%; +} + +.padding0 { + padding: 0 !important; +} diff --git a/containers/tefca-viewer/src/app/query/page.tsx b/containers/tefca-viewer/src/app/query/page.tsx index 999dde1bf0..5f11eedf34 100644 --- a/containers/tefca-viewer/src/app/query/page.tsx +++ b/containers/tefca-viewer/src/app/query/page.tsx @@ -13,6 +13,9 @@ import { } from "../constants"; import CustomizeQuery from "./components/CustomizeQuery"; import LoadingView from "./components/LoadingView"; +import { ToastContainer } from "react-toastify"; + +import "react-toastify/dist/ReactToastify.min.css"; /** * Parent component for the query page. Based on the mode, it will display the search @@ -85,10 +88,13 @@ const Query: React.FC = () => { useCaseQueryResponse={useCaseQueryResponse} queryType={queryType} queryName={UseCaseToQueryNameMap[useCase]} - goBack={() => setMode("search")} + goBack={() => { + setMode("search"); + }} /> )} + ); }; diff --git a/containers/tefca-viewer/src/app/query/test/page.tsx b/containers/tefca-viewer/src/app/query/test/page.tsx index f097c7a815..7b1a2d2452 100644 --- a/containers/tefca-viewer/src/app/query/test/page.tsx +++ b/containers/tefca-viewer/src/app/query/test/page.tsx @@ -77,14 +77,3 @@ const Query: React.FC = () => { ); }; export default Query; -function LoadingView({ loading }: { loading: boolean }) { - if (loading) { - return ( -
-

Loading...

-
- ); - } else { - return null; - } -} diff --git a/containers/tefca-viewer/src/styles/custom-styles.scss b/containers/tefca-viewer/src/styles/custom-styles.scss index 0e28c445e3..28f82f88af 100644 --- a/containers/tefca-viewer/src/styles/custom-styles.scss +++ b/containers/tefca-viewer/src/styles/custom-styles.scss @@ -977,4 +977,8 @@ hr.custom-hr { margin-bottom: 0; border: none; border-top: 1px solid #71767a; -} \ No newline at end of file +} + +.Toastify__toast-container { + width: calc($ecr-viewer-max-width * 0.4) !important; +} diff --git a/package-lock.json b/package-lock.json index c5a15345ba..d204cfe5d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -905,6 +905,7 @@ "pg-promise": "^11.5.4", "react": "^18", "react-dom": "^18", + "react-toastify": "^10.0.5", "sharp": "^0.33.5" }, "devDependencies": { @@ -7563,6 +7564,14 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "dev": true, @@ -14374,6 +14383,18 @@ "version": "2.0.2", "license": "MIT" }, + "node_modules/react-toastify": { + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz", + "integrity": "sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw==", + "dependencies": { + "clsx": "^2.1.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/read-cache": { "version": "1.0.0", "dev": true,