-
Notifications
You must be signed in to change notification settings - Fork 42
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
Experience/16759/code mapping results page #17436
Changes from 6 commits
642c61a
283376a
ff37583
257b7b2
373f80d
87421fd
2f80445
556c7c3
709be2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,24 @@ | ||
import { Button, ButtonGroup, FileInput } from "@trussworks/react-uswds"; | ||
import { FormEventHandler, MouseEventHandler, type PropsWithChildren, useCallback } from "react"; | ||
import CodeMappingResults from "./CodeMappingResults"; | ||
import { FormEventHandler, MouseEventHandler, useCallback } from "react"; | ||
import site from "../../content/site.json"; | ||
import useCodeMappingFormSubmit from "../../hooks/api/UseCodeMappingFormSubmit/UseCodeMappingFormSubmit"; | ||
import Spinner from "../Spinner"; | ||
import { USLink } from "../USLink"; | ||
|
||
export type CodeMappingFormProps = PropsWithChildren; | ||
interface CodeMappingFormProps { | ||
onSubmitHandler: FormEventHandler<HTMLFormElement>; | ||
setFileName: (fileName: string) => void; | ||
} | ||
|
||
const CodeMappingForm = (props: CodeMappingFormProps) => { | ||
const { data, isPending, mutate } = useCodeMappingFormSubmit(); | ||
/** | ||
* TODO: Implement submit handler | ||
*/ | ||
const onSubmitHandler = useCallback<FormEventHandler<HTMLFormElement>>( | ||
(ev) => { | ||
ev.preventDefault(); | ||
mutate(); | ||
return false; | ||
}, | ||
[mutate], | ||
); | ||
const CodeMappingForm = ({ onSubmitHandler, setFileName }: CodeMappingFormProps) => { | ||
const onBackHandler = useCallback<MouseEventHandler>((_ev) => { | ||
window.history.back(); | ||
}, []); | ||
const onCancelHandler = useCallback<MouseEventHandler>((_ev) => { | ||
// Don't have a proper mechanism to cancel in-flight requests so refresh page | ||
window.location.reload(); | ||
}, []); | ||
|
||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => { | ||
const files = event.target.files; | ||
if (!files || files.length === 0) return; | ||
|
||
// Take the first file name | ||
setFileName(files[0].name); | ||
}; | ||
|
||
return ( | ||
<> | ||
|
@@ -40,41 +31,22 @@ const CodeMappingForm = (props: CodeMappingFormProps) => { | |
<a href={site.assets.codeMapTemplate.path}>our template</a> to format your result and organism codes to | ||
LOINC or SNOMED. Note: Local codes cannot be automatically mapped. | ||
</p> | ||
{data && <CodeMappingResults />} | ||
{isPending && ( | ||
<> | ||
<Spinner /> | ||
<p className="text-center"> | ||
Checking your file for any unmapped codes that will <br /> prevent data from being reported | ||
successfully | ||
</p> | ||
<Button type={"button"} outline onClick={onCancelHandler}> | ||
Cancel | ||
|
||
<form onSubmit={onSubmitHandler}> | ||
<label className="usa-label" htmlFor="file-input-specific"> | ||
Upload CSV file | ||
</label> | ||
<span className="usa-hint" id="file-input-specific-hint"> | ||
Make sure your file has a .csv extension | ||
</span> | ||
<FileInput id={""} name={"file"} className="maxw-full" accept=".csv" onChange={handleFileChange} /> | ||
<ButtonGroup className="margin-top-5"> | ||
<Button type={"button"} outline onClick={onBackHandler}> | ||
Back | ||
</Button> | ||
</> | ||
)} | ||
{!data && !isPending && ( | ||
<form onSubmit={onSubmitHandler}> | ||
<label className="usa-label" htmlFor="file-input-specific"> | ||
Upload CSV file | ||
</label> | ||
<span className="usa-hint" id="file-input-specific-hint"> | ||
Make sure your file has a .csv extension | ||
</span> | ||
<FileInput id={""} name={"file"} className="maxw-full" accept=".csv" /> | ||
<ButtonGroup className="margin-top-5"> | ||
<Button type={"button"} outline onClick={onBackHandler}> | ||
Back | ||
</Button> | ||
<Button type={"submit"}>Submit</Button> | ||
</ButtonGroup> | ||
</form> | ||
)} | ||
{props.children} | ||
<p className="margin-top-9"> | ||
Questions or feedback? Please email{" "} | ||
<USLink href="mailto:[email protected]">[email protected]</USLink> | ||
</p> | ||
<Button type={"submit"}>Submit</Button> | ||
</ButtonGroup> | ||
</form> | ||
</> | ||
); | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,61 @@ | ||
import type { PropsWithChildren } from "react"; | ||
import { Button } from "@trussworks/react-uswds"; | ||
import { CodeMapData } from "../../hooks/api/UseCodeMappingFormSubmit/UseCodeMappingFormSubmit"; | ||
import { Alert, Table } from "../../shared"; | ||
|
||
export type CodeMappingResultsProps = PropsWithChildren; | ||
interface CodeMappingResultsProps { | ||
fileName: string; | ||
data: CodeMapData[]; | ||
initialStepHandler: () => void; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: maybe rename to onReset, if that's what is practically happening on button usage |
||
|
||
/** | ||
* TODO: Implement result page | ||
*/ | ||
const CodeMappingResults = (props: CodeMappingResultsProps) => <>TODO {props.children}</>; | ||
const CodeMappingResults = ({ fileName, data, initialStepHandler }: CodeMappingResultsProps) => { | ||
const unmappedData = data.filter((item: CodeMapData) => item.mapped === "N"); | ||
const areCodesMapped = unmappedData.length === 0; | ||
const rowData = unmappedData.map((dataRow) => [ | ||
{ | ||
columnKey: "Code", | ||
columnHeader: "Code", | ||
content: dataRow["test code"], | ||
}, | ||
{ | ||
columnKey: "Name", | ||
columnHeader: "Name", | ||
content: dataRow["test description"], | ||
}, | ||
{ | ||
columnKey: "Coding system", | ||
columnHeader: "Coding system", | ||
content: dataRow["coding system"], | ||
}, | ||
]); | ||
return ( | ||
<> | ||
<h2 className="margin-bottom-0"> | ||
<span className="text-normal font-body-md text-base margin-bottom-0"> | ||
<p>File Name</p> | ||
<p>{fileName}</p> | ||
</span> | ||
</h2> | ||
<div className="margin-top-2"> | ||
{areCodesMapped ? ( | ||
<Alert type={"success"} heading={"All codes are mapped"}></Alert> | ||
) : ( | ||
<Alert type={"error"} heading={"Your file contains unmapped codes "}> | ||
Review unmapped codes for any user error, such as a typo. If the unmapped codes are accurate, | ||
download the table and send the file to your onboarding engineer or [email protected]. Our | ||
team will support any remaining mapping needed. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing hyperlinks:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Talked via Slack: we have a discrete ticket for downloading the CSV and will tackle this then. |
||
</Alert> | ||
)} | ||
</div> | ||
<h3>Unmapped codes</h3> | ||
<div className="padding-top-2 padding-bottom-4 padding-x-3 bg-gray-5 margin-bottom-4"> | ||
<Table gray borderless rowData={rowData} /> | ||
</div> | ||
<Button type="button" onClick={initialStepHandler}> | ||
Test another file | ||
</Button> | ||
</> | ||
); | ||
}; | ||
|
||
export default CodeMappingResults; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,43 @@ | ||
import { useMutation } from "@tanstack/react-query"; | ||
|
||
/** | ||
* TODO: Implement hook | ||
*/ | ||
export interface CodeMapData { | ||
"test code": string; | ||
"test description": string; | ||
"coding system": string; | ||
mapped: string; | ||
} | ||
|
||
const useCodeMappingFormSubmit = () => { | ||
const fn = async () => { | ||
// Fake request until implementation | ||
// Simulate network request | ||
await new Promise((resolve) => setTimeout(resolve, 2500)); | ||
|
||
// Return sample JSON | ||
return [ | ||
{ | ||
"test code": "97097-0", | ||
"test description": | ||
"SARS-CoV-2 (COVID-19) Ag [Presence] in Upper respiratory specimen by Rapid immunoassay", | ||
"coding system": "LOINC", | ||
mapped: "Y", | ||
}, | ||
{ | ||
"test code": "80382-5", | ||
"test description": | ||
"Influenza virus A Ag [Presence] in Upper respiratory specimen by Rapid immunoassay", | ||
"coding system": "LOINC", | ||
mapped: "Y", | ||
}, | ||
{ | ||
"test code": "12345", | ||
"test description": "Flu B", | ||
"coding system": "LOCAL", | ||
mapped: "N", | ||
}, | ||
]; | ||
}; | ||
return useMutation({ | ||
mutationFn: fn, | ||
}); | ||
|
||
return useMutation({ mutationFn: fn }); | ||
}; | ||
|
||
export default useCodeMappingFormSubmit; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,76 @@ | ||
import type { PropsWithChildren } from "react"; | ||
import { Button, GridContainer } from "@trussworks/react-uswds"; | ||
import { FormEventHandler, MouseEventHandler, useCallback, useState } from "react"; | ||
import { Helmet } from "react-helmet-async"; | ||
import CodeMappingForm from "../../components/CodeMapping/CodeMappingForm"; | ||
import CodeMappingResults from "../../components/CodeMapping/CodeMappingResults"; | ||
import Spinner from "../../components/Spinner"; | ||
import { USLink } from "../../components/USLink"; | ||
import useCodeMappingFormSubmit from "../../hooks/api/UseCodeMappingFormSubmit/UseCodeMappingFormSubmit"; | ||
|
||
export type CodeMappingPageProps = PropsWithChildren; | ||
enum CodeMappingSteps { | ||
StepOne = "CodeMapFileSelect", | ||
StepTwo = "CodeMapResult", | ||
} | ||
|
||
const CodeMappingPage = (props: CodeMappingPageProps) => { | ||
const CodeMappingPage = () => { | ||
const { data, isPending, mutate } = useCodeMappingFormSubmit(); | ||
const [currentCodeMapStep, setCurrentCodeMapStep] = useState<CodeMappingSteps>(CodeMappingSteps.StepOne); | ||
const [fileName, setFileName] = useState(""); | ||
const onCancelHandler = useCallback<MouseEventHandler>((_ev) => { | ||
// Don't have a proper mechanism to cancel in-flight requests so refresh page | ||
window.location.reload(); | ||
}, []); | ||
const initialStepHandler = () => { | ||
setCurrentCodeMapStep(CodeMappingSteps.StepOne); | ||
}; | ||
const onSubmitHandler = useCallback<FormEventHandler<HTMLFormElement>>( | ||
(ev) => { | ||
ev.preventDefault(); | ||
mutate(); | ||
setCurrentCodeMapStep(CodeMappingSteps.StepTwo); | ||
return false; | ||
}, | ||
[mutate], | ||
); | ||
return ( | ||
<> | ||
<h1>Code mapping tool</h1> | ||
<CodeMappingForm /> | ||
{props.children} | ||
<Helmet> | ||
<title>Code mapping tool - ReportStream</title> | ||
</Helmet> | ||
|
||
<GridContainer> | ||
<h1>Code mapping tool</h1> | ||
{isPending ? ( | ||
<> | ||
<Spinner /> | ||
<p className="text-center"> | ||
Checking your file for any unmapped codes that will <br /> prevent data from being reported | ||
successfully | ||
</p> | ||
<Button type={"button"} outline onClick={onCancelHandler}> | ||
Cancel | ||
</Button> | ||
</> | ||
) : ( | ||
<> | ||
{currentCodeMapStep === CodeMappingSteps.StepOne && ( | ||
<CodeMappingForm onSubmitHandler={onSubmitHandler} setFileName={setFileName} /> | ||
)} | ||
{currentCodeMapStep === CodeMappingSteps.StepTwo && ( | ||
<CodeMappingResults | ||
fileName={fileName} | ||
data={data ?? []} | ||
initialStepHandler={initialStepHandler} | ||
/> | ||
)} | ||
</> | ||
)} | ||
|
||
<p className="margin-top-9"> | ||
Questions or feedback? Please email{" "} | ||
<USLink href="mailto:[email protected]">[email protected]</USLink> | ||
</p> | ||
</GridContainer> | ||
</> | ||
); | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: rename to onSubmit to follow event handler property names on dom elements