From 791cc3a68b7fa2cefed31aaacabbc68235d829b0 Mon Sep 17 00:00:00 2001 From: Ariel Virgulto Date: Mon, 16 Sep 2024 10:47:09 -0400 Subject: [PATCH 01/10] Initial react app for UI --- frontend/.gitignore | 24 +++ frontend/README.md | 50 ++++++ frontend/eslint.config.js | 28 ++++ frontend/index.html | 13 ++ frontend/package.json | 35 +++++ ... 2024-09-11 at 11.57.35\342\200\257AM.png" | Bin 0 -> 3736 bytes frontend/public/admin.png | Bin 0 -> 3736 bytes frontend/public/vite.svg | 1 + frontend/src/App.css | 69 +++++++++ frontend/src/App.tsx | 51 ++++++ frontend/src/assets/react.svg | 1 + frontend/src/index.css | 13 ++ frontend/src/main.tsx | 10 ++ frontend/src/views/Data.tsx | 86 +++++++++++ .../src/views/DataViews/CaseCollection.tsx | 145 ++++++++++++++++++ frontend/src/views/DataViews/Data.tsx | 69 +++++++++ frontend/src/views/Login.tsx | 66 ++++++++ frontend/src/views/Register.tsx | 13 ++ frontend/src/vite-env.d.ts | 1 + frontend/tsconfig.app.json | 24 +++ frontend/tsconfig.json | 7 + frontend/tsconfig.node.json | 22 +++ frontend/vite.config.ts | 7 + src/ncpdp/script.ts | 22 ++- 24 files changed, 756 insertions(+), 1 deletion(-) create mode 100644 frontend/.gitignore create mode 100644 frontend/README.md create mode 100644 frontend/eslint.config.js create mode 100644 frontend/index.html create mode 100644 frontend/package.json create mode 100644 "frontend/public/Screenshot 2024-09-11 at 11.57.35\342\200\257AM.png" create mode 100644 frontend/public/admin.png create mode 100644 frontend/public/vite.svg create mode 100644 frontend/src/App.css create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/assets/react.svg create mode 100644 frontend/src/index.css create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/views/Data.tsx create mode 100644 frontend/src/views/DataViews/CaseCollection.tsx create mode 100644 frontend/src/views/DataViews/Data.tsx create mode 100644 frontend/src/views/Login.tsx create mode 100644 frontend/src/views/Register.tsx create mode 100644 frontend/src/vite-env.d.ts create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..74872fd --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,50 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default tseslint.config({ + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Optionally add `...tseslint.configs.stylisticTypeChecked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: + +```js +// eslint.config.js +import react from 'eslint-plugin-react' + +export default tseslint.config({ + // Set the react version + settings: { react: { version: '18.3' } }, + plugins: { + // Add the react plugin + react, + }, + rules: { + // other rules... + // Enable its recommended rules + ...react.configs.recommended.rules, + ...react.configs['jsx-runtime'].rules, + }, +}) +``` diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..96e2c90 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Rems Admin UI + + +
+ + + diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..4035ce9 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,35 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11.13.3", + "@emotion/styled": "^11.13.0", + "@mui/icons-material": "^6.1.0", + "@mui/material": "^6.1.0", + "axios": "^1.7.7", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.26.2" + }, + "devDependencies": { + "@eslint/js": "^9.9.0", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "eslint": "^9.9.0", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.9", + "globals": "^15.9.0", + "typescript": "^5.5.3", + "typescript-eslint": "^8.0.1", + "vite": "^5.4.1" + } +} diff --git "a/frontend/public/Screenshot 2024-09-11 at 11.57.35\342\200\257AM.png" "b/frontend/public/Screenshot 2024-09-11 at 11.57.35\342\200\257AM.png" new file mode 100644 index 0000000000000000000000000000000000000000..968533906f596019354daa250e8a52d073eecd6f GIT binary patch literal 3736 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 literal 0 HcmV?d00001 diff --git a/frontend/public/admin.png b/frontend/public/admin.png new file mode 100644 index 0000000000000000000000000000000000000000..968533906f596019354daa250e8a52d073eecd6f GIT binary patch literal 3736 zcmZWr2UJsO(+<5OouwLs(n%m71`MGD6cCUqAV@Vx=tAg-ln5eCAQbt~P;^7DvUFHL zx*%0Rq)J&N^xpsA?(cfO@18Sv=FU9N%sVspo^zx0^=>oK^U?zV046O>bwiStC5Z$r zHR(Qbwe%|hK>rSnMCxlHkwAST!lxvx?WO7 zLQaka2(>>4uLqLJq1`QX7LWF)&kIC1sm$SkOnof-p?N(cHeTo`MkM7SMM(awlcSpP zIW0tF=?h9Rmf4$08Sq|YQUpDO%oXb=IooYo_<}Lf?MR+BK4pw z+S#svNhlP0NPAgrj53N<2>uHH>L3-~^|a@e=%=m);U53mRI%njx{43WUpdd09-p{( zX{PoOZLy*X8gtxnW>oVZ*WRJ1JI!&w@k3kH&Wuku2%4wa4r!@vad=zAsDw(Ryb{bK zA_syWM&Wjs+4wF&XFVMqh`w~U7^ zj3?=q%C<>dZJEcpP&3p#Ag8|lp(3NBw5+_0!e*;juZiA$U3XITc^rNhl}=TGteY5IgfU)Q2>gra!vzUjVCG}{tt_L@q5-Lq= z6=tv@sH4Jy4Hso$GK|Ckk)8bIp=mGq=9#F}X7%}TfNMyQCjA0(C5tVWBOkk`%GCd< zU+XV8hVy&q8uGMXdWZUmGRFp5NbcNxYa=L)u3`oNW3yruY3-STSF=npn*Mox<4QFZ z;*xdbdJFX68a#qrlNZfbz$y;6eLb>sd zlIal+nNs~=M`a90dZbl^RqPbDSba3dqMv)9YT)!QgB?>ws+=g94wyYG%}fU>%x#1f z>ln7vvjN+#*-^`^mzhZlILCN@II{g#nm3j_raLw{=0H?%x#R=58PPgB{CE*&YDpUx z-P6uF>odzd+c)PnOa1ur#?^Y}DeCp`_>Slessqmx)f1i*sS~dgO4S4#O%Gu#%K?@X zD}|-N`m`{$0Byr^)#Ze1*@U9i=+{cSmhzFng| zxA^yBe6g`F-mPj4`=-naXBBEyF=hWG8)2lZ)uhved^)Vs0qg`$XAaDSUTJ=bPP3~X zDjo9p+BqiL?ig-RFz~W0o%Ei3svbB-Zkv)(-o#@8nXl^s^bm-LBd~$F&vFO`( z?u2qrcJu!8dsbz6Ws`TPcik=@pDf=S-Q->Yix)-ltGpBP6Qe$hKIC;uKKn%Up6VVS z@g}he22<4JPv?&iVB%-zFAz9R6aT2cD7?ri5|cz2;w2dAU4AW)QSmxn@qF2CHgVCj zALSUjN!}br!9Z=fYgto#Y>9GXtfH*CJo#{~cFl61cb_15Bp4KbKYmHl=$`lr%StjK znebs7y9Aqf)$rm;AH%}JiV1OKwTLLN@L>7$!|hKZ6Or9w*R55?Lv>ubGAsGw%Z4)sl>v?CsRWwNyZG2nqdVm22x#JgOs)LS^ z^a&>BkF7lBecV$0ho5B-Cdl<75Iv}0?!2`(Kg+R? z+rqJ4u`m-0di_1M_J%iH>S_^q)gnMAz>69obmiMyO>>-ZZCc*z8hFKR7ciKuCq)4= zQRZUI2F&XXE9ZJti7Egh4N`~Ik6yo5?vNsgFCpD9Yh?@CJsLfA_tJ{Ss^|RQ+?~2B zF(BbDadXRidvsyH#IEQ|74QAm)wTAqXX!21!=0auel8IzzGJcP9BQJ9Zm!#vS(#v{ zq#xyC4q7^XV4t9t@HW>yyeFbzI;V*{c)DT0=UZ{Cb@XA|sCJ=t_Z@`$oNMh~`0gc6 z<)Dd-_cejhttmu0wbog%jPJj7UMnIQ*o=R-M2XmbHx#QOxQ+RXI zw@$}S6^`)&&k_BH?AwHh`>|gVzd*dR70sXcowiYTL_~zA(^Sc|#16L4BBPln;Y9E1 zqONSTwQNJpJS_~Qv^BSc#RtYy^RL3%;h!;IH~e<#zC_J&<)Sot9G$fb3Z;sRmfoIs z#rdS}u{W8W8TxAP8;_tzhAjG6K5@J`^pRgDjwx)`By4vr(I}?)!0TB8XZzYpqbF0z z!4GPPUWaNT1=~c6d__&z8>C1UQ_C!|QLd3V&KmMb4VkWFmFlW6iROFQ|%eBY)p&IvX*rbk>k{yu5F%-03g9>9H#GF=wvhMSKt5 z5s`P;yHn-xDx8REGJQHa^qg>s#*YH>^wZJlw#}?;cg5C{;wQccmk2$kW5(&AvW>Qx zJDWXG3E9fHv)o|R*7pOv;8UO2_BiIol=`zhharpID(`jTly}UtI3<7lX0ToTLHJSR zQT)gF*LXXG629{^=GbzxZRgJMXh=zXBP!@vcu~9BCCD4O`ft65GShO<)dh%?Y+3+W zBpN_TvdBonOA-KpA~6C$L(*&{QO_j*8I{eX_-RXA09B2UT3RG+WbcVWxp_IdW9}_* zWsy=1qm9ilX1Y3x_U^7=+lTIUD6pTa#{~rd_fsU9t|*Kx(9hMy%}dcw3G@S@NU|^7 z5D@SO1mmm(GSk%uBHcYvKzXn<&2Lgd`&xa0*hU&Nep_87JK#mxUhav>x>+1{l zl?A(dK7z<7C@4Urp%5rkiiD8z@^{17`boKY3H_bq|MRG$yzD*E9vHN{8}K5pt)06! zMhOJEDD?CC`{z5eSmv~*~SK=>$|0bGvp*)f9uB3t(#J_v_hx}{sA0ixb(f2QN_ty-jtYlM^M=E8~Kda}R6%)kDpsSOg)wYKzu&Vy_eW z{Pcmj_V-3vU1XLuPW2>s-}bx{DB1rDS3I(oO+}G(1+0VBLa+zIDFPULnRv1aeJQ{fIv#tD@&NVr`SF~Rl zzo*ghl&(y*zLlN2Mne`$;EA)G kYl^K%-OI9^oKwlWJI~A+>wHpbFTNfv4L$W@HJh;i0~J5T{r~^~ literal 0 HcmV?d00001 diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css new file mode 100644 index 0000000..7603c51 --- /dev/null +++ b/frontend/src/App.css @@ -0,0 +1,69 @@ +.App { + text-align: center; + background-color: #005B94; + padding: 20px; + margin-bottom: 20px; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +.navButtons { + list-style: none; + text-decoration: none; + margin-left: 5px; +} + +.white-btn { + color: white !important; + border-color: white !important; +} + +.App h1 { + color: white; + line-height: 1.3; + letter-spacing: 0.00938em; + font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; +} + +.logo { + grid-area: main; + text-align: left; + margin-right: auto; + display: inline-flex; + box-sizing: unset; +} + +.links { + grid-area: right; + margin-left: auto; + margin-top: 25px; +} + +.containerg { + display: grid; + grid-template-areas: ' main right'; +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..4c4cc11 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,51 @@ +import './App.css' +import { BrowserRouter, Link, Route, Routes } from 'react-router-dom'; +import { Button } from '@mui/material'; +import Box from '@mui/material/Box'; +import { Container } from '@mui/system'; +import DatasetIcon from '@mui/icons-material/Dataset'; +import Login from './views/Login'; +import Register from './views/Register'; +import Data from './views/DataViews/Data'; + +function App() { + + return ( + + +
+ +
+
+ +

Rems Admin

+
+
+ + + + + + + + + +
+
+
+ +
+ + } /> + } /> + } /> + } /> + +
+
+ ) +} + +export default App diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/frontend/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..ec2585e --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000..6f4ac9b --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App.tsx' +import './index.css' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/frontend/src/views/Data.tsx b/frontend/src/views/Data.tsx new file mode 100644 index 0000000..e8e1faf --- /dev/null +++ b/frontend/src/views/Data.tsx @@ -0,0 +1,86 @@ +import { SetStateAction, SyntheticEvent, useState } from 'react' +import axios from 'axios' +import { useEffect } from 'react' +import { Box, Tab, Tabs } from '@mui/material'; +import { Container } from '@mui/system'; + +function a11yProps(index: number) { + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}` + }; + } +const Data = () => { + + const [allData, setAllData] = useState([]); + const [tabIndex, setValue] = useState(0); + + const handleChange = (event: SyntheticEvent, newValue: number) => { + setValue(newValue); + }; + + useEffect(() => { + getAllRemsCase(); + }, []) + + const getAllRemsCase = async () => { + const url = 'http://localhost:8090/all/remscase'; + await axios + .get(url) + .then(function (response: { data: SetStateAction; }) { + console.log('Response is -- > ', response); + setAllData(response.data); + + }) + .catch((error: any) => { + console.log('Error -- > ', error); + }) + } + + return ( + + + + + + + + + + + + + {tabIndex === 0 && ( + +
This is for rems case
+
+ )} + {tabIndex === 1 && ( + +
This is for etasu
+
+ )} + {tabIndex === 2 && ( + +
This is for other
+
+ )} +
+
+
+ +
+ ) + +} + +export default Data; \ No newline at end of file diff --git a/frontend/src/views/DataViews/CaseCollection.tsx b/frontend/src/views/DataViews/CaseCollection.tsx new file mode 100644 index 0000000..c64e6a0 --- /dev/null +++ b/frontend/src/views/DataViews/CaseCollection.tsx @@ -0,0 +1,145 @@ +import axios from 'axios' +import { useEffect, useState, SetStateAction } from 'react' +import { + Box, + Button, + Card, + CardActions, + CardContent, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography + } from '@mui/material'; +import IconButton from '@mui/material/IconButton'; +import DeleteIcon from '@mui/icons-material/Delete'; + +export type RemsCase = { + case_number?: string; + auth_number?: string; + patientFirstName?: string; + patientLastName?: string; + patientDOB?: string; + drugCode?: string; + drugName?: number; + status?: string; + dispenseStatus?: string; + metRequirements: + | { + metRequirementId: string; + requirementsDescription: string; + requirementName: string; + stakeholderId: string; + completed: boolean; + }[] + | null; + _id: string; + }; +const CaseCollection = () => { + + const [allData, setAllData] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + getAllRemsCase(); + }, []) + + const getAllRemsCase = async () => { + const url = 'http://localhost:8090/all/remscase'; + await axios + .get(url) + .then(function (response: { data: SetStateAction; }) { + setAllData(response.data); + setIsLoading(false); + }) + .catch((error: any) => { + setIsLoading(false); + console.log('Error -- > ', error); + }) + } + + const deleteSingleRow = async (event: any, row: RemsCase) => { + console.log('row to delete is -- > ', row); + const url = 'http://localhost:8090/remsCase/deleteOne'; + await axios + .post(url, {data: { params: row}}) + .then(function (response: { data: any; status: number; }) { + console.log('response data -- > ', response.data); + if (response.status === 200) { + console.log('Response.data -- > ', response.data); + getAllRemsCase(); + } + }) + .catch((error: any) => { + setIsLoading(false); + console.log('Error -- > ', error); + }) + } + + if (allData.length < 1 && !isLoading) { + return ( + +

No data

+
+ ) + } else { + return ( + + + + + + + + + Case Number + Patient First Name + Patient Last Name + Drug Name + Drug Code + Patient DOB + Status + Dispense Status + Authorization Number + {/* Met Requirements */} + Delete + + + + {allData.map(row => { + // console.log('row -- > ', row); + return ( + + {row.case_number} + {row.patientFirstName} + {row.patientLastName} + {row.drugName} + {row.drugCode} + {row.patientDOB} + {row.status} + {row.dispenseStatus} + {row.auth_number} + + deleteSingleRow(event, row)}> + + + + {/* {row.metRequirements} */} + + )})} + +
+
+
+
+
+
+ ) + } +}; + +export default CaseCollection; \ No newline at end of file diff --git a/frontend/src/views/DataViews/Data.tsx b/frontend/src/views/DataViews/Data.tsx new file mode 100644 index 0000000..ac39a9e --- /dev/null +++ b/frontend/src/views/DataViews/Data.tsx @@ -0,0 +1,69 @@ +import { SyntheticEvent, useState } from 'react' +import { Box, Tab, Tabs } from '@mui/material'; +import { Container } from '@mui/system'; +import CaseCollection from './CaseCollection'; + +function a11yProps(index: number) { + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}` + }; + } +const Data = () => { + + const [allData, setAllData] = useState([]); + const [tabIndex, setValue] = useState(0); + + const handleChange = (event: SyntheticEvent, newValue: number) => { + setValue(newValue); + }; + + + + return ( + + + + + + + + + + + + + {tabIndex === 0 && ( + + + + )} + {tabIndex === 1 && ( + +
This is for etasu
+
+ )} + {tabIndex === 2 && ( + +
This is for other
+
+ )} +
+
+
+ +
+ ) + +} + +export default Data; \ No newline at end of file diff --git a/frontend/src/views/Login.tsx b/frontend/src/views/Login.tsx new file mode 100644 index 0000000..6b30157 --- /dev/null +++ b/frontend/src/views/Login.tsx @@ -0,0 +1,66 @@ +import { useState } from 'react' +import { Avatar, Box, Button, Container, CssBaseline, TextField, Typography } from '@mui/material'; +import LockOpenOutlinedIcon from '@mui/icons-material/LockOpenOutlined'; +import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; + +const Login = () => { + const [token, setToken] = useState(null); + + + const handleSubmit = () => { + console.log('handle submit'); + } + + return ( + + + + {token ? ( + + + + ) : ( + + + + )} + + Sign in + + + + + + + + + ) + +} + +export default Login; \ No newline at end of file diff --git a/frontend/src/views/Register.tsx b/frontend/src/views/Register.tsx new file mode 100644 index 0000000..0902a96 --- /dev/null +++ b/frontend/src/views/Register.tsx @@ -0,0 +1,13 @@ +import { useState } from 'react' + +const Register = () => { + + return ( +
+ Hi from register page +
+ ) + +} + +export default Register; \ No newline at end of file diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 0000000..f0a2350 --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..0d3d714 --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/src/ncpdp/script.ts b/src/ncpdp/script.ts index df4c210..a30c9ad 100644 --- a/src/ncpdp/script.ts +++ b/src/ncpdp/script.ts @@ -1,4 +1,4 @@ -import { Router, Request } from 'express'; +import { Router, Request, Response } from 'express'; import { remsCaseCollection } from '../fhir/models'; const router = Router(); @@ -17,6 +17,26 @@ router.post('/ncpdp/script', async (req: Request) => { } }); +router.get('/all/remscase', async (req: Request, res: Response) => { + try { + console.log('Getting all data'); + res.send(await remsCaseCollection.find({})); + } catch (error) { + console.log('ERROR getting data --> ', error); + throw error; + } +}); + +router.post('/remsCase/deleteOne', async (req: Request, res: Response) => { + try { + await remsCaseCollection.findByIdAndDelete({ _id: req.body.data.params?._id }); + res.send(`Deleted REMS Case collection cast number - ${req.body.data.params?.case_number}`); + } catch (error) { + console.log('ERROR deleting data --> ', error); + throw error; + } +}); + const handleRxFill = async (bundle: any) => { const rxfill = bundle.message?.body?.rxfill; From accf432ed869167766a965bd4d58709967ea0422 Mon Sep 17 00:00:00 2001 From: Ariel Virgulto Date: Fri, 20 Sep 2024 08:57:51 -0400 Subject: [PATCH 02/10] Latest rems admin ui stuff --- frontend/config.json | 6 + frontend/src/App.css | 6 + frontend/src/App.tsx | 50 +++--- .../src/views/DataViews/CaseCollection.tsx | 55 +++++-- frontend/src/views/DataViews/Data.tsx | 80 ++++----- frontend/src/views/DataViews/Medications.tsx | 132 +++++++++++++++ .../src/views/DataViews/MetRequirements.tsx | 152 ++++++++++++++++++ frontend/src/views/Login.tsx | 53 ++++-- src/lib/api_routes.ts | 64 ++++++++ src/ncpdp/script.ts | 20 --- src/server.ts | 7 + 11 files changed, 513 insertions(+), 112 deletions(-) create mode 100644 frontend/config.json create mode 100644 frontend/src/views/DataViews/Medications.tsx create mode 100644 frontend/src/views/DataViews/MetRequirements.tsx create mode 100644 src/lib/api_routes.ts diff --git a/frontend/config.json b/frontend/config.json new file mode 100644 index 0000000..2f2a382 --- /dev/null +++ b/frontend/config.json @@ -0,0 +1,6 @@ +{ + "realm": "ClientFhirServer", + "client": "app-login", + "auth": "http://localhost:8180/", + "scopeId": "pims" +} diff --git a/frontend/src/App.css b/frontend/src/App.css index 7603c51..78004a1 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -40,6 +40,12 @@ .white-btn { color: white !important; border-color: white !important; + margin-right: 5px !important; +} +.right-btn { + display: flex; + justify-content: flex-end; + margin: 10px 16px 10px 0; } .App h1 { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4c4cc11..bbe614c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,18 +1,30 @@ import './App.css' -import { BrowserRouter, Link, Route, Routes } from 'react-router-dom'; +import { useState } from 'react'; import { Button } from '@mui/material'; import Box from '@mui/material/Box'; import { Container } from '@mui/system'; import DatasetIcon from '@mui/icons-material/Dataset'; import Login from './views/Login'; -import Register from './views/Register'; import Data from './views/DataViews/Data'; +import axios from 'axios' + function App() { + const [token, setToken] = useState(null); + + const resetDB = async () => { + await axios + .post('http://localhost:8090/etasu/reset') + .then(function (response: any) { + console.log(response); +; }) + .catch((error: any) => { + console.log('Error resetting the DB -- > ', error); + }) + } return ( -
@@ -22,28 +34,22 @@ function App() { />

Rems Admin

-
- - - - - - - - - -
+ { token ? ( +
+ + +
+ ) : ( ) + }
- - - } /> - } /> - } /> - } /> - -
+ { token ? ( + + ) : ( + + ) + }
) } diff --git a/frontend/src/views/DataViews/CaseCollection.tsx b/frontend/src/views/DataViews/CaseCollection.tsx index c64e6a0..3e0b341 100644 --- a/frontend/src/views/DataViews/CaseCollection.tsx +++ b/frontend/src/views/DataViews/CaseCollection.tsx @@ -1,10 +1,8 @@ import axios from 'axios' import { useEffect, useState, SetStateAction } from 'react' import { - Box, Button, Card, - CardActions, CardContent, Paper, Table, @@ -12,11 +10,11 @@ import { TableCell, TableContainer, TableHead, - TableRow, - Typography + TableRow } from '@mui/material'; import IconButton from '@mui/material/IconButton'; import DeleteIcon from '@mui/icons-material/Delete'; +import { Refresh } from '@mui/icons-material'; export type RemsCase = { case_number?: string; @@ -35,8 +33,7 @@ export type RemsCase = { requirementName: string; stakeholderId: string; completed: boolean; - }[] - | null; + }[]; _id: string; }; const CaseCollection = () => { @@ -49,7 +46,7 @@ const CaseCollection = () => { }, []) const getAllRemsCase = async () => { - const url = 'http://localhost:8090/all/remscase'; + const url = 'http://localhost:8090/api/all/remscase'; await axios .get(url) .then(function (response: { data: SetStateAction; }) { @@ -63,14 +60,11 @@ const CaseCollection = () => { } const deleteSingleRow = async (event: any, row: RemsCase) => { - console.log('row to delete is -- > ', row); - const url = 'http://localhost:8090/remsCase/deleteOne'; + const url = 'http://localhost:8090/api/remsCase/deleteOne'; await axios .post(url, {data: { params: row}}) .then(function (response: { data: any; status: number; }) { - console.log('response data -- > ', response.data); if (response.status === 200) { - console.log('Response.data -- > ', response.data); getAllRemsCase(); } }) @@ -80,15 +74,46 @@ const CaseCollection = () => { }) } + const formattedReqs = (row: RemsCase) => { + let reqNames: String[] = [] + row.metRequirements.forEach((req: any) => { + const completed = req.completed ? 'Completed': 'Not completed'; + reqNames.push(`${req.requirementName}: ${completed}`); + }) + return reqNames.join(', '); + } + if (allData.length < 1 && !isLoading) { return ( +
+ +

No data

) } else { return ( +
+ +
@@ -105,13 +130,13 @@ const CaseCollection = () => { Status Dispense Status Authorization Number - {/* Met Requirements */} + Met Requirements Delete {allData.map(row => { - // console.log('row -- > ', row); + const metReq = formattedReqs(row); return ( {row.case_number} @@ -122,13 +147,13 @@ const CaseCollection = () => { {row.patientDOB} {row.status} {row.dispenseStatus} - {row.auth_number} + {row.auth_number} + {metReq} deleteSingleRow(event, row)}> - {/* {row.metRequirements} */} )})} diff --git a/frontend/src/views/DataViews/Data.tsx b/frontend/src/views/DataViews/Data.tsx index ac39a9e..9341e2e 100644 --- a/frontend/src/views/DataViews/Data.tsx +++ b/frontend/src/views/DataViews/Data.tsx @@ -1,7 +1,9 @@ import { SyntheticEvent, useState } from 'react' -import { Box, Tab, Tabs } from '@mui/material'; +import { Box, Tab, Tabs, Button } from '@mui/material'; import { Container } from '@mui/system'; import CaseCollection from './CaseCollection'; +import Medications from './Medications'; +import MetRequirements from './MetRequirements'; function a11yProps(index: number) { return { @@ -10,58 +12,56 @@ function a11yProps(index: number) { }; } const Data = () => { - - const [allData, setAllData] = useState([]); const [tabIndex, setValue] = useState(0); const handleChange = (event: SyntheticEvent, newValue: number) => { setValue(newValue); }; - - return ( - - - - - - - - - - - - - {tabIndex === 0 && ( - - +
+ + + + + + + + - )} - {tabIndex === 1 && ( + -
This is for etasu
+ + {tabIndex === 0 && ( + + + + )} + {tabIndex === 1 && ( + + + + )} + {tabIndex === 2 && ( + + + + )} - )} - {tabIndex === 2 && ( - -
This is for other
- )} -
- -
+ +
) } diff --git a/frontend/src/views/DataViews/Medications.tsx b/frontend/src/views/DataViews/Medications.tsx new file mode 100644 index 0000000..aa4db4d --- /dev/null +++ b/frontend/src/views/DataViews/Medications.tsx @@ -0,0 +1,132 @@ +import axios from 'axios' +import { useEffect, useState, SetStateAction } from 'react' +import { + Box, + Button, + Card, + CardActions, + CardContent, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography + } from '@mui/material'; +import { Refresh } from '@mui/icons-material'; + +export type Medication = { + code?: string; + codeSystem?: string; + name?: string; + requirements: Array<{ + name: string; + description: string; + resourceId: string; + appContext: string; + requiredToDispense: boolean; + }>; + _id: string; + }; + +const Medications = () => { + + const [allData, setAllData] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + getAllMedications(); + // getAllMetReqs(); + }, []) + + const getAllMedications = async () => { + const url = 'http://localhost:8090/api/all/medications'; + await axios + .get(url) + .then(function (response: { data: SetStateAction }) { + setAllData(response.data); + setIsLoading(false); + }) + .catch((error: any) => { + setIsLoading(false); + console.log('Error -- > ', error); + }) + } + + const formattedReqs = (row: Medication) => { + let reqNames: String[] = [] + row.requirements.forEach((req: any) => { + reqNames.push(req.name); + }) + return reqNames.join(', '); + } + + if (allData.length < 1 && !isLoading) { + return ( + +
+ +
+

No data

+
+ ) + } else { + return ( + + +
+ +
+ + + + + + + Name + Code + Code System + Requirements + + + + {allData.map(row => { + const format = formattedReqs(row); + return ( + + {row.name} + {row.code} + {row.codeSystem} + {format} + + )})} + +
+
+
+
+
+
+ ) + } +}; + +export default Medications; \ No newline at end of file diff --git a/frontend/src/views/DataViews/MetRequirements.tsx b/frontend/src/views/DataViews/MetRequirements.tsx new file mode 100644 index 0000000..f9a349a --- /dev/null +++ b/frontend/src/views/DataViews/MetRequirements.tsx @@ -0,0 +1,152 @@ +import axios from 'axios' +import { useEffect, useState, SetStateAction } from 'react' +import { + Box, + Button, + Card, + CardActions, + CardContent, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography + } from '@mui/material'; +import IconButton from '@mui/material/IconButton'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { Refresh } from '@mui/icons-material'; + +export type MetRequirements = { + drugName?: string; + requirementName?: string; + stakeholderId?: string; + completed?: boolean; + completedQuestionnaire?: { questionnaire: string; }; + case_numbers?: []; + _id: string; + }; + +const MetRequirements = () => { + + const [allData, setAllData] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + getAllMetReqs(); + }, []) + + const getAllMetReqs = async () => { + const url = 'http://localhost:8090/api/all/metreqs'; + await axios + .get(url) + .then(function (response: { data: any }) { + setAllData(response.data); + setIsLoading(false); + }) + .catch((error: any) => { + setIsLoading(false); + console.log('Error -- > ', error); + }) + } + + const deleteSingleRow = async (event: any, row: MetRequirements) => { + const url = 'http://localhost:8090/api/metreqs/deleteOne'; + await axios + .post(url, {data: { params: row}}) + .then(function (response: { data: any; status: number; }) { + if (response.status === 200) { + getAllMetReqs(); + } + }) + .catch((error: any) => { + setIsLoading(false); + console.log('Error -- > ', error); + }) + } + + const formattedQuestionnaire = (row: MetRequirements) => { + return row?.completedQuestionnaire?.questionnaire.split('http://localhost:8090/4_0_0/')[1]; + } + const formattedCompleted = (row: MetRequirements) => { + return row?.completed === true ? 'Yes' : 'No'; + } + + if (allData.length < 1 && !isLoading) { + return ( + +
+ +
+

No data

+
+ ) + } else { + return ( + + +
+ +
+ + + + + + + Drug Name + Requirement Name + Case Numbers + Completed + Completed Questionnaire + Delete + + + + {allData.map(row => { + const format = formattedQuestionnaire(row); + const complete = formattedCompleted(row); + return ( + + {row.drugName} + {row.requirementName} + {row.case_numbers?.join(", ")} + {complete} + {format} + + deleteSingleRow(event, row)}> + + + + + )})} + +
+
+
+
+
+
+ ) + } +}; + +export default MetRequirements; \ No newline at end of file diff --git a/frontend/src/views/Login.tsx b/frontend/src/views/Login.tsx index 6b30157..adb0555 100644 --- a/frontend/src/views/Login.tsx +++ b/frontend/src/views/Login.tsx @@ -1,14 +1,43 @@ -import { useState } from 'react' +import { SetStateAction } from 'react' +import axios from 'axios' import { Avatar, Box, Button, Container, CssBaseline, TextField, Typography } from '@mui/material'; -import LockOpenOutlinedIcon from '@mui/icons-material/LockOpenOutlined'; import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; +import config from '../../config.json'; -const Login = () => { - const [token, setToken] = useState(null); +const Login = (props) => { - - const handleSubmit = () => { - console.log('handle submit'); + const handleSubmit = (event: { preventDefault: () => void; currentTarget: HTMLFormElement | undefined; }) => { + event.preventDefault(); + const data = new FormData(event.currentTarget); + const user = data.get('username')?.toString(); + const pass = data.get('password')?.toString(); + if (user && pass) { + const params = new URLSearchParams(); + params.append('username', user); + params.append('password', pass); + params.append('grant_type', 'password'); + params.append('client_id', config.client); + axios + .post(`${config.auth}/realms/${config.realm}/protocol/openid-connect/token`, params, { + withCredentials: true + }) + .then((result: { data: { scope: string; access_token: SetStateAction; }; }) => { + // do something with the token + const scope = result.data.scope; + if (scope) { + props.tokenCallback(result.data.access_token); + } else { + console.error('Unauthorized User'); + } + }) + .catch(err => { + if (err.response.status === 401) { + console.error('Unknown user'); + } else { + console.error(err); + } + }); + } } return ( @@ -22,15 +51,9 @@ const Login = () => { alignItems: 'center' }} > - {token ? ( - - - - ) : ( - + - - )} + Sign in diff --git a/src/lib/api_routes.ts b/src/lib/api_routes.ts new file mode 100644 index 0000000..c009dba --- /dev/null +++ b/src/lib/api_routes.ts @@ -0,0 +1,64 @@ +import { Router, Request, Response } from 'express'; +import { + remsCaseCollection, + medicationCollection, + metRequirementsCollection +} from '../fhir/models'; +import axios from 'axios'; +const router = Router(); + +router.get('/all/remscase', async (req: Request, res: Response) => { + try { + console.log('Getting all data'); + res.send(await remsCaseCollection.find({})); + } catch (error) { + console.log('ERROR getting data --> ', error); + throw error; + } +}); + +router.post('/remsCase/deleteOne', async (req: Request, res: Response) => { + try { + // delete rems case collection + await remsCaseCollection.findByIdAndDelete({ _id: req.body.data.params?._id }); + // delete patient met requirements, either enrollment or status update forms + res.send(`Deleted REMS Case collection case number - ${req.body.data.params?.case_number}`); + } catch (error) { + console.log('ERROR deleting data --> ', error); + throw error; + } +}); + +router.get('/all/medications', async (req: Request, res: Response) => { + try { + console.log('Getting all data'); + res.send(await medicationCollection.find({})); + } catch (error) { + console.log('ERROR getting data --> ', error); + throw error; + } +}); + +router.get('/all/metreqs', async (req: Request, res: Response) => { + try { + console.log('Getting all data'); + res.send(await metRequirementsCollection.find({})); + } catch (error) { + console.log('ERROR getting data --> ', error); + throw error; + } +}); + +router.post('/metreqs/deleteOne', async (req: Request, res: Response) => { + try { + // delete rems case collection + await metRequirementsCollection.findByIdAndDelete({ _id: req.body.data.params?._id }); + // delete patient met requirements, either enrollment or status update forms + res.send(`Deleted met requirement with name - ${req.body.data.params?.requirementName}`); + } catch (error) { + console.log('ERROR deleting data --> ', error); + throw error; + } +}); + +export default router; diff --git a/src/ncpdp/script.ts b/src/ncpdp/script.ts index a30c9ad..e4055aa 100644 --- a/src/ncpdp/script.ts +++ b/src/ncpdp/script.ts @@ -17,26 +17,6 @@ router.post('/ncpdp/script', async (req: Request) => { } }); -router.get('/all/remscase', async (req: Request, res: Response) => { - try { - console.log('Getting all data'); - res.send(await remsCaseCollection.find({})); - } catch (error) { - console.log('ERROR getting data --> ', error); - throw error; - } -}); - -router.post('/remsCase/deleteOne', async (req: Request, res: Response) => { - try { - await remsCaseCollection.findByIdAndDelete({ _id: req.body.data.params?._id }); - res.send(`Deleted REMS Case collection cast number - ${req.body.data.params?.case_number}`); - } catch (error) { - console.log('ERROR deleting data --> ', error); - throw error; - } -}); - const handleRxFill = async (bundle: any) => { const rxfill = bundle.message?.body?.rxfill; diff --git a/src/server.ts b/src/server.ts index a2e2f8f..bfb47ef 100644 --- a/src/server.ts +++ b/src/server.ts @@ -10,6 +10,7 @@ import encounterStartService from './hooks/rems.encounterstart'; import { Server } from '@projecttacoma/node-fhir-server-core'; import Etasu from './lib/etasu'; import Ncpdp from './ncpdp/script'; +import Api from './lib/api_routes'; import env from 'env-var'; import https from 'https'; import fs from 'fs'; @@ -29,6 +30,7 @@ const initialize = (config: any) => { .registerCdsHooks(config.server) .configureEtasuEndpoints() .configureNCPDPEndpoints() + .configureUIEndpoints() .setErrorRoutes(); }; @@ -135,6 +137,11 @@ class REMSServer extends Server { return this; } + configureUIEndpoints() { + this.app.use('/api/', Api); + return this; + } + /** * @method listen * @description Start listening on the configured port From f3a0f78984cd895221b14eee0080663b29e32659 Mon Sep 17 00:00:00 2001 From: Ariel Virgulto Date: Fri, 20 Sep 2024 13:52:57 -0400 Subject: [PATCH 03/10] Updates to api call --- frontend/src/App.css | 6 ++ frontend/src/App.tsx | 1 + frontend/src/views/Data.tsx | 71 ++++++++++---------- frontend/src/views/DataViews/Medications.tsx | 1 - frontend/src/views/Login.tsx | 65 +++++++++--------- src/lib/api_routes.ts | 43 ++++++++---- 6 files changed, 109 insertions(+), 78 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index 78004a1..15c0226 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -73,3 +73,9 @@ display: grid; grid-template-areas: ' main right'; } + +.err-msg { + color: red; + font-style: italic; + text-align: center; +} \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index bbe614c..43f52d0 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -12,6 +12,7 @@ import axios from 'axios' function App() { const [token, setToken] = useState(null); + // to do - add popup for a 'are you sure' const resetDB = async () => { await axios .post('http://localhost:8090/etasu/reset') diff --git a/frontend/src/views/Data.tsx b/frontend/src/views/Data.tsx index e8e1faf..279c16e 100644 --- a/frontend/src/views/Data.tsx +++ b/frontend/src/views/Data.tsx @@ -3,6 +3,9 @@ import axios from 'axios' import { useEffect } from 'react' import { Box, Tab, Tabs } from '@mui/material'; import { Container } from '@mui/system'; +import CaseCollection from './DataViews/CaseCollection'; +import Medications from './DataViews/Medications'; +import MetRequirements from './DataViews/MetRequirements'; function a11yProps(index: number) { return { @@ -12,30 +15,29 @@ function a11yProps(index: number) { } const Data = () => { - const [allData, setAllData] = useState([]); + // const [allData, setAllData] = useState([]); const [tabIndex, setValue] = useState(0); const handleChange = (event: SyntheticEvent, newValue: number) => { setValue(newValue); }; - useEffect(() => { - getAllRemsCase(); - }, []) + // useEffect(() => { + // getAllRemsCase(); + // }, []) - const getAllRemsCase = async () => { - const url = 'http://localhost:8090/all/remscase'; - await axios - .get(url) - .then(function (response: { data: SetStateAction; }) { - console.log('Response is -- > ', response); - setAllData(response.data); + // const getAllRemsCase = async () => { + // const url = 'http://localhost:8090/all/remscase'; + // await axios + // .get(url) + // .then(function (response: { data: SetStateAction; }) { + // setAllData(response.data); - }) - .catch((error: any) => { - console.log('Error -- > ', error); - }) - } + // }) + // .catch((error: any) => { + // console.log('Error -- > ', error); + // }) + // } return ( @@ -52,29 +54,28 @@ const Data = () => { - - + + - - - {tabIndex === 0 && ( - -
This is for rems case
-
- )} - {tabIndex === 1 && ( - -
This is for etasu
+ + {tabIndex === 0 && ( + + + + )} + {tabIndex === 1 && ( + + + + )} + {tabIndex === 2 && ( + + + + )} - )} - {tabIndex === 2 && ( - -
This is for other
-
- )} -
diff --git a/frontend/src/views/DataViews/Medications.tsx b/frontend/src/views/DataViews/Medications.tsx index aa4db4d..71a889d 100644 --- a/frontend/src/views/DataViews/Medications.tsx +++ b/frontend/src/views/DataViews/Medications.tsx @@ -38,7 +38,6 @@ const Medications = () => { useEffect(() => { getAllMedications(); - // getAllMetReqs(); }, []) const getAllMedications = async () => { diff --git a/frontend/src/views/Login.tsx b/frontend/src/views/Login.tsx index adb0555..c02c1be 100644 --- a/frontend/src/views/Login.tsx +++ b/frontend/src/views/Login.tsx @@ -1,10 +1,11 @@ -import { SetStateAction } from 'react' +import { SetStateAction, useState } from 'react' import axios from 'axios' import { Avatar, Box, Button, Container, CssBaseline, TextField, Typography } from '@mui/material'; import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; import config from '../../config.json'; const Login = (props) => { + const [showMessage, setShowMessage] = useState(false); const handleSubmit = (event: { preventDefault: () => void; currentTarget: HTMLFormElement | undefined; }) => { event.preventDefault(); @@ -25,6 +26,7 @@ const Login = (props) => { // do something with the token const scope = result.data.scope; if (scope) { + setShowMessage(true); props.tokenCallback(result.data.access_token); } else { console.error('Unauthorized User'); @@ -33,6 +35,8 @@ const Login = (props) => { .catch(err => { if (err.response.status === 401) { console.error('Unknown user'); + setShowMessage(true); + } else { console.error(err); } @@ -51,35 +55,36 @@ const Login = (props) => { alignItems: 'center' }} > - - - - - Sign in - - - - - - + + + + + Sign in + + + + + + { showMessage ?

Error signing in. Please try again.

: ""}; +
) diff --git a/src/lib/api_routes.ts b/src/lib/api_routes.ts index c009dba..310e7a8 100644 --- a/src/lib/api_routes.ts +++ b/src/lib/api_routes.ts @@ -4,15 +4,14 @@ import { medicationCollection, metRequirementsCollection } from '../fhir/models'; -import axios from 'axios'; const router = Router(); router.get('/all/remscase', async (req: Request, res: Response) => { try { - console.log('Getting all data'); + console.log('Getting all rems case collection'); res.send(await remsCaseCollection.find({})); } catch (error) { - console.log('ERROR getting data --> ', error); + console.log('ERROR getting rems case collection --> ', error); throw error; } }); @@ -21,8 +20,29 @@ router.post('/remsCase/deleteOne', async (req: Request, res: Response) => { try { // delete rems case collection await remsCaseCollection.findByIdAndDelete({ _id: req.body.data.params?._id }); - // delete patient met requirements, either enrollment or status update forms - res.send(`Deleted REMS Case collection case number - ${req.body.data.params?.case_number}`); + // find and delete patient met requirements, either enrollment or status update forms + const allMatchedMetReqs = await metRequirementsCollection.find({ + case_numbers: { $in: req.body.data.params?.case_number } + }); + allMatchedMetReqs.forEach(async matchedReq => { + if (matchedReq.requirementName.includes('Patient')) { + await metRequirementsCollection.findOneAndDelete({ _id: matchedReq._id }); + } else { + // If not a patient form - remove case number from prescriber forms + await metRequirementsCollection.findOneAndUpdate( + { _id: matchedReq._id }, + { + case_numbers: matchedReq.case_numbers.filter( + num => num !== req.body.data.params?.case_number + ) + }, + { new: true } + ); + } + }); + res.send( + `Deleted REMS Case collection and patient forms with case number - ${req.body.data.params?.case_number}` + ); } catch (error) { console.log('ERROR deleting data --> ', error); throw error; @@ -31,32 +51,31 @@ router.post('/remsCase/deleteOne', async (req: Request, res: Response) => { router.get('/all/medications', async (req: Request, res: Response) => { try { - console.log('Getting all data'); + console.log('Getting all medications'); res.send(await medicationCollection.find({})); } catch (error) { - console.log('ERROR getting data --> ', error); + console.log('ERROR getting all medications --> ', error); throw error; } }); router.get('/all/metreqs', async (req: Request, res: Response) => { try { - console.log('Getting all data'); + console.log('Getting all met requirements'); res.send(await metRequirementsCollection.find({})); } catch (error) { - console.log('ERROR getting data --> ', error); + console.log('ERROR getting met requirements --> ', error); throw error; } }); router.post('/metreqs/deleteOne', async (req: Request, res: Response) => { try { - // delete rems case collection + // delete met requirements await metRequirementsCollection.findByIdAndDelete({ _id: req.body.data.params?._id }); - // delete patient met requirements, either enrollment or status update forms res.send(`Deleted met requirement with name - ${req.body.data.params?.requirementName}`); } catch (error) { - console.log('ERROR deleting data --> ', error); + console.log('ERROR deleting met requirement --> ', error); throw error; } }); From fa68fd5e077c8aafa60f3935a2a57840ac7de902 Mon Sep 17 00:00:00 2001 From: Ariel Virgulto Date: Sun, 22 Sep 2024 22:41:02 -0400 Subject: [PATCH 04/10] Updated reset DB --- frontend/src/App.tsx | 43 +++++++++++++++++-- .../src/views/DataViews/CaseCollection.tsx | 8 +++- frontend/src/views/DataViews/Data.tsx | 10 ++--- frontend/src/views/DataViews/Medications.tsx | 8 +++- .../src/views/DataViews/MetRequirements.tsx | 8 +++- 5 files changed, 66 insertions(+), 11 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 43f52d0..b15171f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -7,17 +7,33 @@ import DatasetIcon from '@mui/icons-material/Dataset'; import Login from './views/Login'; import Data from './views/DataViews/Data'; import axios from 'axios' +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; function App() { const [token, setToken] = useState(null); + const [open, setOpen] = useState(false); + const [forceRefresh, setForceRefresh] = useState(false); + + const handleClickOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; - // to do - add popup for a 'are you sure' const resetDB = async () => { + setOpen(false); await axios .post('http://localhost:8090/etasu/reset') .then(function (response: any) { console.log(response); + setForceRefresh(true); ; }) .catch((error: any) => { console.log('Error resetting the DB -- > ', error); @@ -37,16 +53,37 @@ function App() { { token ? (
- +
) : ( ) } + + + {"Reset Rems Admin Database?"} + + + + Resetting the rems admin database will delete any existing rems case information and completed questionnaires. + + + + + + + { token ? ( - + ) : ( ) diff --git a/frontend/src/views/DataViews/CaseCollection.tsx b/frontend/src/views/DataViews/CaseCollection.tsx index 3e0b341..d1b1a72 100644 --- a/frontend/src/views/DataViews/CaseCollection.tsx +++ b/frontend/src/views/DataViews/CaseCollection.tsx @@ -36,11 +36,17 @@ export type RemsCase = { }[]; _id: string; }; -const CaseCollection = () => { +const CaseCollection = (props: { refresh: boolean; }) => { const [allData, setAllData] = useState([]); const [isLoading, setIsLoading] = useState(true); + useEffect(() => { + if (props.refresh) { + getAllRemsCase() + } + }, [props.refresh]) + useEffect(() => { getAllRemsCase(); }, []) diff --git a/frontend/src/views/DataViews/Data.tsx b/frontend/src/views/DataViews/Data.tsx index 9341e2e..903f1b1 100644 --- a/frontend/src/views/DataViews/Data.tsx +++ b/frontend/src/views/DataViews/Data.tsx @@ -1,5 +1,5 @@ import { SyntheticEvent, useState } from 'react' -import { Box, Tab, Tabs, Button } from '@mui/material'; +import { Box, Tab, Tabs } from '@mui/material'; import { Container } from '@mui/system'; import CaseCollection from './CaseCollection'; import Medications from './Medications'; @@ -11,7 +11,7 @@ function a11yProps(index: number) { 'aria-controls': `simple-tabpanel-${index}` }; } -const Data = () => { +const Data = (props: { refresh: boolean; }) => { const [tabIndex, setValue] = useState(0); const handleChange = (event: SyntheticEvent, newValue: number) => { @@ -43,17 +43,17 @@ const Data = () => { {tabIndex === 0 && ( - + )} {tabIndex === 1 && ( - + )} {tabIndex === 2 && ( - + )} diff --git a/frontend/src/views/DataViews/Medications.tsx b/frontend/src/views/DataViews/Medications.tsx index 71a889d..982ce41 100644 --- a/frontend/src/views/DataViews/Medications.tsx +++ b/frontend/src/views/DataViews/Medications.tsx @@ -31,11 +31,17 @@ export type Medication = { _id: string; }; -const Medications = () => { +const Medications = (props: { refresh: boolean; }) => { const [allData, setAllData] = useState([]); const [isLoading, setIsLoading] = useState(true); + useEffect(() => { + if (props.refresh) { + getAllMedications() + } + }, [props.refresh]) + useEffect(() => { getAllMedications(); }, []) diff --git a/frontend/src/views/DataViews/MetRequirements.tsx b/frontend/src/views/DataViews/MetRequirements.tsx index f9a349a..e31cb5c 100644 --- a/frontend/src/views/DataViews/MetRequirements.tsx +++ b/frontend/src/views/DataViews/MetRequirements.tsx @@ -29,11 +29,17 @@ export type MetRequirements = { _id: string; }; -const MetRequirements = () => { +const MetRequirements = (props: { refresh: boolean; }) => { const [allData, setAllData] = useState([]); const [isLoading, setIsLoading] = useState(true); + useEffect(() => { + if (props.refresh) { + getAllMetReqs() + } + }, [props.refresh]) + useEffect(() => { getAllMetReqs(); }, []) From 561b74ba481183ddce0045833734d8a6303a8228 Mon Sep 17 00:00:00 2001 From: Ariel Virgulto Date: Sun, 22 Sep 2024 22:50:39 -0400 Subject: [PATCH 05/10] Remove unused files --- frontend/src/views/Data.tsx | 87 --------------------------------- frontend/src/views/Register.tsx | 13 ----- 2 files changed, 100 deletions(-) delete mode 100644 frontend/src/views/Data.tsx delete mode 100644 frontend/src/views/Register.tsx diff --git a/frontend/src/views/Data.tsx b/frontend/src/views/Data.tsx deleted file mode 100644 index 279c16e..0000000 --- a/frontend/src/views/Data.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { SetStateAction, SyntheticEvent, useState } from 'react' -import axios from 'axios' -import { useEffect } from 'react' -import { Box, Tab, Tabs } from '@mui/material'; -import { Container } from '@mui/system'; -import CaseCollection from './DataViews/CaseCollection'; -import Medications from './DataViews/Medications'; -import MetRequirements from './DataViews/MetRequirements'; - -function a11yProps(index: number) { - return { - id: `simple-tab-${index}`, - 'aria-controls': `simple-tabpanel-${index}` - }; - } -const Data = () => { - - // const [allData, setAllData] = useState([]); - const [tabIndex, setValue] = useState(0); - - const handleChange = (event: SyntheticEvent, newValue: number) => { - setValue(newValue); - }; - - // useEffect(() => { - // getAllRemsCase(); - // }, []) - - // const getAllRemsCase = async () => { - // const url = 'http://localhost:8090/all/remscase'; - // await axios - // .get(url) - // .then(function (response: { data: SetStateAction; }) { - // setAllData(response.data); - - // }) - // .catch((error: any) => { - // console.log('Error -- > ', error); - // }) - // } - - return ( - - - - - - - - - - - - {tabIndex === 0 && ( - - - - )} - {tabIndex === 1 && ( - - - - )} - {tabIndex === 2 && ( - - - - )} - - - - - - ) - -} - -export default Data; \ No newline at end of file diff --git a/frontend/src/views/Register.tsx b/frontend/src/views/Register.tsx deleted file mode 100644 index 0902a96..0000000 --- a/frontend/src/views/Register.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { useState } from 'react' - -const Register = () => { - - return ( -
- Hi from register page -
- ) - -} - -export default Register; \ No newline at end of file From 2d1e5996ab52de6cec371bdae8345d35dfec3a3e Mon Sep 17 00:00:00 2001 From: Ariel Virgulto Date: Mon, 23 Sep 2024 09:33:11 -0400 Subject: [PATCH 06/10] Fix eslint and prettier --- frontend/eslint.config.js | 25 +- frontend/src/App.tsx | 108 +++--- frontend/src/main.tsx | 12 +- .../src/views/DataViews/CaseCollection.tsx | 327 +++++++++--------- frontend/src/views/DataViews/Data.tsx | 112 +++--- frontend/src/views/DataViews/Medications.tsx | 250 ++++++------- .../src/views/DataViews/MetRequirements.tsx | 287 +++++++-------- frontend/src/views/Login.tsx | 173 ++++----- frontend/vite.config.ts | 8 +- src/ncpdp/script.ts | 2 +- 10 files changed, 653 insertions(+), 651 deletions(-) diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 092408a..f3bd4ef 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -1,8 +1,8 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ['dist'] }, @@ -11,18 +11,15 @@ export default tseslint.config( files: ['**/*.{ts,tsx}'], languageOptions: { ecmaVersion: 2020, - globals: globals.browser, + globals: globals.browser }, plugins: { 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, + 'react-refresh': reactRefresh }, rules: { ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, - }, -) + 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }] + } + } +); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b15171f..895151d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,4 @@ -import './App.css' +import './App.css'; import { useState } from 'react'; import { Button } from '@mui/material'; import Box from '@mui/material/Box'; @@ -6,14 +6,13 @@ import { Container } from '@mui/system'; import DatasetIcon from '@mui/icons-material/Dataset'; import Login from './views/Login'; import Data from './views/DataViews/Data'; -import axios from 'axios' +import axios from 'axios'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; - function App() { const [token, setToken] = useState(null); const [open, setOpen] = useState(false); @@ -30,66 +29,65 @@ function App() { const resetDB = async () => { setOpen(false); await axios - .post('http://localhost:8090/etasu/reset') - .then(function (response: any) { + .post('http://localhost:8090/etasu/reset') + .then(function (response: any) { console.log(response); setForceRefresh(true); -; }) - .catch((error: any) => { + }) + .catch((error: any) => { console.log('Error resetting the DB -- > ', error); - }) - } + }); + }; return ( -
- -
-
- -

Rems Admin

-
- { token ? ( -
- - -
- ) : ( ) - } +
+ +
+
+ +

Rems Admin

+
+ {token ? ( +
+ +
- - - - {"Reset Rems Admin Database?"} - - - - Resetting the rems admin database will delete any existing rems case information and completed questionnaires. - - - - - - - -
- { token ? ( - ) : ( - - ) - } + + )} +
+ + + {'Reset Rems Admin Database?'} + + + Resetting the rems admin database will delete any existing rems case information and + completed questionnaires. + + + + + + + +
+ {token ? : } - ) + ); } -export default App +export default App; diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 6f4ac9b..ea9e363 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,10 +1,10 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import App from './App.tsx' -import './index.css' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import App from './App.tsx'; +import './index.css'; createRoot(document.getElementById('root')!).render( - , -) + +); diff --git a/frontend/src/views/DataViews/CaseCollection.tsx b/frontend/src/views/DataViews/CaseCollection.tsx index d1b1a72..fa97399 100644 --- a/frontend/src/views/DataViews/CaseCollection.tsx +++ b/frontend/src/views/DataViews/CaseCollection.tsx @@ -1,176 +1,179 @@ -import axios from 'axios' -import { useEffect, useState, SetStateAction } from 'react' +import axios from 'axios'; +import { useEffect, useState, SetStateAction } from 'react'; import { - Button, - Card, - CardContent, - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow - } from '@mui/material'; + Button, + Card, + CardContent, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow +} from '@mui/material'; import IconButton from '@mui/material/IconButton'; import DeleteIcon from '@mui/icons-material/Delete'; import { Refresh } from '@mui/icons-material'; export type RemsCase = { - case_number?: string; - auth_number?: string; - patientFirstName?: string; - patientLastName?: string; - patientDOB?: string; - drugCode?: string; - drugName?: number; - status?: string; - dispenseStatus?: string; - metRequirements: - | { - metRequirementId: string; - requirementsDescription: string; - requirementName: string; - stakeholderId: string; - completed: boolean; - }[]; - _id: string; - }; -const CaseCollection = (props: { refresh: boolean; }) => { - - const [allData, setAllData] = useState([]); - const [isLoading, setIsLoading] = useState(true); + case_number?: string; + auth_number?: string; + patientFirstName?: string; + patientLastName?: string; + patientDOB?: string; + drugCode?: string; + drugName?: number; + status?: string; + dispenseStatus?: string; + metRequirements: + | { + metRequirementId: string; + requirementsDescription: string; + requirementName: string; + stakeholderId: string; + completed: boolean; + }[]; + _id: string; +}; +const CaseCollection = (props: { refresh: boolean }) => { + const [allData, setAllData] = useState([]); + const [isLoading, setIsLoading] = useState(true); - useEffect(() => { - if (props.refresh) { - getAllRemsCase() - } - }, [props.refresh]) + useEffect(() => { + if (props.refresh) { + getAllRemsCase(); + } + }, [props.refresh]); - useEffect(() => { - getAllRemsCase(); - }, []) + useEffect(() => { + getAllRemsCase(); + }, []); - const getAllRemsCase = async () => { - const url = 'http://localhost:8090/api/all/remscase'; - await axios - .get(url) - .then(function (response: { data: SetStateAction; }) { - setAllData(response.data); - setIsLoading(false); - }) - .catch((error: any) => { - setIsLoading(false); - console.log('Error -- > ', error); - }) - } + const getAllRemsCase = async () => { + const url = 'http://localhost:8090/api/all/remscase'; + await axios + .get(url) + .then(function (response: { data: SetStateAction }) { + setAllData(response.data); + setIsLoading(false); + }) + .catch((error: any) => { + setIsLoading(false); + console.log('Error -- > ', error); + }); + }; - const deleteSingleRow = async (event: any, row: RemsCase) => { - const url = 'http://localhost:8090/api/remsCase/deleteOne'; - await axios - .post(url, {data: { params: row}}) - .then(function (response: { data: any; status: number; }) { - if (response.status === 200) { - getAllRemsCase(); - } - }) - .catch((error: any) => { - setIsLoading(false); - console.log('Error -- > ', error); - }) - } + const deleteSingleRow = async (event: any, row: RemsCase) => { + const url = 'http://localhost:8090/api/remsCase/deleteOne'; + await axios + .post(url, { data: { params: row } }) + .then(function (response: { data: any; status: number }) { + if (response.status === 200) { + getAllRemsCase(); + } + }) + .catch((error: any) => { + setIsLoading(false); + console.log('Error -- > ', error); + }); + }; - const formattedReqs = (row: RemsCase) => { - let reqNames: String[] = [] - row.metRequirements.forEach((req: any) => { - const completed = req.completed ? 'Completed': 'Not completed'; - reqNames.push(`${req.requirementName}: ${completed}`); - }) - return reqNames.join(', '); - } + const formattedReqs = (row: RemsCase) => { + let reqNames: String[] = []; + row.metRequirements.forEach((req: any) => { + const completed = req.completed ? 'Completed' : 'Not completed'; + reqNames.push(`${req.requirementName}: ${completed}`); + }); + return reqNames.join(', '); + }; - if (allData.length < 1 && !isLoading) { - return ( - -
- -
-

No data

-
- ) - } else { - return ( - -
- -
- - - - - - - - Case Number - Patient First Name - Patient Last Name - Drug Name - Drug Code - Patient DOB - Status - Dispense Status - Authorization Number - Met Requirements - Delete - - - - {allData.map(row => { - const metReq = formattedReqs(row); - return ( - - {row.case_number} - {row.patientFirstName} - {row.patientLastName} - {row.drugName} - {row.drugCode} - {row.patientDOB} - {row.status} - {row.dispenseStatus} - {row.auth_number} - {metReq} - - deleteSingleRow(event, row)}> - - - - - )})} - -
-
-
-
-
-
- ) - } + if (allData.length < 1 && !isLoading) { + return ( + +
+ +
+

No data

+
+ ); + } else { + return ( + +
+ +
+ + + + + + + + Case Number + Patient First Name + Patient Last Name + Drug Name + Drug Code + Patient DOB + Status + Dispense Status + Authorization Number + Met Requirements + Delete + + + + {allData.map(row => { + const metReq = formattedReqs(row); + return ( + + {row.case_number} + {row.patientFirstName} + {row.patientLastName} + {row.drugName} + {row.drugCode} + {row.patientDOB} + {row.status} + {row.dispenseStatus} + {row.auth_number} + {metReq} + + deleteSingleRow(event, row)} + > + + + + + ); + })} + +
+
+
+
+
+
+ ); + } }; -export default CaseCollection; \ No newline at end of file +export default CaseCollection; diff --git a/frontend/src/views/DataViews/Data.tsx b/frontend/src/views/DataViews/Data.tsx index 903f1b1..5c5e30e 100644 --- a/frontend/src/views/DataViews/Data.tsx +++ b/frontend/src/views/DataViews/Data.tsx @@ -1,4 +1,4 @@ -import { SyntheticEvent, useState } from 'react' +import { SyntheticEvent, useState } from 'react'; import { Box, Tab, Tabs } from '@mui/material'; import { Container } from '@mui/system'; import CaseCollection from './CaseCollection'; @@ -6,64 +6,62 @@ import Medications from './Medications'; import MetRequirements from './MetRequirements'; function a11yProps(index: number) { - return { - id: `simple-tab-${index}`, - 'aria-controls': `simple-tabpanel-${index}` - }; - } -const Data = (props: { refresh: boolean; }) => { - const [tabIndex, setValue] = useState(0); + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}` + }; +} +const Data = (props: { refresh: boolean }) => { + const [tabIndex, setValue] = useState(0); - const handleChange = (event: SyntheticEvent, newValue: number) => { - setValue(newValue); - }; + const handleChange = (event: SyntheticEvent, newValue: number) => { + setValue(newValue); + }; - return ( -
- - - - - - - - - + return ( +
+ + + + + + + + + - - - {tabIndex === 0 && ( - - - - )} - {tabIndex === 1 && ( - - - - )} - {tabIndex === 2 && ( - - - - )} - - + + + {tabIndex === 0 && ( + + + )} + {tabIndex === 1 && ( + + + + )} + {tabIndex === 2 && ( + + + + )} + + + + +
+ ); +}; -
-
- ) - -} - -export default Data; \ No newline at end of file +export default Data; diff --git a/frontend/src/views/DataViews/Medications.tsx b/frontend/src/views/DataViews/Medications.tsx index 982ce41..6096291 100644 --- a/frontend/src/views/DataViews/Medications.tsx +++ b/frontend/src/views/DataViews/Medications.tsx @@ -1,137 +1,137 @@ -import axios from 'axios' -import { useEffect, useState, SetStateAction } from 'react' +import axios from 'axios'; +import { useEffect, useState, SetStateAction } from 'react'; import { - Box, - Button, - Card, - CardActions, - CardContent, - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Typography - } from '@mui/material'; + Box, + Button, + Card, + CardActions, + CardContent, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography +} from '@mui/material'; import { Refresh } from '@mui/icons-material'; export type Medication = { - code?: string; - codeSystem?: string; - name?: string; - requirements: Array<{ - name: string; - description: string; - resourceId: string; - appContext: string; - requiredToDispense: boolean; - }>; - _id: string; - }; - -const Medications = (props: { refresh: boolean; }) => { + code?: string; + codeSystem?: string; + name?: string; + requirements: Array<{ + name: string; + description: string; + resourceId: string; + appContext: string; + requiredToDispense: boolean; + }>; + _id: string; +}; - const [allData, setAllData] = useState([]); - const [isLoading, setIsLoading] = useState(true); +const Medications = (props: { refresh: boolean }) => { + const [allData, setAllData] = useState([]); + const [isLoading, setIsLoading] = useState(true); - useEffect(() => { - if (props.refresh) { - getAllMedications() - } - }, [props.refresh]) + useEffect(() => { + if (props.refresh) { + getAllMedications(); + } + }, [props.refresh]); - useEffect(() => { - getAllMedications(); - }, []) + useEffect(() => { + getAllMedications(); + }, []); - const getAllMedications = async () => { - const url = 'http://localhost:8090/api/all/medications'; - await axios - .get(url) - .then(function (response: { data: SetStateAction }) { - setAllData(response.data); - setIsLoading(false); - }) - .catch((error: any) => { - setIsLoading(false); - console.log('Error -- > ', error); - }) - } + const getAllMedications = async () => { + const url = 'http://localhost:8090/api/all/medications'; + await axios + .get(url) + .then(function (response: { data: SetStateAction }) { + setAllData(response.data); + setIsLoading(false); + }) + .catch((error: any) => { + setIsLoading(false); + console.log('Error -- > ', error); + }); + }; - const formattedReqs = (row: Medication) => { - let reqNames: String[] = [] - row.requirements.forEach((req: any) => { - reqNames.push(req.name); - }) - return reqNames.join(', '); - } + const formattedReqs = (row: Medication) => { + let reqNames: String[] = []; + row.requirements.forEach((req: any) => { + reqNames.push(req.name); + }); + return reqNames.join(', '); + }; - if (allData.length < 1 && !isLoading) { - return ( - -
- -
-

No data

-
- ) - } else { - return ( - - -
- -
- - - - - - - Name - Code - Code System - Requirements - - - - {allData.map(row => { - const format = formattedReqs(row); - return ( - - {row.name} - {row.code} - {row.codeSystem} - {format} - - )})} - -
-
-
-
-
-
- ) - } + if (allData.length < 1 && !isLoading) { + return ( + +
+ +
+

No data

+
+ ); + } else { + return ( + + +
+ +
+ + + + + + + Name + Code + Code System + Requirements + + + + {allData.map(row => { + const format = formattedReqs(row); + return ( + + {row.name} + {row.code} + {row.codeSystem} + {format} + + ); + })} + +
+
+
+
+
+
+ ); + } }; -export default Medications; \ No newline at end of file +export default Medications; diff --git a/frontend/src/views/DataViews/MetRequirements.tsx b/frontend/src/views/DataViews/MetRequirements.tsx index e31cb5c..806f6fc 100644 --- a/frontend/src/views/DataViews/MetRequirements.tsx +++ b/frontend/src/views/DataViews/MetRequirements.tsx @@ -1,158 +1,161 @@ -import axios from 'axios' -import { useEffect, useState, SetStateAction } from 'react' +import axios from 'axios'; +import { useEffect, useState, SetStateAction } from 'react'; import { - Box, - Button, - Card, - CardActions, - CardContent, - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Typography - } from '@mui/material'; + Box, + Button, + Card, + CardActions, + CardContent, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography +} from '@mui/material'; import IconButton from '@mui/material/IconButton'; import DeleteIcon from '@mui/icons-material/Delete'; import { Refresh } from '@mui/icons-material'; export type MetRequirements = { - drugName?: string; - requirementName?: string; - stakeholderId?: string; - completed?: boolean; - completedQuestionnaire?: { questionnaire: string; }; - case_numbers?: []; - _id: string; - }; - -const MetRequirements = (props: { refresh: boolean; }) => { + drugName?: string; + requirementName?: string; + stakeholderId?: string; + completed?: boolean; + completedQuestionnaire?: { questionnaire: string }; + case_numbers?: []; + _id: string; +}; - const [allData, setAllData] = useState([]); - const [isLoading, setIsLoading] = useState(true); +const MetRequirements = (props: { refresh: boolean }) => { + const [allData, setAllData] = useState([]); + const [isLoading, setIsLoading] = useState(true); - useEffect(() => { - if (props.refresh) { - getAllMetReqs() - } - }, [props.refresh]) + useEffect(() => { + if (props.refresh) { + getAllMetReqs(); + } + }, [props.refresh]); - useEffect(() => { - getAllMetReqs(); - }, []) + useEffect(() => { + getAllMetReqs(); + }, []); - const getAllMetReqs = async () => { - const url = 'http://localhost:8090/api/all/metreqs'; - await axios - .get(url) - .then(function (response: { data: any }) { - setAllData(response.data); - setIsLoading(false); - }) - .catch((error: any) => { - setIsLoading(false); - console.log('Error -- > ', error); - }) - } + const getAllMetReqs = async () => { + const url = 'http://localhost:8090/api/all/metreqs'; + await axios + .get(url) + .then(function (response: { data: any }) { + setAllData(response.data); + setIsLoading(false); + }) + .catch((error: any) => { + setIsLoading(false); + console.log('Error -- > ', error); + }); + }; - const deleteSingleRow = async (event: any, row: MetRequirements) => { - const url = 'http://localhost:8090/api/metreqs/deleteOne'; - await axios - .post(url, {data: { params: row}}) - .then(function (response: { data: any; status: number; }) { - if (response.status === 200) { - getAllMetReqs(); - } - }) - .catch((error: any) => { - setIsLoading(false); - console.log('Error -- > ', error); - }) - } + const deleteSingleRow = async (event: any, row: MetRequirements) => { + const url = 'http://localhost:8090/api/metreqs/deleteOne'; + await axios + .post(url, { data: { params: row } }) + .then(function (response: { data: any; status: number }) { + if (response.status === 200) { + getAllMetReqs(); + } + }) + .catch((error: any) => { + setIsLoading(false); + console.log('Error -- > ', error); + }); + }; - const formattedQuestionnaire = (row: MetRequirements) => { - return row?.completedQuestionnaire?.questionnaire.split('http://localhost:8090/4_0_0/')[1]; - } - const formattedCompleted = (row: MetRequirements) => { - return row?.completed === true ? 'Yes' : 'No'; - } + const formattedQuestionnaire = (row: MetRequirements) => { + return row?.completedQuestionnaire?.questionnaire.split('http://localhost:8090/4_0_0/')[1]; + }; + const formattedCompleted = (row: MetRequirements) => { + return row?.completed === true ? 'Yes' : 'No'; + }; - if (allData.length < 1 && !isLoading) { - return ( - -
- -
-

No data

-
- ) - } else { - return ( - - -
- +
+

No data

+
+ ); + } else { + return ( + + +
+ +
+ + + + + + + Drug Name + Requirement Name + Case Numbers + Completed + Completed Questionnaire + Delete + + + + {allData.map(row => { + const format = formattedQuestionnaire(row); + const complete = formattedCompleted(row); + return ( + + {row.drugName} + {row.requirementName} + {row.case_numbers?.join(', ')} + {complete} + {format} + + deleteSingleRow(event, row)} > - Refresh - - - - - -
- - - Drug Name - Requirement Name - Case Numbers - Completed - Completed Questionnaire - Delete - - - - {allData.map(row => { - const format = formattedQuestionnaire(row); - const complete = formattedCompleted(row); - return ( - - {row.drugName} - {row.requirementName} - {row.case_numbers?.join(", ")} - {complete} - {format} - - deleteSingleRow(event, row)}> - - - - - )})} - -
-
-
-
-
-
- ) - } + + + + + ); + })} + + + + +
+ + + ); + } }; -export default MetRequirements; \ No newline at end of file +export default MetRequirements; diff --git a/frontend/src/views/Login.tsx b/frontend/src/views/Login.tsx index c02c1be..0ccaa7c 100644 --- a/frontend/src/views/Login.tsx +++ b/frontend/src/views/Login.tsx @@ -1,94 +1,97 @@ -import { SetStateAction, useState } from 'react' -import axios from 'axios' +import { SetStateAction, useState } from 'react'; +import axios from 'axios'; import { Avatar, Box, Button, Container, CssBaseline, TextField, Typography } from '@mui/material'; import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; import config from '../../config.json'; -const Login = (props) => { +const Login = props => { const [showMessage, setShowMessage] = useState(false); - const handleSubmit = (event: { preventDefault: () => void; currentTarget: HTMLFormElement | undefined; }) => { - event.preventDefault(); - const data = new FormData(event.currentTarget); - const user = data.get('username')?.toString(); - const pass = data.get('password')?.toString(); - if (user && pass) { - const params = new URLSearchParams(); - params.append('username', user); - params.append('password', pass); - params.append('grant_type', 'password'); - params.append('client_id', config.client); - axios - .post(`${config.auth}/realms/${config.realm}/protocol/openid-connect/token`, params, { - withCredentials: true - }) - .then((result: { data: { scope: string; access_token: SetStateAction; }; }) => { - // do something with the token - const scope = result.data.scope; - if (scope) { - setShowMessage(true); - props.tokenCallback(result.data.access_token); - } else { - console.error('Unauthorized User'); - } - }) - .catch(err => { - if (err.response.status === 401) { - console.error('Unknown user'); - setShowMessage(true); - - } else { - console.error(err); - } - }); - } + const handleSubmit = (event: { + preventDefault: () => void; + currentTarget: HTMLFormElement | undefined; + }) => { + event.preventDefault(); + const data = new FormData(event.currentTarget); + const user = data.get('username')?.toString(); + const pass = data.get('password')?.toString(); + if (user && pass) { + const params = new URLSearchParams(); + params.append('username', user); + params.append('password', pass); + params.append('grant_type', 'password'); + params.append('client_id', config.client); + axios + .post(`${config.auth}/realms/${config.realm}/protocol/openid-connect/token`, params, { + withCredentials: true + }) + .then( + (result: { data: { scope: string; access_token: SetStateAction } }) => { + // do something with the token + const scope = result.data.scope; + if (scope) { + setShowMessage(true); + props.tokenCallback(result.data.access_token); + } else { + console.error('Unauthorized User'); + } + } + ) + .catch(err => { + if (err.response.status === 401) { + console.error('Unknown user'); + setShowMessage(true); + } else { + console.error(err); + } + }); } + }; - return ( - - - - - - - - Sign in - - - - - - { showMessage ?

Error signing in. Please try again.

: ""}; -
-
-
- ) - -} + return ( + + + + + + + + Sign in + + + + + + {showMessage ?

Error signing in. Please try again.

: ''}; +
+
+
+ ); +}; -export default Login; \ No newline at end of file +export default Login; diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 5a33944..6549182 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,7 +1,7 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], -}) + plugins: [react()] +}); diff --git a/src/ncpdp/script.ts b/src/ncpdp/script.ts index e4055aa..df4c210 100644 --- a/src/ncpdp/script.ts +++ b/src/ncpdp/script.ts @@ -1,4 +1,4 @@ -import { Router, Request, Response } from 'express'; +import { Router, Request } from 'express'; import { remsCaseCollection } from '../fhir/models'; const router = Router(); From 60161eeaf1d0ff4fb635279590f94724405c61f7 Mon Sep 17 00:00:00 2001 From: Ariel Virgulto Date: Thu, 26 Sep 2024 10:00:24 -0400 Subject: [PATCH 07/10] remove extra ; and change header color --- frontend/src/App.css | 4 ++-- frontend/src/views/Login.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index 15c0226..d1b03b7 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,6 +1,6 @@ .App { text-align: center; - background-color: #005B94; + background-color: #2F6A47; padding: 20px; margin-bottom: 20px; } @@ -17,7 +17,7 @@ } .App-header { - background-color: #282c34; + background-color: #2F6A47; min-height: 100vh; display: flex; flex-direction: column; diff --git a/frontend/src/views/Login.tsx b/frontend/src/views/Login.tsx index 0ccaa7c..73fb156 100644 --- a/frontend/src/views/Login.tsx +++ b/frontend/src/views/Login.tsx @@ -87,7 +87,7 @@ const Login = props => { - {showMessage ?

Error signing in. Please try again.

: ''}; + {showMessage ?

Error signing in. Please try again.

: ''}
From aa9633bc312508be607508a49f19289769fd80e1 Mon Sep 17 00:00:00 2001 From: Ariel Virgulto Date: Thu, 26 Sep 2024 10:01:26 -0400 Subject: [PATCH 08/10] change start command to npm start --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 4035ce9..7f69441 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", + "start": "vite", "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview" From 41064a5c19cd9dfed79fa38895f7b0a1fa0e0871 Mon Sep 17 00:00:00 2001 From: Ariel Virgulto Date: Thu, 26 Sep 2024 11:23:39 -0400 Subject: [PATCH 09/10] Update button colors throughout app --- ... 2024-09-11 at 11.57.35\342\200\257AM.png" | Bin 3736 -> 0 bytes frontend/public/admin.png | Bin 3736 -> 1392 bytes .../src/views/DataViews/CaseCollection.tsx | 1 + frontend/src/views/DataViews/Medications.tsx | 1 + .../src/views/DataViews/MetRequirements.tsx | 2 ++ frontend/src/views/Login.tsx | 2 +- src/rems-cds-hooks | 2 +- 7 files changed, 6 insertions(+), 2 deletions(-) delete mode 100644 "frontend/public/Screenshot 2024-09-11 at 11.57.35\342\200\257AM.png" diff --git "a/frontend/public/Screenshot 2024-09-11 at 11.57.35\342\200\257AM.png" "b/frontend/public/Screenshot 2024-09-11 at 11.57.35\342\200\257AM.png" deleted file mode 100644 index 968533906f596019354daa250e8a52d073eecd6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3736 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 diff --git a/frontend/public/admin.png b/frontend/public/admin.png index 968533906f596019354daa250e8a52d073eecd6f..6691ba198c4638369e15c911c3301627d9d9406d 100644 GIT binary patch delta 891 zcmV->1BCpT9q(w++ zyIhTE7k@%TOVAJvElq6^E&YI^ps^a-BbSofxmuakcR0L%4=?8&&Uc{8rx`}U1r$pa zGZ~u=tgNmDJkNCF$IBFhnqD@-i9}pV_|nc-zr|teb2uWGORks0@+bM)YE zeaG`=vSnTLU2;-4%nCSE;ly^uu(>MSZ^}}_dp74*ui9j z9C<1-AuNtTfvoU6C3MCa6IPgzRPB5JzGK^~9l~@UxQ^RiJPD^k`9iYo4L?BNC7dgU zW@_Kpb<|zu^^nryo-=UoTGmSnJR{J!v#hr>%W51Bf%6_N>K`bHVx_7L{tc7T3Ogq@ zpaTE|000010000l00000K~qCXNdN!EJdJjoW3qX86f zu)rhLb0p_6P)yMqkYgtq9;0pY!xm_;Si>fNhf9oPkKr_kC9w>;ENehDTWQ8 zCnaLlVKa+ZF{*hCqtL%rDZp5m?Y7hy6!!{|7dW?irFesFc#@L{U1`-^> zNa`rniG}-d8c%F7POLg)i!9;-n5if}Z1dQt$EcV}=wj5cjj_>|2Qle~*cJt`>WHxumzrUl z6h}PGTHV*vTxTuh2k>L&2fP$OzUVDI!6NR+80%?Am%{ zCs~scBp3H2yKaCqken0;$ekoxJL)kil7no3HH^mC07n4H){n**$w4;28UT3oWJXdv RuEqcW002ovPDHLkV1hP1g+KrR delta 3253 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 diff --git a/frontend/src/views/DataViews/CaseCollection.tsx b/frontend/src/views/DataViews/CaseCollection.tsx index fa97399..865c6b4 100644 --- a/frontend/src/views/DataViews/CaseCollection.tsx +++ b/frontend/src/views/DataViews/CaseCollection.tsx @@ -111,6 +111,7 @@ const CaseCollection = (props: { refresh: boolean }) => {
{showMessage ?

Error signing in. Please try again.

: ''} diff --git a/src/rems-cds-hooks b/src/rems-cds-hooks index 51dd2d4..8a51ac5 160000 --- a/src/rems-cds-hooks +++ b/src/rems-cds-hooks @@ -1 +1 @@ -Subproject commit 51dd2d4d3dd46313187b4fb2c035c5e9b62d0fdd +Subproject commit 8a51ac5c6837549e43a30c4f886c0bfd2aeae50d From beb7773a6fff0fd6735b766bfae2c2872e99b917 Mon Sep 17 00:00:00 2001 From: Ariel Virgulto Date: Thu, 26 Sep 2024 11:29:24 -0400 Subject: [PATCH 10/10] Update readme --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 24f4fd3..c68f548 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,24 @@ Follow the mongodb setup instructions in the [REMS End to End Setup Guide](https If you would rather run with docker, follow the setup found in the [REMS Simple Setup Guide](https://github.com/mcode/rems-setup/blob/main/SimpleSetupGuide.md) (this will also setup the other REMS applications in docker as well). +## Starting the frontend + +Cd into the frontend repository + +### `cd frontend/` + +Next, install the required dependencies by running the following: + +### `npm install` + +Next, start the frontend with the following: + +### `npm start` + +Go to the UI running on http://localhost:5173/ (or whichever port it was run on) + +Still need to update docker to start the UI automatically. + ## Available Scripts In the project directory, you can run: