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

Frontend progress bar #27

Merged
merged 13 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions front-end/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NEXT_PUBLIC_PROCESS_URL=ws://localhost:8080/process-ws
9 changes: 5 additions & 4 deletions front-end/app/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default function FooterComponent() {
className="
usa-footer__logo
grid-row
mobile-lg:grid-col-8 mobile-lg:grid-gap-1
mobile-lg:grid-col-10 mobile-lg:grid-gap-1
"
>
<div className="mobile-lg:grid-col-auto">
Expand All @@ -29,11 +29,12 @@ export default function FooterComponent() {
className="
usa-footer__logo
grid-row
mobile-lg:grid-col-4 mobile-lg:grid-gap-1
mobile-lg:grid-col-2 mobile-lg:grid-gap-1
right-justified-text
"
>
<div className="mobile-lg:grid-col-auto">
<p className="text-base-lightest usa_footer-trademark">© 2023 CDC. All rights reserved.</p>
<div className="mobile-lg:grid-col-auto right-justified-text">
<p className="text-base-lightest usa_footer-trademark right-justified-text">© 2023 CDC. All rights reserved.</p>
</div>
</div>
</div>
Expand Down
98 changes: 95 additions & 3 deletions front-end/app/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
}
}
.usa_footer-trademark {
font-family: Public Sans,sans-serif!important;
font-family: Source Sans Pro Web, Helvetica Neue, Helvetica, Roboto, Arial, sans-serif!important;
font-size: 14px;
font-weight: 300;
line-height: 23px;
Expand All @@ -54,17 +54,109 @@

}

// @media all and (min-height: 35rem){
// .usa-footer{
// position: absolute;
// bottom: 0;
// width: 100%;
// }
// }


.hover\:text-base-lightest:hover, .text-base-lightest {
color: #f0f0f0 !important;
}

.usa-nav-container {
max-width: 88rem!important;
max-width: 100% !important;
}

@media all and (min-width: 64em) and (min-width: 64em) {
.usa-nav-container {
padding-left: 32px !important;
padding-right: 32px !important;
}
}

.main-body{
min-height: 40rem;
min-height: 74vh;
margin-top: 1rem;
margin-bottom: 1rem;
//styleName: H1;
h1{
font-family: Source Sans Pro Web, Helvetica Neue, Helvetica, Roboto, Arial, sans-serif !important;
font-size: 40px;
font-weight: 700;
line-height: 50px;
letter-spacing: 0em;
text-align: left;
}

.width-lg{
width: 100%;
max-width: 100%;
}
.usa-button--outline{
background-color: white !important;
&:hover {
background-color: #005ea2 !important;
color: white;
}
}

}

.no-content::before{
content: "" !important;
}

.parse-icon::before{
background-image: url("/parsing.svg");
background-position: 50% 50%;
}

.geolocation-icon::before {
background-image: url("/geolocatting.svg");
background-position: 50% 50%;
}

.exporting-icon::before {
background-image: url("/exporting-data-fields.svg");
background-position: 50% 50%;
}

.standardizing-icon::before {
background-image: url("/standardizing.svg");
background-position: 50% 50%;
}

.fhir-icon::before {
background-image: url("/converting-to-fhir.svg");
background-position: 50% 50%;
}

.usa-step-indicator__segment--complete::before{
background-color: #162E51 !important;
}

.usa-step-indicator-in-progress::before{
background-color: #005ea2 !important;
box-shadow: inset 0 0 0 0.25rem #005ea2, 0 0 0 0.25rem #fff !important;
}

.right-justified-text {
text-align: right;
}

@media (min-width: 64em){
.usa-logo {
margin-top: 2rem !important;
margin-bottom: 2rem !important;
}
}

.max-611{
max-width: 611px;
margin-left: auto;
margin-right: auto;
}
2 changes: 1 addition & 1 deletion front-end/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function RootLayout({
<html lang="en">
<body>
<Header />
<div className="grid-container maxw-desktop-lg main-body">
<div className="grid-container main-body">
<DataProvider>
{children}
</DataProvider>
Expand Down
124 changes: 89 additions & 35 deletions front-end/app/upload_file/page.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,100 @@
'use client'
import { useData } from '@/utils/DataContext';
import { FileInput, FormGroup, Alert, Button } from '@trussworks/react-uswds'
import { useState } from 'react';
import { FileInput, FormGroup, Button } from '@trussworks/react-uswds'
import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import LinkAccordion from '@/components/LinkAccordion/LinkAccordion';


import {formatData, ProgressData, createWebSocket, stepHtml, alertHtml} from './utils'
import { useData } from '@/utils/DataContext';

export default function UploadFile() {
const { setData } = useData();
const router = useRouter();
// We will change this and put it in a constants
// file when orchestration is published
const process_url = 'http://localhost:8080/process'
const [file, setFile] = useState<File | null>(null);
const addFile = (event: React.ChangeEvent<HTMLInputElement>): void => {
setFile(event.target.files?.item(0) || null);
const { setData } = useData();
const router = useRouter();
const url = process.env.NEXT_PUBLIC_PROCESS_URL
const [progress, setProgress] = useState<ProgressData | null>(null); // State for progress
const [socket, setSocket] = useState<WebSocket | null>(null);
const [file, setFile] = useState<File | null>(null);

const handleSubmit = () => {
// Send form data to the server via a WebSocket
if(!file || !socket){
return 'false';
}
const formData = new FormData();
formData.append("file", file);
socket.send(file)
};
const addFile = (event: React.ChangeEvent<HTMLInputElement>) => {
const selectedFile = event.target.files?.item(0);
if(selectedFile){
setFile(selectedFile);
}
};

useEffect(() => {
const ws = createWebSocket(url);
ws.onmessage = (event) => {
let data = formatData(event.data)
if(data.complete && data["processed_values"]){
setData(data)
} else {
setProgress(formatData(event.data));
}
};

ws.onclose = (event) => {
// Handle WebSocket closed
};

setSocket(ws);

const handleSubmit = async (e: any) => {
e.preventDefault();
if (file) {
const formData = new FormData();
formData.append('upload_file', file);
formData.append('message_type', 'ecr');
formData.append('include_error_types', 'errors');
try {
const response = await fetch(process_url, {
method: 'POST',
body: formData,
});
const data = await response.json();
setData(data);
router.push('/export')
} catch (error) {
console.error('Error uploading file', error);
}
}
return () => {
ws.close(); // Close the WebSocket when the component unmounts
};
}, []);



const progressHtml = () =>{
if(!progress || !file){
return (<></>)
}
return (
<div className="display-flex flex-justify-center margin-top-5">
<div className="max-611">
<h1 className='font-sans-xl text-bold margin-top'>Processing your eCR</h1>
<p className="font-sans-lg text-light">
View the progress of your eCR through our pipeline
</p>
{alertHtml(progress, file)}
<div
className="usa-step-indicator usa-step-indicator--counters margin-top-4"
aria-label="progress"
>
<ol className="usa-step-indicator__segments">
{stepHtml(progress)}
</ol>
</div>
<div className='margin-top-5'>
<button type="button" className="usa-button--outline usa-button" onClick={()=>location.reload()}>Cancel</button>
<button
type="button"
className="usa-button"
onClick={()=>router.push('/export')}
disabled={progress.complete ? false : true}
>
Continue
</button>
</div>
</div>
</div>
)
}
if(progress){
return progressHtml()
} else {
return (
<div className="display-flex flex-justify-center margin-top-5">
<div>
<div className="max-611">
<h1 className='font-sans-xl text-bold margin-top'>Upload your eCR</h1>
<p className="font-sans-lg text-light">Select an eCR .zip file to process</p>
<div className="usa-alert usa-alert--info usa-alert--no-icon maxw-tablet">
Expand All @@ -53,7 +106,7 @@ export default function UploadFile() {
</div>
<FormGroup>
<FileInput id="file-input-single"
name="file-input-single" onChange={(addFile)}
name="file-input-single" onChange={addFile}
/>
<div className="margin-top-205">
<LinkAccordion></LinkAccordion>
Expand All @@ -62,5 +115,6 @@ export default function UploadFile() {
</FormGroup>
</div>
</div>
)
)
}
}
Loading
Loading