├── public ├── DOCTOR.png ├── Poster.png ├── Vector.png ├── diabetes.png ├── stroke.png ├── LIFE_SUPPORT.png ├── cardiovascular.png ├── PNG │ ├── Drawkit-Vector-Illustration-Medical-01.png │ ├── Drawkit-Vector-Illustration-Medical-02.png │ ├── Drawkit-Vector-Illustration-Medical-03.png │ ├── Drawkit-Vector-Illustration-Medical-04.png │ ├── Drawkit-Vector-Illustration-Medical-05.png │ ├── Drawkit-Vector-Illustration-Medical-06.png │ ├── Drawkit-Vector-Illustration-Medical-07.png │ ├── Drawkit-Vector-Illustration-Medical-08.png │ ├── Drawkit-Vector-Illustration-Medical-09.png │ ├── Drawkit-Vector-Illustration-Medical-10.png │ ├── Drawkit-Vector-Illustration-Medical-11.png │ ├── Drawkit-Vector-Illustration-Medical-12.png │ ├── Drawkit-Vector-Illustration-Medical-13.png │ ├── Drawkit-Vector-Illustration-Medical-14.png │ ├── Drawkit-Vector-Illustration-Medical-15.png │ ├── Drawkit-Vector-Illustration-Medical-16.png │ ├── Drawkit-Vector-Illustration-Medical-17.png │ ├── Drawkit-Vector-Illustration-Medical-18.png │ ├── Drawkit-Vector-Illustration-Medical-19.png │ └── Drawkit-Vector-Illustration-Medical-20.png └── SVG │ ├── Drawkit-Vector-Illustration-Medical-10.svg │ ├── Drawkit-Vector-Illustration-Medical-11.svg │ ├── Drawkit-Vector-Illustration-Medical-08.svg │ ├── Drawkit-Vector-Illustration-Medical-12.svg │ ├── Drawkit-Vector-Illustration-Medical-09.svg │ ├── Drawkit-Vector-Illustration-Medical-07.svg │ ├── Drawkit-Vector-Illustration-Medical-20.svg │ ├── Drawkit-Vector-Illustration-Medical-13.svg │ ├── Drawkit-Vector-Illustration-Medical-05.svg │ ├── Drawkit-Vector-Illustration-Medical-01.svg │ ├── Drawkit-Vector-Illustration-Medical-14.svg │ ├── Drawkit-Vector-Illustration-Medical-15.svg │ ├── Drawkit-Vector-Illustration-Medical-16.svg │ ├── Drawkit-Vector-Illustration-Medical-02.svg │ ├── Drawkit-Vector-Illustration-Medical-06.svg │ ├── Drawkit-Vector-Illustration-Medical-04.svg │ └── Drawkit-Vector-Illustration-Medical-03.svg ├── components ├── Navigation │ ├── Navigation.module.scss │ └── Navigation.tsx ├── Layout │ ├── Layout.module.scss │ ├── Layout.tsx │ ├── TopNav.tsx │ └── SideNav.tsx ├── FormElements │ ├── Input.tsx │ ├── Select.tsx │ └── Slider.tsx ├── hoc │ └── QuestionnaireHOC.tsx ├── Answers │ └── ScoreSlider.tsx ├── PageTransition.tsx └── Question │ ├── Chart.tsx │ ├── Results.tsx │ └── Question.tsx ├── next.config.js ├── redux ├── action-creators │ ├── index.ts │ └── questions.ts ├── store.ts ├── reducers │ ├── index.ts │ └── questions.ts ├── types │ ├── questions.ts │ └── actions.ts ├── actions │ └── questions.ts └── api.ts ├── .idea ├── .gitignore ├── watcherTasks.xml ├── vcs.xml ├── modules.xml ├── inspectionProfiles │ └── Project_Default.xml └── next.iml ├── hooks ├── useTypedSelector.ts └── useActions.ts ├── utils └── risk.ts ├── pages ├── 404.tsx ├── question │ ├── results.tsx │ └── [id].tsx ├── _app.tsx ├── overview │ └── [disease].tsx └── index.tsx ├── next-env.d.ts ├── enzyme.js ├── router └── nav.ts ├── .gitignore ├── jest.tsconfig.json ├── styles ├── Router.module.scss ├── globals.css └── theme │ └── theme.ts ├── tsconfig.json ├── .github └── workflows │ └── codecov.yml ├── jest.config.js ├── LICENSE ├── __tests__ └── root.spec.tsx ├── README.md └── package.json /public/DOCTOR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/DOCTOR.png -------------------------------------------------------------------------------- /public/Poster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/Poster.png -------------------------------------------------------------------------------- /public/Vector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/Vector.png -------------------------------------------------------------------------------- /public/diabetes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/diabetes.png -------------------------------------------------------------------------------- /public/stroke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/stroke.png -------------------------------------------------------------------------------- /public/LIFE_SUPPORT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/LIFE_SUPPORT.png -------------------------------------------------------------------------------- /components/Navigation/Navigation.module.scss: -------------------------------------------------------------------------------- 1 | .AppBar { 2 | height: 5vh; 3 | min-height: 50px; 4 | } 5 | -------------------------------------------------------------------------------- /public/cardiovascular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/cardiovascular.png -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | module.exports = { 3 | reactStrictMode: true, 4 | } 5 | -------------------------------------------------------------------------------- /redux/action-creators/index.ts: -------------------------------------------------------------------------------- 1 | import * as questions from "./questions"; 2 | 3 | export default { 4 | ...questions 5 | } -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-01.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-02.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-03.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-04.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-05.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-06.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-07.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-08.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-09.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-10.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-11.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-12.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-13.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-14.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-15.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-16.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-17.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-18.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-19.png -------------------------------------------------------------------------------- /public/PNG/Drawkit-Vector-Illustration-Medical-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doctorinna/frontend/HEAD/public/PNG/Drawkit-Vector-Illustration-Medical-20.png -------------------------------------------------------------------------------- /.idea/watcherTasks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /hooks/useTypedSelector.ts: -------------------------------------------------------------------------------- 1 | import {TypedUseSelectorHook, useSelector} from "react-redux"; 2 | import {RootState} from "../redux/reducers"; 3 | 4 | export const useTypedSelector: TypedUseSelectorHook = useSelector; -------------------------------------------------------------------------------- /utils/risk.ts: -------------------------------------------------------------------------------- 1 | export const parseRisk = (risk: number) => { 2 | return Math.round(risk * 1000) / 10; 3 | }; 4 | export const concatInOneString = (str: string) => { 5 | return str.split(" ").reduce((sum,prev) => (sum + prev), "") 6 | }; -------------------------------------------------------------------------------- /pages/404.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Error_404:React.FC = () => { 4 | return ( 5 |
6 |

This page in not found, try another path

7 |
8 | ); 9 | }; 10 | 11 | export default Error_404; -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/basic-features/typescript for more information. 7 | -------------------------------------------------------------------------------- /enzyme.js: -------------------------------------------------------------------------------- 1 | //import Adapter from "@wojtekmaj/enzyme-adapter-react-17"; 2 | //import {configure} from "enzyme"; 3 | //configure({adapter: new Adapter()}); 4 | //using old js syntax 5 | const Adapter = require("@wojtekmaj/enzyme-adapter-react-17"); 6 | require("enzyme").configure({adapter: new Adapter()}); -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /router/nav.ts: -------------------------------------------------------------------------------- 1 | interface navProps{ 2 | name: string, 3 | path: string 4 | } 5 | 6 | export const nav: navProps[] = [ 7 | { 8 | name: "Home", 9 | path: "/" 10 | }, 11 | { 12 | name: "Questions", 13 | path: "/questions" 14 | } 15 | ] 16 | 17 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /hooks/useActions.ts: -------------------------------------------------------------------------------- 1 | import {useDispatch} from "react-redux"; 2 | import {bindActionCreators} from "redux"; 3 | import ActionCreators from "../redux/action-creators"; 4 | 5 | 6 | export const useActions = () => { 7 | const dispatch = useDispatch(); 8 | return bindActionCreators(ActionCreators, dispatch); 9 | } -------------------------------------------------------------------------------- /components/Layout/Layout.module.scss: -------------------------------------------------------------------------------- 1 | .Container { 2 | min-width: 100vw; 3 | min-height: 100vh; 4 | background-color: #E5E5E5; 5 | justify-content: center; 6 | align-items:center; 7 | padding: 0.25vh 0.25vw; 8 | } 9 | 10 | .Box { 11 | margin: 1vh 1vw; 12 | background: white; 13 | justify-content: center; 14 | box-shadow: 0.25vh 0.25vh 0.5vh dimgray; 15 | } -------------------------------------------------------------------------------- /pages/question/results.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import QuestionnaireHoc from "../../components/hoc/QuestionnaireHOC"; 3 | import ResultsComponent from "../../components/Question/Results"; 4 | const Results:React.FC = () => { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | }; 11 | 12 | export default Results; -------------------------------------------------------------------------------- /components/Layout/Layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Box} from "@mui/material"; 3 | import classes from "./Layout.module.scss"; 4 | 5 | const Layout: React.FC = (props) => { 6 | return ( 7 | 8 | 9 | {props.children} 10 | 11 | 12 | ); 13 | }; 14 | 15 | export default Layout; -------------------------------------------------------------------------------- /components/FormElements/Input.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {TextField} from "@mui/material"; 3 | 4 | interface InputProps { 5 | value: string, 6 | handleValue: ()=>void 7 | } 8 | 9 | const Input:React.FC = ({value, handleValue}) => { 10 | return ( 11 | 12 | ); 13 | }; 14 | 15 | export default Input; -------------------------------------------------------------------------------- /.idea/next.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /jest.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "esnext", 5 | "esModuleInterop": true, 6 | "jsx": "react", 7 | "sourceMap": false, 8 | "experimentalDecorators": true, 9 | "noImplicitUseStrict": true, 10 | "removeComments": true, 11 | "moduleResolution": "node", 12 | "lib": [ 13 | "es2017", 14 | "dom" 15 | ], 16 | "typeRoots": [ 17 | "node_modules/@types" 18 | ] 19 | }, 20 | "exclude": [ 21 | "node_modules", 22 | "out", 23 | ".next" 24 | ] 25 | } -------------------------------------------------------------------------------- /styles/Router.module.scss: -------------------------------------------------------------------------------- 1 | $timeout: 300ms; 2 | $transition: opacity $timeout ease-in-out, transform $timeout ease-in-out; 3 | 4 | .page-entering { 5 | transition: $transition; 6 | opacity: 1; 7 | transform: translateX(0); 8 | animation: "blink .3s linear 2"; 9 | } 10 | 11 | .page-exiting { 12 | transition: $transition; 13 | opacity: 0; 14 | transform: translateX(-50px); 15 | } 16 | 17 | .page-exited { 18 | opacity: 0; 19 | transform: translateX(100%); 20 | } 21 | 22 | .page-entered { 23 | opacity: 0; 24 | transform: translateX(50px); 25 | } 26 | -------------------------------------------------------------------------------- /redux/store.ts: -------------------------------------------------------------------------------- 1 | import {createStore, applyMiddleware, compose, Store} from "redux"; 2 | import thunk from "redux-thunk"; 3 | import {reducer, RootState} from "./reducers"; 4 | import {Context, createWrapper} from "next-redux-wrapper"; 5 | 6 | let composeEnhancers = compose; 7 | 8 | if(typeof window !== "undefined") 9 | composeEnhancers = (window as any)["__REDUX_DEVTOOLS_EXTENSION_COMPOSE__"] as typeof compose || compose; 10 | 11 | export const makeStore = (context?: Context) => createStore(reducer, undefined, composeEnhancers(applyMiddleware(thunk))); 12 | export const wrapper = createWrapper>(makeStore, {debug: false}); -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans&family=Inter&family=Mulish&family=Rubik&display=swap'); 2 | 3 | html, 4 | body { 5 | padding: 0; 6 | margin: 0; 7 | font-family: 8 | -apple-system, 9 | BlinkMacSystemFont, 10 | Segoe UI, 11 | Roboto, 12 | Oxygen, 13 | Ubuntu, 14 | Cantarell, 15 | Fira Sans, 16 | Droid Sans, 17 | Helvetica Neue, 18 | sans-serif; 19 | } 20 | 21 | a { 22 | color: inherit; 23 | text-decoration: none; 24 | } 25 | 26 | * { 27 | box-sizing: border-box; 28 | } -------------------------------------------------------------------------------- /components/hoc/QuestionnaireHOC.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Grid} from "@mui/material"; 3 | import SideNav from "../Layout/SideNav"; 4 | 5 | interface QuestionnaireHocProps { 6 | underline: number 7 | } 8 | 9 | const QuestionnaireHoc: React.FC = ({underline, children}) => { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | {children} 17 | 18 | 19 | ); 20 | }; 21 | 22 | export default QuestionnaireHoc; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "noImplicitAny": true, 17 | "noImplicitThis": true, 18 | "strictNullChecks": true, 19 | "importHelpers": true 20 | }, 21 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /redux/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import {reducer as questionsReducer} from "./questions"; 2 | import {combineReducers} from "redux"; 3 | import {HYDRATE} from "next-redux-wrapper"; 4 | 5 | export const rootReducer = combineReducers({ 6 | questions: questionsReducer 7 | }); 8 | 9 | export type RootState = ReturnType; 10 | 11 | export const reducer = (state: any, action: any) => { 12 | if (action.type === HYDRATE) { 13 | const nextState = { 14 | ...state, // use previous state 15 | ...action.payload, // apply delta from hydration 16 | } 17 | if (state.count) nextState.count = state.count; // preserve count value on client side navigation 18 | return nextState 19 | } else { 20 | return rootReducer(state, action) 21 | } 22 | } -------------------------------------------------------------------------------- /.github/workflows/codecov.yml: -------------------------------------------------------------------------------- 1 | name: Running Code Coverage 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [14.x] 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v2 17 | with: 18 | fetch-depth: 2 19 | 20 | - name: Set up Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | 25 | - name: Install dependencies 26 | run: npm install 27 | 28 | - name: Run tests 29 | run: npm run test:coverage 30 | 31 | - name: Upload coverage to Codecov 32 | uses: codecov/codecov-action@v1 33 | with: 34 | token: ${{ secrets.CODECOV_TOKEN }} -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "../styles/globals.css"; 2 | import type {AppProps} from "next/app"; 3 | import Layout from "../components/Layout/Layout"; 4 | import React from "react"; 5 | import PageTransition from "../components/PageTransition"; 6 | import {wrapper} from "../redux/store"; 7 | import {ThemeProvider} from "@mui/material"; 8 | import {THEME} from "../styles/theme/theme"; 9 | 10 | function MyApp({Component, pageProps, router}: AppProps) { 11 | console.warn = () => {}; 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ) 21 | } 22 | 23 | export default wrapper.withRedux(MyApp); 24 | -------------------------------------------------------------------------------- /components/Answers/ScoreSlider.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Slider} from "@mui/material"; 3 | 4 | interface scoreSliderProps { 5 | value: number//between 0 and 1 6 | } 7 | const ScoreSlider: React.FC = ({value}) => { 8 | const valueText = (val: number) => { 9 | const rounded = Math.round(val*1000); 10 | return rounded/10 + "%"; 11 | } 12 | const N = 10; 13 | const marks = Array.from({length: N}, (v, k) => (k+1)*10).map(val => ({value: val, label:val})); 14 | return ( 15 | 25 | ); 26 | }; 27 | 28 | export default ScoreSlider; -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: [ 3 | "ts", 4 | "tsx", 5 | "js" 6 | ], 7 | transform: { 8 | "^.+\\.tsx?$": "ts-jest" 9 | }, 10 | testMatch: [ 11 | "**/*.(test|spec).(ts|tsx)" 12 | ], 13 | globals: { 14 | "ts-jest": { 15 | useBabelrc: true, 16 | tsConfigFile: "jest.tsconfig.json" 17 | } 18 | }, 19 | coveragePathIgnorePatterns: [ 20 | "/node_modules/", 21 | "enzyme.js" 22 | ], 23 | setupTestFrameworkScriptFile: "/enzyme.js", 24 | coverageReporters: [ 25 | "json", 26 | "lcov", 27 | "text", 28 | "text-summary" 29 | ], 30 | moduleNameMapper: { 31 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/mocks.js", 32 | "\\.(css|less|scss)$": "identity-obj-proxy"//"/__mocks__/mocks.js" 33 | } 34 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Doctorinna 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /components/FormElements/Select.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | MenuItem, 4 | Select as Sel, 5 | FormControl, 6 | SelectChangeEvent, 7 | FormHelperText 8 | } from "@mui/material"; 9 | import {Option} from "../../redux/types/questions"; 10 | 11 | interface SelectProps { 12 | options: Option[], 13 | value: string, 14 | handleValue: (e: SelectChangeEvent)=>void; 15 | hasError: boolean; 16 | } 17 | 18 | const Select:React.FC = ({options, value, handleValue, hasError}) => { 19 | return ( 20 | 21 | 29 | {options.map((opt)=>( 30 | {opt.answer} 31 | ))} 32 | 33 | {hasError && This is required!} 34 | 35 | ); 36 | }; 37 | 38 | export default Select; -------------------------------------------------------------------------------- /redux/types/questions.ts: -------------------------------------------------------------------------------- 1 | interface Range { 2 | id: number; 3 | min: number; 4 | max: number; 5 | } 6 | 7 | interface Region { 8 | region: string; 9 | avg_factor: number; 10 | } 11 | 12 | interface LocalRegion { 13 | label: string; 14 | count: number; 15 | } 16 | 17 | export interface Option { 18 | id: number; 19 | answer: string; 20 | } 21 | 22 | interface Category { 23 | id: number; 24 | title: string 25 | } 26 | export interface QuestionType{ 27 | id: number; 28 | description: string; 29 | category: Category; 30 | range: Range | null; 31 | options: Option[]; 32 | } 33 | 34 | export interface DiseaseType { 35 | id: number; 36 | illness: string; 37 | description: string; 38 | } 39 | 40 | export interface CategoryType { 41 | id: number; 42 | title: string; 43 | } 44 | 45 | export interface AnswerType { 46 | question: string; 47 | answer: string; 48 | } 49 | export interface Recommendation { 50 | disease: DiseaseType; 51 | risk_factor: number 52 | prescription: string 53 | } 54 | 55 | export interface Statistics { 56 | country: Region[] 57 | your_region: LocalRegion[] 58 | } -------------------------------------------------------------------------------- /redux/actions/questions.ts: -------------------------------------------------------------------------------- 1 | import {actionTypes} from "../types/actions"; 2 | import { 3 | AnswerType, 4 | CategoryType, 5 | DiseaseType, 6 | QuestionType, 7 | Recommendation, 8 | Statistics 9 | } from "../types/questions"; 10 | 11 | export const actions = { 12 | fetchQuestions: (payload: QuestionType[]) => ({ 13 | type: actionTypes.FETCH_QUESTIONS, 14 | payload 15 | }), 16 | fetchCategories: (payload: CategoryType[]) => ({ 17 | type: actionTypes.FETCH_CATEGORIES, 18 | payload 19 | }), 20 | fetchDiseases: (payload: DiseaseType[]) => ({ 21 | type: actionTypes.FETCH_DISEASES, 22 | payload 23 | }), 24 | saveAnswers: (payload: AnswerType[]) => ({ 25 | type: actionTypes.SAVE_ANSWERS, 26 | payload 27 | }), 28 | fetchResults: (payload: Recommendation[]) => ({ 29 | type: actionTypes.FETCH_RESULTS, 30 | payload 31 | }), 32 | fetchStatistics: (payload: Statistics) => ({ 33 | type: actionTypes.FETCH_STATISTICS, 34 | payload 35 | }), 36 | saveToken: (payload: string) => ({ 37 | type: actionTypes.SAVE_TOKEN, 38 | payload 39 | }) 40 | } -------------------------------------------------------------------------------- /redux/api.ts: -------------------------------------------------------------------------------- 1 | const urls = { 2 | HOST: "http://18.216.235.168", 3 | mappings: { 4 | API: "api", 5 | RISKS: "risks", 6 | DISEASES: "diseases", 7 | QUESTIONS: "questions", 8 | RESPONSE: "response", 9 | CATEGORIES: "categories", 10 | RESULT: "result", 11 | STATISTICS: "statistics" 12 | } 13 | } 14 | const getQuestions = (category: string) => 15 | `${urls.HOST}/${urls.mappings.API}/${urls.mappings.RISKS}/${urls.mappings.QUESTIONS}/${category}`; 16 | const getAllDiseases = () => 17 | `${urls.HOST}/${urls.mappings.API}/${urls.mappings.RISKS}/${urls.mappings.DISEASES}`; 18 | const getAllCategories = () => 19 | `${urls.HOST}/${urls.mappings.API}/${urls.mappings.RISKS}/${urls.mappings.CATEGORIES}/`; 20 | const sendAnswers = () => 21 | `${urls.HOST}/${urls.mappings.API}/${urls.mappings.RISKS}/${urls.mappings.RESPONSE}/`; 22 | const getResults = (token: string) => 23 | `${urls.HOST}/${urls.mappings.API}/${urls.mappings.RISKS}/${urls.mappings.RESULT}/${token}/__all__`; 24 | const getStatistics = (illness: string, token: string) => 25 | `${urls.HOST}/${urls.mappings.API}/${urls.mappings.RISKS}/${urls.mappings.RESULT}/${urls.mappings.STATISTICS}/${token}/${illness}`; 26 | 27 | export {getAllDiseases, getQuestions, sendAnswers, getAllCategories, getResults, getStatistics}; -------------------------------------------------------------------------------- /redux/reducers/questions.ts: -------------------------------------------------------------------------------- 1 | import {actions, actionTypes, questionsState} from "../types/actions"; 2 | 3 | const initialState: questionsState = { 4 | questions: [], 5 | answers: [], 6 | results: [], 7 | diseases: [], 8 | categories: [], 9 | statistics: { 10 | country: [], 11 | your_region: [] 12 | }, 13 | token: "" 14 | } 15 | 16 | export const reducer = (state: questionsState = initialState, action: actions): questionsState => { 17 | switch (action.type) { 18 | case actionTypes.SAVE_TOKEN: 19 | return {...state, token: action.payload}; 20 | case actionTypes.FETCH_QUESTIONS: 21 | return {...state, questions: action.payload}; 22 | case actionTypes.FETCH_CATEGORIES: 23 | return {...state, categories: action.payload}; 24 | case actionTypes.FETCH_DISEASES: 25 | return {...state, diseases: action.payload}; 26 | case actionTypes.FETCH_RESULTS: 27 | return {...state, results: action.payload}; 28 | case actionTypes.FETCH_STATISTICS: 29 | return {...state, statistics: action.payload}; 30 | case actionTypes.SAVE_ANSWERS: 31 | const answers = state.answers.concat(action.payload); 32 | return {...state, answers: answers}; 33 | default: 34 | return state; 35 | } 36 | } -------------------------------------------------------------------------------- /__tests__/root.spec.tsx: -------------------------------------------------------------------------------- 1 | import 'jsdom-global/register'; 2 | import React from "react"; 3 | import {ReactWrapper, shallow, mount, ShallowWrapper} from "enzyme"; 4 | import {Box, Grid} from "@mui/material"; 5 | import Index from "../pages/index"; 6 | import QuestionnaireHoc from "../components/hoc/QuestionnaireHOC"; 7 | import {Provider} from "react-redux"; 8 | import {makeStore} from "../redux/store"; 9 | import Layout from "../components/Layout/Layout"; 10 | 11 | describe("Pages", ()=>{ 12 | describe("Index page", ()=>{ 13 | it("All Grid components in root page are rendered", ()=>{ 14 | const wrapper: ReactWrapper = mount( 15 | 16 | ); 17 | const grid_number = wrapper.find(Grid).length; 18 | expect(grid_number).toEqual(16); 19 | }); 20 | it("Layout is rendered", ()=>{ 21 | const wrapper: ShallowWrapper = shallow(); 22 | const box_number = wrapper.find(Box).length; 23 | expect(box_number).toEqual(2); 24 | }) 25 | it("All Grid components in questionnaire HOC are rendered", ()=>{ 26 | const wrapper: ReactWrapper = mount(); 27 | const grid_number = wrapper.find(Grid).length; 28 | expect(grid_number).toEqual(11); 29 | }) 30 | }) 31 | }) -------------------------------------------------------------------------------- /components/PageTransition.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Transition, TransitionGroup} from "react-transition-group"; 3 | 4 | interface TransitionProps { 5 | children: T, 6 | location: string 7 | } 8 | 9 | const TIMEOUT: number = 450; 10 | const TIMING_FUNCTION : string = "cubic-bezier(0,1.5,1,1.05)"; 11 | 12 | const getTransitionStyles:any = { 13 | entering: { 14 | position: "absolute", 15 | opacity: 0, 16 | transform: "translateX(-50%)", 17 | }, 18 | entered: { 19 | transition: `opacity ${TIMEOUT}ms ${TIMING_FUNCTION}, transform ${TIMEOUT}ms ${TIMING_FUNCTION}`, 20 | opacity: 1, 21 | transform: "translateX(0px)", 22 | 23 | }, 24 | exiting: { 25 | transition: `opacity ${TIMEOUT}ms ${TIMING_FUNCTION}, transform ${TIMEOUT}ms ${TIMING_FUNCTION}`, 26 | opacity: 0, 27 | transform: "translateX(50%)", 28 | }, 29 | }; 30 | 31 | const PageTransition:React.FC> = ({location, children}) => { 32 | return ( 33 | 34 | 35 | {(state) => ( 36 |
{children}
37 | )} 38 |
39 |
40 | ); 41 | }; 42 | 43 | export default PageTransition; -------------------------------------------------------------------------------- /components/Question/Chart.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Box, Typography} from "@mui/material"; 3 | import {Statistics} from "../../redux/types/questions"; 4 | import {parseRisk} from "../../utils/risk"; 5 | import {purple} from "@mui/material/colors"; 6 | 7 | 8 | interface chartProps { 9 | stats: Statistics 10 | } 11 | 12 | const Chart: React.FC = ({stats}) => { 13 | const colors = [purple["900"], purple["700"], purple["500"], 14 | purple["300"], purple["100"], purple["50"]]; 15 | let max = stats.country[0]?.avg_factor; 16 | stats.country.forEach((r) => { 17 | if (r.avg_factor > max) max = r.avg_factor; 18 | }); 19 | const scale = (percent: number) => percent / parseRisk(max) * 100; 20 | 21 | return (<> 22 | {stats.country.map((r, index) => ( 23 | 24 | 30 | {r.region} 31 | 32 | 33 | ))} 34 | 35 | ); 36 | }; 37 | 38 | export default Chart; -------------------------------------------------------------------------------- /components/Layout/TopNav.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Box, Grid, Typography} from "@mui/material"; 3 | import {useTypedSelector} from "../../hooks/useTypedSelector"; 4 | 5 | interface TopNav { 6 | chosen: string 7 | } 8 | 9 | const TopNav: React.FC = ({chosen}) => { 10 | const {categories} = useTypedSelector(state => state.questions); 11 | return ( 12 | 15 | 16 | {categories.map((val, ind) => ( 17 | 18 | 19 | {val.title} 21 | 22 | 23 | {ind !== categories.length - 1 ? 24 | . 25 | : null 26 | } 27 | 28 | 29 | ))} 30 | 31 | 32 | ); 33 | }; 34 | 35 | export default TopNav; -------------------------------------------------------------------------------- /components/Layout/SideNav.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Box, Grid, Typography} from "@mui/material"; 3 | 4 | interface SideNavProps { 5 | underline: number 6 | } 7 | const SideNav:React.FC = ({underline}) => { 8 | const links = ["Start", "Questionnaire", "Results", "Overview"]; 9 | return ( 10 | 11 | 12 | 13 | 14 | heart 15 | 16 | 17 | Doctorinna 18 | 19 | 20 | 21 | 22 | {links.map((v,ind) => ( 23 | 24 | {v} 28 | 29 | ))} 30 | 31 | 32 | 33 | 34 | ); 35 | }; 36 | 37 | export default SideNav; -------------------------------------------------------------------------------- /components/Navigation/Navigation.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {AppBar, Toolbar, IconButton, Typography, Grid} from "@mui/material"; 3 | import {Menu as MenuIcon} from "@mui/icons-material"; 4 | import {nav} from "../../router/nav"; 5 | import classes from "./Navigation.module.scss"; 6 | import Link from "next/link"; 7 | 8 | const Navigation: React.FC = () => { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {nav.map((el, ind) => ( 23 | 24 | 25 | 26 | {el.name} 27 | 28 | 29 | 30 | 31 | 32 | ))} 33 | 34 | 35 | 36 | ); 37 | }; 38 | 39 | export default Navigation; -------------------------------------------------------------------------------- /public/SVG/Drawkit-Vector-Illustration-Medical-10.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Doctorinna-Frontend 2 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/d6c01263e90b4c3a9e85b630e274eb72)](https://www.codacy.com/gh/Doctorinna/frontend/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Doctorinna/frontend&utm_campaign=Badge_Grade) 3 | [![codecov](https://codecov.io/gh/Doctorinna/frontend/branch/master/graph/badge.svg?token=8BE2XA4162)](https://codecov.io/gh/Doctorinna/frontend) 4 | 5 | This repository is place for Doctorinna frontend project. It is built using next js framework on react, with redux as state manager and material ui components. Project is written on typescript to allow code completion and catching bugs more easily. Current version is just layout of a SPA. 6 | 7 | ## Getting Started 8 | For project I use Node.js 14, but most of packages are working on different versions of node, if you have a problems with sass [check supported versions](https://github.com/sass/node-sass) and use different version of node/node-sass(edit package.json and package-lock.json) package 9 | 10 | To learn your version of node run: 11 | 12 | ```bash 13 | node --version 14 | ``` 15 | 16 | To install dependencies run: 17 | 18 | ```bash 19 | npm install 20 | # or 21 | yarn install 22 | ``` 23 | 24 | To run the development server: 25 | 26 | ```bash 27 | npm run dev 28 | # or 29 | yarn dev 30 | ``` 31 | 32 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 33 | 34 | ## MVC 35 | MVC of applicatiom is deployed to vercel platform that already has CI. 36 | 37 | But unfortunately, vercel only permit https, so it isn't integrated with backend yet. It works locally though. 38 | 39 | You can see the app here [https://doctorinna.vercel.app/](https://doctorinna.vercel.app/) 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "test": "jest", 11 | "test:watch": "jest --watch", 12 | "test:coverage": "jest --coverage --detectOpenHandles" 13 | }, 14 | "dependencies": { 15 | "@emotion/react": "^11.4.1", 16 | "@emotion/styled": "^11.3.0", 17 | "@mui/icons-material": "^5.0.1", 18 | "@mui/material": "^5.0.2", 19 | "@types/node": "^16.7.7", 20 | "@types/react-dom": "^17.0.9", 21 | "@types/react-redux": "^7.1.18", 22 | "@types/react-transition-group": "^4.4.2", 23 | "axios": "^0.21.1", 24 | "bootstrap": "^5.1.0", 25 | "next": "11.1.0", 26 | "next-redux-wrapper": "^7.0.5", 27 | "node-sass": "^4.14.1", 28 | "react": "17.0.2", 29 | "react-dom": "17.0.2", 30 | "react-redux": "^7.2.4", 31 | "react-transition-group": "^4.4.2", 32 | "redux": "^4.1.1", 33 | "redux-thunk": "^2.3.0", 34 | "tslib": "^2.3.1" 35 | }, 36 | "devDependencies": { 37 | "@types/enzyme": "^3.10.9", 38 | "@types/jest": "^27.0.1", 39 | "@types/react": "17.0.19", 40 | "@wojtekmaj/enzyme-adapter-react-17": "^0.6.3", 41 | "babel-core": "^6.26.3", 42 | "babel-jest": "^27.1.0", 43 | "babel-preset-env": "^1.7.0", 44 | "babel-preset-react": "^6.24.1", 45 | "enzyme": "^3.11.0", 46 | "eslint": "7.32.0", 47 | "eslint-config-next": "11.1.0", 48 | "identity-obj-proxy": "^3.0.0", 49 | "jest": "^27.1.0", 50 | "jsdom": "18.0.0", 51 | "jsdom-global": "3.0.2", 52 | "redux-devtools-extension": "^2.13.9", 53 | "ts-jest": "^27.0.5", 54 | "typescript": "4.4.2" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /redux/types/actions.ts: -------------------------------------------------------------------------------- 1 | import {QuestionType, AnswerType, DiseaseType, CategoryType, Recommendation, Statistics} from "./questions"; 2 | 3 | export interface questionsState { 4 | questions: QuestionType[], 5 | answers: AnswerType[], 6 | diseases: DiseaseType[], 7 | categories: CategoryType[], 8 | results: Recommendation[], 9 | statistics: Statistics, 10 | token: string 11 | } 12 | 13 | export enum actionTypes { 14 | FETCH_QUESTIONS = "FETCH_QUESTIONS", 15 | SAVE_ANSWERS = "SAVE_ANSWERS", 16 | FETCH_CATEGORIES = "FETCH_CATEGORIES", 17 | FETCH_DISEASES = "FETCH_DISEASES", 18 | FETCH_RESULTS = "FETCH_RESULTS", 19 | FETCH_STATISTICS = "FETCH_STATISTICS", 20 | SAVE_TOKEN = "SAVE_TOKEN" 21 | } 22 | 23 | interface fetchQuestionsAction { 24 | type: actionTypes.FETCH_QUESTIONS, 25 | payload: QuestionType[] 26 | } 27 | 28 | interface fetchStatisticsAction { 29 | type: actionTypes.FETCH_STATISTICS, 30 | payload: Statistics 31 | } 32 | 33 | interface saveAnswersAction { 34 | type: actionTypes.SAVE_ANSWERS, 35 | payload: AnswerType[] 36 | } 37 | 38 | interface fetchCategoriesAction { 39 | type: actionTypes.FETCH_CATEGORIES, 40 | payload: CategoryType[] 41 | } 42 | interface fetchDiseasesAction { 43 | type: actionTypes.FETCH_DISEASES, 44 | payload: DiseaseType[] 45 | } 46 | interface fetchResultsAction { 47 | type: actionTypes.FETCH_RESULTS, 48 | payload: Recommendation[] 49 | } 50 | interface saveTokenAction { 51 | type: actionTypes.SAVE_TOKEN, 52 | payload: string 53 | } 54 | 55 | export type actions = fetchQuestionsAction | 56 | saveAnswersAction | 57 | fetchDiseasesAction | 58 | fetchCategoriesAction | 59 | fetchResultsAction | 60 | fetchStatisticsAction | 61 | saveTokenAction; -------------------------------------------------------------------------------- /public/SVG/Drawkit-Vector-Illustration-Medical-11.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/Question/Results.tsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect} from "react"; 2 | import {Grid, Typography} from "@mui/material"; 3 | import {useTypedSelector} from "../../hooks/useTypedSelector"; 4 | import {useActions} from "../../hooks/useActions"; 5 | import Link from "next/link"; 6 | import {concatInOneString, parseRisk} from "../../utils/risk"; 7 | 8 | const Results: React.FC = () => { 9 | const state = useTypedSelector(state => state.questions); 10 | const {fetchResults} = useActions(); 11 | useEffect(()=>{ 12 | fetchResults(); 13 | },[]) 14 | const srcImage = (disease: string) => { 15 | if(disease === "diabetes")return "/diabetes.png"; 16 | if(disease === "cardiovascular disease")return "/cardiovascular.png"; 17 | if(disease === "stroke")return "/stroke.png"; 18 | return ""; 19 | 20 | } 21 | const results = state.results; 22 | return ( 23 | 24 | {results.map((r)=>( 25 | 26 | 27 | 28 | 29 | {r.disease.illness}/ 30 | 31 | 32 | {r.disease.illness} 33 | 34 | 35 | {parseRisk(r.risk_factor) + "%"} 36 | 37 | 38 | 39 | 40 | ))} 41 | 42 | ); 43 | }; 44 | 45 | export default Results; -------------------------------------------------------------------------------- /public/SVG/Drawkit-Vector-Illustration-Medical-08.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/FormElements/Slider.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from "react"; 2 | import {Box, Checkbox, FormControlLabel, Slider as Slid} from "@mui/material"; 3 | import {Option} from "../../redux/types/questions"; 4 | 5 | interface sliderProps { 6 | max: number; 7 | min: number; 8 | value: number; 9 | handler: (e: Event, val: number | number[]) => void; 10 | options: Option[]; 11 | optionHandler: (checked: boolean) => void 12 | } 13 | 14 | const Slider: React.FC = ({optionHandler, max, min, value, handler, options}) => { 15 | const [dis, setDis] = useState(false); 16 | const N = 5; 17 | const marks = Array.from({length: N + 1}, (v, k) => Math.round(min + (max - min) * k / N)).map(val => ({ 18 | value: val, 19 | label: val.toString() 20 | })); 21 | return ( 22 | 23 | 46 | {options[0] ? 47 | ) => { 50 | setDis(e.target.checked); 51 | optionHandler(e.target.checked); 52 | }} 53 | inputProps={{"aria-label": "controlled"}}/>} label={options[0].answer}/> 54 | : null} 55 | 56 | ); 57 | }; 58 | 59 | export default Slider; -------------------------------------------------------------------------------- /pages/overview/[disease].tsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect} from "react"; 2 | import {Box, Grid, Typography} from "@mui/material"; 3 | import {useRouter} from "next/router"; 4 | import {useTypedSelector} from "../../hooks/useTypedSelector"; 5 | import {useActions} from "../../hooks/useActions"; 6 | import Chart from "../../components/Question/Chart"; 7 | import {parseRisk} from "../../utils/risk"; 8 | 9 | const Disease: React.FC = () => { 10 | const {query} = useRouter(); 11 | const {disease} = query; 12 | const {fetchStatistics} = useActions(); 13 | const {results, statistics} = useTypedSelector(state => state.questions); 14 | const index = results.findIndex((v) => v.disease.illness === disease); 15 | const result = results[index]; 16 | useEffect(() => { 17 | fetchStatistics(disease as string); 18 | }, []); 19 | return ( 20 | 21 | 22 | 23 | 24 | heart 25 | 26 | 27 | Doctorinna 28 | 29 | 30 | 31 | 32 | 33 | {result?.disease.illness} 34 | 35 | 36 | {"Risk is " + parseRisk(result.risk_factor) + "%"} 37 | 38 | 39 | 40 | 41 | {result.prescription} 42 | 43 | 44 | 45 | 46 | 47 | 48 | {result?.disease.description} 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ); 58 | }; 59 | 60 | export default Disease; 61 | -------------------------------------------------------------------------------- /components/Question/Question.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from "react"; 2 | import { 3 | SelectChangeEvent, 4 | Box 5 | } from "@mui/material"; 6 | import Select from "../FormElements/Select"; 7 | import Slider from "../FormElements/Slider"; 8 | import {AnswerType, QuestionType} from "../../redux/types/questions"; 9 | 10 | interface QuestionProps { 11 | question: QuestionType, 12 | answer: AnswerType, 13 | setAns: (answer: AnswerType) => void, 14 | hasError: boolean 15 | } 16 | 17 | const Question: React.FC = ({question, answer, setAns, hasError}) => { 18 | let elementType: JSX.Element; 19 | if (question.range) { 20 | const max = question.range.max; 21 | const min = question.range.min; 22 | setAns({...answer, answer: Math.round((min+max)/2).toString()}); 23 | const [value, setValue] = useState(Math.round((min+max)/2)); 24 | let oldValue : number; 25 | elementType = { 30 | setValue(val as number); 31 | setAns({...answer, answer: (val as number).toString()}) 32 | } 33 | } 34 | optionHandler={ 35 | (checked: boolean) => { 36 | if(checked){ 37 | oldValue = value; 38 | setAns({...answer, answer:question.options[0].answer}); 39 | } 40 | else setAns({...answer, answer:oldValue.toString()}); 41 | } 42 | } 43 | options={question.options}/> 44 | } 45 | else { 46 | const [value, setValue] = useState(question.options[0].answer); 47 | setAns({...answer, answer: question.options[0].answer}); 48 | elementType =